Dan Leighton
Dan Leighton

Reputation: 137

Copy single document in Firestore and edit fields in new copy

I need a utility function that can copy a single document in Firestore from one collection to another.

This excellent answer provides a way to copy a collection. But I can't work out how to modify it to copy a single document.

For instance I have this current structure:

collection1/abc123000000

The document abc123000000 has fields name and email with content Joe Bloggs and [email protected] respectively.

I wish to copy the xyz123000001 document from collection1 and all its fields and data to a new document in collection2:

collection2/xyz910110000

I would happily just run the command from the terminal to achieve this.

Ideally of course, it would be useful to have a function that looped through and copied all documents from one collection to the other dependent on the content of a field!

Many thanks in advance for any help.

[Original question title edited to assist in future searches as extra info added into the answer.]

Upvotes: 1

Views: 2893

Answers (2)

Dan Leighton
Dan Leighton

Reputation: 137

Many thanks to José Soní and to Lahiru Chandima on this post about copying collections for giving me the key bits of information which allowed me to solve this - outstandingly helpful!

I've found it really frustrating not having all the bits of the puzzle to solve this issue...so I am posting a heavily commented version below which I hope will be of use to anyone coming after. Apologies to anyone who already knows all this stuff...this answer is not for you ;-)

// Create a file with this code.
// In your Firestore DB, create the destination collection.
// const firebaseUrl refers to your databaseUrl which you can find in the project settings of your Firebase console.
// Save as filename.js within the directory where you have initialised Firebase.
// Ensure Node.js is installed and that node is available, try node --version
// Then run node filename.js from the terminal.

const firebaseAdmin = require('firebase-admin');
const serviceAccount = '../../firebase-service-account-key.json';
const firebaseUrl = 'https://my-app.firebaseio.com';

firebaseAdmin.initializeApp({
    credential: firebaseAdmin.credential.cert(require(serviceAccount)),
    databaseURL: firebaseUrl
});

const db = firebaseAdmin.firestore();

function copy(db){
    db.collection('collectionName').get()
      .then((snapshot) => {
        snapshot.forEach((doc) => {
          // We are iterating on the documents of the collection
          let data =  doc.data();
          console.log(doc.id, '=>', doc.data());
          if(doc.id == 'randomDocIdAssignedByFirestore'){
            // We have read the document till here

            //From here: we create a new document in the collection

            // Change some of the data in the fields in the new document
            let id = 'newMeaningfulDocId'; // Instead of allowing Firestore to create a random ID
            data.title = 'Meaningful Title'; // Write new data into the field called title
            data.description = 'Meaningful Description'; // Write new data into the field called description

            /*
            We are using a Firestore Reference type field in our DB to reference a parent doc.
            If you just used data.parent = '/category/OS_EnDept'; you would end up writing a string field type.
            However, you want to use a Reference Type AND you want the parent to be collectionName
            do it like this:
                            data.parent = doc;
            We, however, want to be able to write in different parent collection names - hence the next line.
            */
            data.parent = db.collection('collectionName').doc('desiredParentDocId')
            let setDoc = db.collection('collectionName').doc(id).set(data);
            setDoc.then(res => {
              console.log('Set: ', res);
            });
          }
        });
      })
      .catch((err) => {
        console.log('Error getting documents', err);
      });
   }
// Call the function when we run this file...
   copy(db);

Upvotes: 1

Soni Sol
Soni Sol

Reputation: 2612

Yo can do this by reading the collection, iterating on it and for each element of the collection 1 write it in collection 2.

Here is a quick example:

function copy(db){
  db.collection('collection1').get()
    .then((snapshot) => {
      snapshot.forEach((doc) => {
        // We are iterating on the documents of the collection
        let data =  doc.data();
        console.log(doc.id, '=>', doc.data());
        if(<PUT_CONDITIONS_HERE>){
          //we have read the document till here
          let setDoc = db.collection('collection2').doc(doc.id).set(data);
          setDoc.then(res => {
            console.log('Set: ', res);
          });
        }
      });
    })
    .catch((err) => {
      console.log('Error getting documents', err);
    });
 }

For more examples on how to read and write using the nodejs CLI you can go to the Github repository here

Also this can be done with a query from collection one to filter at that level, and iterate over less files. However this depends on the conditions you have to determine if it needs to be copied or not.

Upvotes: 2

Related Questions