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.
Related
(1) In my Canvas App I create a collection like this:
Collect(colShoppingBasket; {Category: varCategoryTitle ; Supplier: lblSupplier.Text ; ProductNumber: lblProductNumber.Text });;
It works - I get a collection. And whenever I push my "Add to shopping basket" button, an item are added to my collection.
(2) Now I want to sort the collection and then use the sorted output for other things.
This function sorts it by supplier. No problems here:
Sort(colShoppingBasket; Supplier)
(3) Then I want to display the SORTED version of the collection in various scenarios. And this is where the issue is. Because all I can do is manipulate a DISPLAY of the collection "colShoppingBasket" - (unsorted).
(4) What would be really nice would be the option to create and store a manipulated copy of the original collection. And the display that whereever I needed. Sort of:
Collect(colShoppingBasketSORTED; { Sort(colShoppingBasket; supplier) });; <--- p.s. I know this is not a working function
You could use the following:
ClearCollect(colShoppingBasketSorted, Sort(colShoppingBasket, Supplier))
Note that it is without the { }
This will Clear and Collect the whole colShoppingBasket sorted.
If you want to store the table in a single row in a collection, you can use
ClearCollect(colShoppingBasketSortedAlternative, {SingleRow: Sort(colShoppingBasket, Supplier)})
I wouldn't recommend this though because if you want to use the Sorted values you'd have to do something like this:
First(colShoppingBasketSortedAlternative).SingleRow -> this returns the first records of the colShoppingBasketSortedAlternative then gets the content of the SingleRow column, which in this case is a Collection
Note: You will need to replace the , with ; to work on your case
Lets say I have a checklist collection where each item is it's own document since it contains a lot of other data.
I want the user to be able to drag and drop to reorder this list an save it that way. My initial thought was to have a field that is changed to reflect this order but moving one object requires changing the value on every document that is after the new location.
Is there a way to achieve this without a massive number of writes?
If the order of documents changes frequently, you can avoid writing the contents of each document by instead using a whole different document to maintain the order, using an array of strings containing the document IDs. In fact, you could hold lots of different orderings depending on how you want to display the documents.
Say you have a collection of documents:
collection
- docA
- docB
- docC
Now you want to store mutable orderings in a document called "order" in another collection:
collection-meta
- order
- byAlpha: ["docA", "docB", "docC"]
- byScore: ["docC", "docA", "docB"]
Just query the "order" document first, then get each document for display in the order defined in the array. To reorder the documents, just update the contents of the single array in the "order" doc.
I usually do this by using a floating point value for the order.
Say you have a list with these 3 documents:
ID=a, order=1.0
ID=b, order=2.0
ID=c, order=3.0
Now let's assume we want to move document a between b and c. You'd do that by changing its order to 2.0 + (3.0 - 2.0) / 2 = 2.5.
ID=b, order=2.0
ID=a, order=2.5
ID=c, order=3.0
This works for a reasonable number of swaps, which is the scenario I usually deal with.
If you're dealing with a large number/potentially infinite of iterations, you'll want to look at the precision of the floating point operation. In that case your alternative might be to use a custom value type, i.e. encoding the value into a string field and then use a custom library to do the division at a higher numeric precision.
If i understood your question correctly, what you can do is You can have a separate document that contains index of all the other checklist document. All the checklist documents can keep their respective info as it is supposed to be. For example
Checklist1 Checklist3
Checklist2 Checklist2
Checklist3 Checklist1
Index Index
(Before) (After Drag)
and Index contains structure like below after the drag is performed:
Index -> field value
Checklist1 3
Checklist2 2
Checklist3 1
i am trying to order a field in Firestore which is a string, but contains numbers. Can i somehow specify the ordering method or customise it?
** Edit 1:
The order i am getting is like this:
1 -> 10 -> 100 -> 101
But i want:
1 -> 2 -> 3 -> 4
If you have a need for strings to behave like numbers, you're using the wrong data type. Use numbers when you need numeric behavior. It sounds like you may have to rewrite all your documents and update your code to properly type the data.
In short, no, you can't do what you're asking (without making a change, or reordering the documents on the client).
I was trying to order items in my Firestore Recycler by querying a number value field and the app was crashing.
So i tried :
Query query = placesRef.orderBy( String.valueOf("likesNum"), Query.Direction.DESCENDING).limit(5);
And it worked. Although the compiler is saying String.valueOF() is not necessary, my problem is perfectly solved.
Maybe that helps someone with same issue.
query = placesRef.orderBy( String.valueOf("likesNum"), Query.Direction.DESCENDING).limit(5);
This only works because when you order by String, the limit is 9 items. Nothing will show up again after that so the best way is to store the field as a number.
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}',
)
I have an ordered list of firebase locations. I'm using a property ut (update time) as their priority. I want to make the list such that it's easy to get the latest updated documents.
So I set the priority to be negative ut.
var query = fb.child('view/documents').limit(20)
query.on('child_added', function(child) {
console.log(child.val())
console.log(child.getPriority())
})
I expect something like this to return the latest 20 documents, but it doesn't, it returns the oldest 20. In the forge I see the listing the way I expect it, the latest documents are on top, but the query is sending me the bottom 20. It seems contrary to my expectations for the query to send me the bottom 20 instead of the top 20.
What really confuses me is that the child_added returns the expected order, latest (smallest priority) first. But again it's the oldest in the list.
Am I doing something wrong or is this a bug in firebase.
Thanks.
I understand your confusion, but that's really how it's supposed to work: limit(20) returns the 20 greatest-priority children, starting with the 20th-greatest-priority child and ending with the absolute-greatest-priority child (and then updating whenever a new child is added whose priority is great enough to make the list).
You can see the example at https://www.firebase.com/docs/queries.html, where the priority is the Unix timestamp of when the message was sent, and messageListRef.limit(100) is used to get the 100 most recent messages (i.e., the 100 greatest-priority messages).
I think what you are looking for is : .startAt()
before the limit(), that will return the data in correct order, without the keyword you will always get the last specified number of children.
Here is the reference : https://www.firebase.com/docs/javascript/query/limit.html