I have such structure in my database:
User 1
+- items
+- 1
| |- question: some question
| |- answer: some answer
|
+- 2
|- question: another question
I want to add answer to second item, but I don't have the id.
I'm trying to get items and it returns
[null, {answer: some answer, question: some question}, {question: another question}]
But I can't get needed item without answer field and I can't get their ids.
I tried to use equalTo, but it didn't help.
// attempt 1
Query child = _databaseReference.child(item._user).child(db_items).equalTo(null, key: db_answer);
child.reference().once().then((DataSnapshot snapshot) {
print('snapshot: ${snapshot.value}');
});
// attempt 2
Query child = _databaseReference.child(item._user).child(db_items).equalTo(null, key: db_answer);
child.reference().child(db_question).once().then((DataSnapshot snapshot) {
print('snapshot: ${snapshot.value}');
});
First attempt returns the same output as I wrote above, second returns null
So, does anybody know how can I add answer field to second question?
P.S. I use Flutter, but I don't think it means a lot for this issue.
So basically you are trying to add 'answer' to your second item.
I would have done it in this way:
First is to know the key beforehand, so that I can insert a child node in the second item. That way i can write something like:
myDBReference.child("items").child("key_known_beforehand").child("answer").setValue("this is the ansewr");
Or in case I didnt know the key, which seems like the problem here:
I would use Firebase Query. Something like..
myDBReference.child("items").orderByValue().startAt(starting_point_of_child_item).endAt(ending_point_of_child_item).child
This query would givve me the requred node and i would set value there.
"
Using startAt(), endAt(), and equalTo() allows you to choose arbitrary starting and ending points for your queries
To filter data, you can combine any of the limit or range methods with an order-by method when constructing a query.
Unlike the order-by methods, you can combine multiple limit or range functions. For example, you can combine the startAt() and endAt() methods to limit the results to a specified range of values.
"
Related
I'm trying to group all the documents based on an element value. Through X-Query, I'm able to get the element value and its corresponding count. But, with Java API I'm not able to do that.
X-Query:
for $name in distinct-values(doc()/document/<element_name>)
return fn:concat("Element Value:",$name,", Count:",fn:count(doc()/document/[element_name eq $name]));
Output:
Element Value:A, Count:100
Element Value:B, Count:200
Java:
QueryManager qryMgr = client.newQueryManager();
StructuredQueryBuilder qb = new StructuredQueryBuilder();
StructuredQueryDefinition querydef = qb.containerQuery(qb.element("<element_name>"), qb.term("A"));
SearchHandle handle = new SearchHandle();
qryMgr.search(querydef, handle);
System.out.println(handle.getTotalResults());
By this method, I'm able to get the document count only for a particular value. Is there any way to get the count of all documents. Kindly Help!
If I understand your use case, you can use a range index to solve this problem, which is - you want to know what all the values are for a particular element, and then how many documents have that value. That's exactly what a range index is for.
Try adding a range index on "element_name" - you can use the ML Admin app for that - go to your database and click on Element Range Indexes.
In XQuery, you can then do something like this:
for $val in cts:element-values(xs:QName("element_name"))
return text{$val, cts:frequency($val)}
With the Java Client, you can do the same by adding a range-based constraint to a search options file, and then the response from SearchManager will have all of the values and frequencies in it that match your query. Check the REST API docs for constructing such a search options file.
Is it possible to do wildcard queries on Firebase? For example:
https://foo.firebaseio.com/person.json?orderBy="name"&equalTo="Lun*"
I know it's been a while but I thought that others might be interested. You can "fake" a wildcard search for things like foo* (so basically you can search for values beginning with a specified string).
For iOS & Swift it would look like this:
dbReference.child("person").queryOrdered(byChild: "name").queryStarting(atValue: "foo").queryEnding(atValue: "foo\u{f8ff}").observe(.childAdded) { (snapshot: FIRDataSnapshot) in
print("\(snapshot.key) - \(String(describing: snapshot.value))")
}
What this does is using a start and end values for name property where the end key is equal to the start + a very high code point in the Unicode range. Because it is after most regular characters in Unicode, the query matches all values that start with foo.
No. But kinda.
You cannot do a wildcard query, however, you can structure your data that will allow for this.
For example, say we want to find matches for users whose name starts with Ler
Here's our structure
users
uid_0
name: "Leroy"
Store the decomposed data in another node: Remember, disk space is cheap.
decomposed
uid_0
L: true
Le: true
Ler: true
Lero: true
Leroy: true
then perform a query on the decomposed node for the value of true for children equal to Ler
ref.queryOrderedByChild("Ler").queryEqualToValue(true).observeEventType(.ChildAdded,
withBlock: { snapshot in
print(snapshot.key)
})
And the snapshot.key will be uid_0
You can do something like this.
Make sure you order your search field alphabetically.
Then you search for all names (starting at Lun and ending at Luo) where the last letter 'o' is calculated with the initial last letter 'n' + 1.
I guess you see the general idea here.
So it should return anything between 'Lun*' and stop at the first entry of 'Luo*'
https://foo.firebaseio.com/person.json?orderBy="name"&startAt="Lun"&endAt="Luo"
Since Firebase doesn't support wildcard searching I decided to go with Apigee BaaS for our company.
I would like to 'upsert' a document in DynamoDB. That is, I would like to specify a key, and a set of field/value pairs. If no document exists with that key, I want one created with that key and the key/value pairs I specified. If a document exists with that key, I want the fields I specified to be set to the values specified (if those fields did not exist before, then they should be added). Any other, unspecified fields on the existing document should be left alone.
It seems I can do this pretty well with the UpdateItem call, when the field/value pairs I am setting are all top-level fields. If I have nested structures, UpdateItem will work to set the nested fields, as long as the structure exists. In other words, if my existing document has "foo": {}, then I can set "foo.bar": 42 successfully.
However, I don't seem to be able to set "foo.bar": 42 if there is no foo object already (like in the case where there is no document with the specified field at all, and my 'upsert' is behaving as an 'insert'.
I found a discussion on the AWS forums from a few years ago which seems to imply that what I want to do cannot be done, but I'm hoping this has changed recently, or maybe someone knows of a way to do it?
UpdateItem behaves like an "upsert" operation: The item is updated if it exists in the table, but if not, a new item is added (inserted).
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SQLtoNoSQL.UpdateData.html
That ("foo.bar": 42) can be achieved using the below query:
table.update_item(Key = {'Id' : id},
UpdateExpression = 'SET foo = :value1',
ExpressionAttributeValues = {':value1': {'bar' : 42}}
)
Hope this helps :)
I found this UpdateItem limitation (top level vs nested attributes) frustrating as well. Eventually I came across this answer and was able to work around the problem: https://stackoverflow.com/a/43136029/431296
It requires two UpdateItem calls (possibly more depending on level of nesting?). I only needed a single level, so this is how I did it:
Update the item using an attribute_exists condition to create the top level attribute as an empty map if it doesn't already exist. This will work if the entire item is missing or if it exists and has other pre-existing attributes you don't want to lose.
Then do the 2nd level update item to update the nested value. As long as the parent exists (ex: an empty map in my case) it works great.
I got the impression you weren't using python, but here's the python code to accomplish the upsert of a nested attribute in an item like this:
{
"partition_key": "key",
"top_level_attribute": {
"nested_attribute": "value"
}
}
python boto3 code:
def upsert_nested_item(self, partition_key, top_level_attribute_name, nested_attribute_name, nested_item_value):
try:
self.table.update_item(
Key={'partition_key': partition_key},
ExpressionAttributeNames={f'#{top_level_attribute_name}': top_level_attribute_name},
ExpressionAttributeValues={':empty': {}},
ConditionExpression=f'attribute_not_exists(#{top_level_attribute_name})',
UpdateExpression=f'SET #{top_level_attribute_name} = :empty',
)
except self.DYNAMODB.meta.client.exceptions.ConditionalCheckFailedException:
pass
self.table.update_item(
Key={'partition_key': partition_key},
ExpressionAttributeNames={
f'#{top_level_attribute_name}': top_level_attribute_name,
f'#{nested_attribute_name}': nested_attribute_name
},
ExpressionAttributeValues={f':{top_level_attribute_name}': nested_item_value},
UpdateExpression=f'SET #{top_level_attribute_name}.#{nested_attribute_name} = :{top_level_attribute_name}',
)
Hello Internet Denizens,
I'm using Firebase at the moment and am trying to query the following structure:
items
1
- name: "Car"
- Children
- 2
- 3
2
- name: "Wheels"
3
- name: "Engine"
You can see from this example that I have an array of items with Ids of 1,2, & 3. 1 also has its own sub-array called Children, which is composed of Ids 2 & 3.
How would I make a firebase query that pulled back only the Ids which had an array called children?
At first, I thought something like the following would work:
<my firebase path>.child('items').orderByChild('Children').once('value', function(snap){
// ... No results :(
});
No luck. Next, I tried adding an attribute called hasChildren = true to the Id of 1 and then queried like so.
<my firebase path>.child('items').orderByChild('hasChildren').equalTo(true).once('value', function(snap){
// ... iterate over results, which in this case is the Id 1
});
However, when I do a snap.ForEach(function(data)... and then look at data.val().Children, it's just a blank result, which confuses me because I thought Firebase pulled back all child nodes. Hence, the need to flatten your data.
How do I get those Children values?
** Update ** Plunker Added
http://plnkr.co/edit/9U0ujEwdKi7sgQGmd6IE?p=preview
Looks like this was a comedy of errors.
1) In the simple example, 1, 2, & 3 are ordered, so firebase treats it as an array. In the real world, those numbers are much more random so firebase treats them as an object since they aren't ordered.
2) For some reason, Intellij wasn't showing the Children object's contents. I had to type in something like data.val().Children['3'] to get a value or something like for(var property in data.val().Children) to iterate through the fields. Weird.
3) I STILL can't do .child('items').orderByChild('Children') ... since that returns all records. Oddly enough, however, it returns the ones that have Children first.
DataSnapshot.forEach returns another snapshot in the callback. So instead of trying to access data['Children'], you would have to use data.val().Children.
Looks like modifying my data structure is the best way to go since checking for the existence of an array object isn't possible at this point.
I have a simple social-networking like graph w/ users, friends, comments, likes etc. Users can "own" items, comment on "items", like "items". I am trying to write a cypher query that returns "items" along w/ extra information to display them in my stream.
I have tried using optional match and collect and stuff, but there is always some part of the result that doesn't work.
Specifically, for a given user(say user1), I want to return "items" that:
a specific user + his friends own
show number of likes,
also show number of comments,
Know if the item is already owned by me (so I can hide "own" button in the UI)
If the item is owned by friends, I want to show name, image of up to 2 friends (but not more than 2 friends if, say, 5 friends own that item)
You can copy-paste below to get the graph
// 3 users
CREATE (u1:USER{name:"USER1", image: "image1"})
CREATE (u2:USER{name:"USER2", image: "image2"})
CREATE (u3:USER{name:"USER3", image: "image3"})
//3 items
CREATE (i1:ITEM{name:"ITEM1"})
CREATE (i2:ITEM{name:"ITEM2"})
CREATE (i3:ITEM{name:"ITEM3"})
// OWNERSHIP ..
//user1 owns 2 items
CREATE (u1)-[:OWNS]->(i1)
CREATE (u1)-[:OWNS]->(i2)
// user2 owns i2 and i3
CREATE (u2)-[:OWNS]->(i2)
CREATE (u2)-[:OWNS]->(i3)
// user3 also owns i2 and i3 (so i2 is owned by u1, u2 and u3; and i3 is owned by u2 and u3)
CREATE (u3)-[:OWNS]->(i2)
CREATE (u3)-[:OWNS]->(i3)
// FRIENDSHIP..
// user1 is friend of both user2 and user3
CREATE (u1)-[:FRIEND_OF]->(u2)
CREATE (u1)-[:FRIEND_OF]->(u3)
// COMMENTS ..
//user1 has commented on all those items he owns
CREATE (u1i1:COMMENT{text:"user1 comment on item1"})
CREATE (u1)-[:COMMENTED]->(u1i1)-[:COMMENT_FOR]->(i1)
CREATE (u1i2:COMMENT{text:"user1 comment on item2"})
CREATE (u1)-[:COMMENTED]->(u1i2)-[:COMMENT_FOR]->(i2)
//user 2 has also commented on all those he owns
CREATE (u2i2:COMMENT{text:"user2 comment on item2"})
CREATE (u2)-[:COMMENTED]->(u2i2)-[:COMMENT_FOR]->(i2)
CREATE (u2i3:COMMENT{text:"user2 comment on item3"})
CREATE (u2)-[:COMMENTED]->(u2i3)-[:COMMENT_FOR]->(i3)
// LIKES ..
//user1 has liked user2's and user3's items
CREATE (u1)-[:LIKED]->(i2)
CREATE (u1)-[:LIKED]->(i3)
//user2 has liked user1's items
CREATE (u2)-[:LIKED]->(i1)
Let's build your query up step by step:
Specifically, for a given user(say user1), I want to return "items" that:
a specific user + his friends own
MATCH (u:USER {name: "USER1"})-[:FRIEND_OF*0..1]-(friend:USER)-[:OWNS]-(i:ITEM)
WITH u,i,
// Know if the item is already owned by me (so I can hide "own" button in the UI)
sum(size((u)-[:OWNS]->(i))) > 0 as user_owns,
// If the item is owned by friends, I want to show name, image of up to 2 friends
collect({name:friend.name, image:friend.image})[0..2] as friends
RETURN u,i, user_owns, friends
// show number of likes,
sum(size(()-[:LIKED]->(i))) as like,
// also show number of comments,
sum(size(()-[:COMMENT_FOR]->(i))) as comments
Actually because it is such a good question, I sat down and created a GraphGist documenting each step here.
Fairly easy. First you need to have a variable path length match from 0..1 on FRIEND_OF returning either yourself. Follow to all items being owned by those.
Use OPTIONAL MATCH for likes and comments since there might or might not exist any.
Since there are potentially multiple paths to a single item, you need to count the distinct likes and comments.
To check if you already own the item, check the endpoint of the variable path match from above if its name is yours.
For getting up to two images of the friends owning the item filter the list for your friends and return the image property. Last step is to slice the collection for the first two elements using subscript operator.
MATCH (:USER { name:'USER1' })-[:FRIEND_OF*0..1]->(me_or_friend)-[:OWNS]->(item)
OPTIONAL MATCH (item)<-[l:LIKED]-()
OPTIONAL MATCH (item)<-[c:COMMENT_FOR]-()
WITH item, count(distinct l) AS likes, count(distinct c) AS comments,
collect(DISTINCT me_or_friend) AS me_or_friends
RETURN item, likes, comments,
ANY (x IN me_or_friends WHERE x.name='USER1') AS i_already_own,
[x IN me_or_friends WHERE x.name<>'USER1' | x.image][0..2] as friendImages
Final comment:
On SO we appreciate if you show in your question what you've already tried yourself to solve the problem. Question like "solve that problem for me" are not that much welcome.