2

I am trying to run this code:

function updateUserLimitations(userId, limitations, batchOrTransaction = undefined) {
  const userLimitationsRef = firestore
    .collection("users")
    .doc(userId)
    .collection("limitations")
    .doc("userLimitations");

  if (batchOrTransaction) {
    return batchOrTransaction.set(
      userLimitationsRef,
      limitations,
      { merge: true }
    );
  }

  return userLimitationsRef.set(limitations, { merge: true });
}

updateUserLimitations(userId, { "messages.totalMessages": admin.firestore.FieldValue.increment(1) });

But...

Instead of getting this doc data in my db:

{ // Doc data
   messages: {
      initialDate: timestamp, // Date of the first message (I need to preserve it),
      totalMessages: 20,
   },
}

I am getting:

{
   ...other doc data...,
   messages.totalMessages: 20,
}

I need the set with the merge option, because I am updating and creating if the doc doesn't exist....

Any ideas? Am I doing something wrong here?

Victor Molina
  • 2,353
  • 2
  • 19
  • 49
  • Hi. Dot notation only works for maps. "message.totalMesssages" is not a map. You should do something like: ``{ message: {} } message['totalMessages'] = admin.firestore.FieldValue.increment(1)``. Also, Why using this when you're applying ``merge: true``? If you're using merge, you could directly write ``{ message: { totalMessages: 'yourValue' } }`` – Lucas David Ferrero Dec 01 '21 at 12:10
  • @LucasDavidFerrero does { merge: true } work for updating a specific field of an object too? I thought it was only possible via dot notation. – Victor Molina Dec 01 '21 at 12:13
  • What does `limitations` contain? Show us the value. – Alex Mamo Dec 01 '21 at 12:15
  • @AlexMamo I updated the question with the value – Victor Molina Dec 01 '21 at 12:17
  • 1
    I think this could be useful to you @VictorMolina https://stackoverflow.com/questions/46597327/difference-between-set-with-merge-true-and-update – Lucas David Ferrero Dec 01 '21 at 12:24
  • okay it works with the current answer. Didn't know that set combined with { merge: true } merges objects fields too, I thought it only merged the doc top level fields – Victor Molina Dec 01 '21 at 12:24

4 Answers4

4

You can use an object for "setting" nested data:

{
  messages: {
    totalMessages: 20,
  }
}

The "dot" notation is for applying updates to nested data.

Elliot Hesp
  • 503
  • 3
  • 8
  • The object itself has more data. I am using the dot notation for updating a specific field. BTW, I will try it, but I think that the object will be overlapped, instead of updating the specific field. – Victor Molina Dec 01 '21 at 12:10
  • my fault, it deeply update object fields too. – Victor Molina Dec 01 '21 at 12:26
1

If you need only to update, then use the update() function instead of set(). So please change the following line of code:

return userLimitationsRef.set(limitations, { merge: true });

To:

return userLimitationsRef.update(limitations);

And your:

updateUserLimitations(userId, { "messages.totalMessages": admin.firestore.FieldValue.increment(1) });

It will work as expected. If you want to add if it doesn't exist, then you should indeed use the set() function.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • 2
    The main problem is that I need to create the doc if doesn't exist, as I specified at the bottom of the question. BTW, without the dot notation, it seems that I can deeply update the object combining set() with { merge: true } too! – Victor Molina Dec 01 '21 at 12:34
  • @AlexMamo: The `update()` function does not take write options Alex and can (last I checked) not be used to *create* a document. https://firebase.google.com/docs/reference/js/v8/firebase.firestore.DocumentReference#update – Frank van Puffelen Dec 01 '21 at 15:06
  • @FrankvanPuffelen Didn't see that "creating if the doc doesn't exist". Of course, the update function does not contain a second argument. Forgot to remove when copied and paste it. As the name says, it updates, if a document exists. Doesn't create documents. – Alex Mamo Dec 01 '21 at 15:18
1

With my current implementation

function updateUserLimitations(userId, limitations, batchOrTransaction = undefined) {
  const userLimitationsRef = firestore
    .collection("users")
    .doc(userId)
    .collection("limitations")
    .doc("userLimitations");

  if (batchOrTransaction) {
    return batchOrTransaction.set(
      userLimitationsRef,
      limitations,
      { merge: true }
    );
  }

  return userLimitationsRef.set(limitations, { merge: true });
}

I can deeply update the document messages field too, as follows:

updateUserLimitations(
   userId,
   { 
      messages: {
        totalMessages: admin.firestore.FieldValue.increment(1),
      }
   }
);

It preserves all the previous existing object fields and updates the specific one.

{ // Doc data
  messages: {
    initialDate: timestamp, // PRESERVED!
    totalMessages: 21,
  },
}
Victor Molina
  • 2,353
  • 2
  • 19
  • 49
0

You could use mergeFields.

For example:

const newBook = { id: '', name: 'Some book' };

docRef.set(
    {`books.${newBook.id}`: newBook},
    { mergeFields: [`books.${newBook.id}`] }
);

This way you only set the books.${newBook.id} field value.

Ref: https://firebase.google.com/docs/reference/node/firebase.firestore.SetOptions#optional-mergefields