Firestore Update fields in nested objects with dynamic key - firebase

I need to update a field in a nested object with a dynamic key.
the path could look like this: level1.level2.DYNAMIC_KEY : updatedValue
The update-method deletes everything else on level1 instead of only updating the field in the nested object. The update() acts more like a set(). What am I doing wrong?
I tried the following already:
I read the documentation https://firebase.google.com/docs/firestore/manage-data/add-data#update-data
but that way it is a) static and b) still deletes the other fields.
Update fields in nested objects
If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call update()
This would be static and result in
update({
'level1.level2.STATIC_KEY' : 'updatedValue'
});
Then I found this answer https://stackoverflow.com/a/47296152/5552695
which helped me to make the updatepath dynamic.
The desired solution after this could look like
field[`level1.level2.${DYNAMIC_KEY}`] = updateValue;
update(field);
But still: it'll delete the other fields in this path.
UPDATE:
The Structure of my Doc is as follows:
So inside this structure i want to update only complexArray > 0 > innerObject > age
Writing the above path into the update() method will delete everything else on the complexArray-level.
A simple update on first-level-fields works fine and lets the other first-level-fields untouched.
Is it possible, that firestore functions like update() can only act on the lowest field-level on an document. And as soon as i put complex objects into an document its not possible to select such inner fields?
I know there would be the solution to extract those "complex" objects into separate collections + documents and put these into my current lowest document level. I think this would be a more accurate way to stick to the FireStore principles. But on Application side it is easier to work with complex object than to always dig deeper in firestore collection + document structure.
So my current solution is to send the whole complex object into the update() method even though I just changed only one field on application side.

Have you tried using the { merge: true } option in your request?
db
.collection("myCollection")
.doc("myDoc")
.set(
{
level1: { level2: { myField: "myValue" } }
},
{ merge: true }
)

Related

Project all fields except X

I'm trying to return partial documents with DynamoDB. Instead of listing all the items I want returned using ProjectionExpression, it would be far easier in this case to just filter the single item I don't want returned.
i.e. below, I'd like to return everything except privateItem.
{
"item1" : ...,
"item2" : ...,
"privateItem" : {
...
}
}
Is this possible? I've scoured the docs to no avail.
Thanks.
Based upon the docs it seems that you can't, you can only get either every field or a whitelist of fields (maybe that'll change in future though).
In your case I would imagine the best thing to do is to delete/filter the field you don't want after you've retrieved the document from DynamoDB.

delete item out of firebase database

I have a restaurant bookmarks list in my firebase, but I don't know how to delete a specific restaurant in my database.
So I have the function unfavorite(favorite) where I pass the favorite restaurant as a parameter. Till here, than I want to pass this parameters id to the query to remove from the database like this:
this.afDb.list(`bookmarks/${user.uid}/restaurant/${favorite.restaurant.id})`).remove();
here is a screenshot of my database list:
How can I remove that specific restaurant out of the bookmarks list?
You will first need to add an ".indexOn": ["id"] rule to your database something like this:
"bookmarks": {
"$user_id": {
// normal reads and write rules here
},
".indexOn": ["id"]
This step is necessary for firebase database because otherwise you wont be able to use the orderByChild() and equalTo() methods.
Then, where you have your delete function, you want to instead use:
exampleRef = yourDb.ref("bookmarks/${user.uid}"); //this is just to simplify your reference a bit
exampleRef.orderByChild('id').equalTo(theDeleteIDhere).once('value').then(snapshot => {
snapshot.forEach((restaurant) => {
restaurant.ref.remove();
});
}); //this is a Promise that you can modify to return "true" if successful for example
The example I provided is just the way I have done it before (i.e. I prefer to use promises; hence the then() becuase this makes it easier to return that promise in an angular service which allows me to check whether the request was successful). You can use any variation of this so long as you have the "indexOn" rule and you use any sort of "sorting" method firebase provides here
Method 2
When I wrote this I totally glanced over the ability to map your restaurants like such:
Lets say your project is already listing those restaurants. You can therefore save each restaurant's auto generated id to a variable or map:
restaurants; // this is a map like this <your-identifier:autoID>
you can then easily just call:
exampleRef.child(restaurants[yourIdentifier]).remove();

Cloud Functions for Firebase - get parent data from database trigger

Is there a clean built in way of directly referencing the data value of the node above the database trigger? I understand I can get a parent ref which I could then query for the value, but if there was a more concise way of doing this that would be great thanks.
For clarity, I want to use a child node within an object as a trigger, and when it occurs get the value of the parent object directly, to avoid the function being invoked when other changes are made to the parent object like so:
const parentObject = {
triggerValue: 'I want the function to be triggered only on writes to this path',
parentValue: 'But I also want this value',
}
Thanks
I've googled for this answer like six times and keep having to re-implement the solution.
Here's how to get the parent data, post, from a child attribute, post.body, that's changed:
exports.doStuff = functions.database
.ref("/posts/{postId}/body")
.onWrite(event => {
return event.data.ref.parent.once("value").then(snap => {
const post = snap.val();
// do stuff with post here
});
});
You can use event.data.ref and event.data.adminRef to navigate the database. They are Reference objects and work exactly like they would if you were building a web app with Firebase on the client side. So, if using the parent property to navigate up a level works fine for you, then just use that. There's no additional special syntax.

Swift Array Objects Invalidated During Delete

Swift 3, Xcode 8, Realm 2.0.0
I have a Realm object that has a List property called like this:
class Entry: Object{
let approaches = List<Approach>()
//...
}
I pull an Entry object that has an Approach list in it so that entry.approaches is a list of approaches.
I then load up my own separate array with the list so that I can manipulate its contents using Approach objects.
var approaches = [Approach]()
for approach in entry.approaches{
approaches.append(approach)
}
Prior to saving some edits to the Entry, I want to remove all existing approaches and replace them with new elements I put in the approaches array elsewhere in my code.
try realm.write {
print(approaches) //This prints out my Approach objects
realm.delete(entry.approaches) //Clear out existing items in list
print(approaches) //-!- This prints [[invalid object], [invalid object]]
}
If I manually put the Realm objects in my own approachesarray, why are they invalidated when entry.approaches is deleted?
Is there a better way to replace all objects in a list than this?
It's intended behavior. Maybe you are confusing deleting objects from Realm and removing objects from List. realm.delete() execute the former. The objects were deleted from Realm. Then all references pointed to the objects would be invalidated.
If you'd like to clear entry.approaches, not deleting from Realm, you can use List.removeAll() method.
try realm.write {
entry.approaches.removeAll()
}
FYI: Copying all list items to array can be written like the following. No need to iterate list.
let approaches = [Approach](entry.approaches)

What's the right way to check if collection.find success in Meteor?

The Meteor document said 'find returns a cursor', and can use 'fetch' to return all matching documents, but I didn't find a complete reference of this 'cursor' object.
I want to use this 'cursor' object to check if find sucessfully got some result or got nothing.
Following is what I am doing now:
if (Tags.find({name: tag["tag"]}).fetch().length === 0) {
// Not found, add default documents
}
Not sure if this is right way(best practice) to do this?
The idiom is to use .findOne:
if (!Tags.findOne(...)) {
// nothing found, add default documents
}
This is more efficient than fetch() because it only queries the database for one document.
You can also use <cursor>.count, but beware that in Mongo, count operations are expensive.

Resources