6

Every day I am importing products from external retailers into a Google Cloud Firestore database.

During that process, products can be either new (a new document will be added to the database) or existing (an existing document will be updated in the database).

Should be noted that I am importing about 10 million products each day so I am not querying the database for each product to check if it exists already.

I am currently using set with merge, which is exactly what I need as it creates a document if it doesn't exist or updates specific fields of an existing document.

Now the question is, how could I achieve a createdAt timestamp given that provided fields will be updated, therefore the original createdAt timestamp will be lost on update? Is there any way to not update a specific field if that field already exists in the document?

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
SebScoFr
  • 881
  • 1
  • 9
  • 24
  • 1
    What about creating a new dedicated field with a Cloud Function when a doc is created. This field shall not be included in the object you pass to the `set()` method when you update the docs. – Renaud Tarnec Aug 02 '18 at 15:01
  • Good point. Will give that a go. – SebScoFr Aug 02 '18 at 15:08
  • I've elaborated an answer based on my comment, with the corresponding Cloud Function code. Hope this helps. – Renaud Tarnec Aug 02 '18 at 15:34
  • On the Node.js side, the [DocumentSnapshot](https://cloud.google.com/nodejs/docs/reference/firestore/0.15.x/DocumentSnapshot#createTime) contains a field for `createTime`. – Bob Snyder Aug 02 '18 at 15:58
  • @BobSnyder Thanks for the info Bob, interesting to know! Do you suggest that the CF could use `snap.createTime;` as the field value? – Renaud Tarnec Aug 02 '18 at 16:00
  • @RenaudTarnec: It's not clear to me. The documentation for the Admin version of `DocumentSnapshot` is different than `@google-cloud/firestore`. Not sure if `createTime` is available. – Bob Snyder Aug 02 '18 at 16:08
  • @BobSnyder See my update to the answer: it works. No idea which method is the best to use however! – Renaud Tarnec Aug 02 '18 at 16:11

2 Answers2

7

I suggest that you use a Cloud Function that would create a new dedicated field when a Firestore doc is created. This field shall not be included in the object you pass to the set() method when you update the docs.

Here is a proposal for the Cloud Function code:

const functions = require('firebase-functions');

const admin = require('firebase-admin');

admin.initializeApp();


exports.productsCreatedDate = functions.firestore
  .document('products/{productId}')
  .onCreate((snap, context) => {

    return snap.ref.set(
      {
        calculatedCreatedAt: admin.firestore.FieldValue.serverTimestamp()
      },
      { merge: true }
    )
    .catch(error => {
       console.log(error);
       return false;
    });
  });

Based on Bob Snyder comment above, note that you could also do:

const docCreatedTimestamp = snap.createTime;
return snap.ref.set(
  {
    calculatedCreatedAt: docCreatedTimestamp
  },
  { merge: true }
)
.catch(...)
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
4

in version firebase 9 the correct way is:

import { serverTimestamp } from "firebase/firestore";
....
return snap.ref.set(
      {
        calculatedCreatedAt: serverTimestamp();
.....
luis urdaneta
  • 91
  • 1
  • 4