regisin
regisin

Reputation: 127

Using transaction to check if multiple docs exists, add them if not

This is my situation: I have a local json file with a lot of entries. This file is not sanitized (i.e. there might be repeated entries in the list). My goal is to add each item (once) from the local file to my DB using firestore.

The docs say that a transaction can be multiple get operations followed by multiple update/set/delete operations.However, the samples usually only have a single get. How can I go and have multiple get operations without nesting inside then()? I guess I could get the entire collection and compare the result. But then I won't have learned how to use multiple get!

Sample data:

/* mock data */
var myList = [
    {
        id: '123',
        name: 'Aloha 1',
        latitude: 0.0,
        longitude: 1.1,
        url: 'https://www.google.com'
    },
    {
        id: '321',
        name: 'Aloha 2',
        latitude: 3.0,
        longitude: 3.1,
        url: 'https://www.gmail.com'
    },
    {
        id: '123',
        name: 'Aloha 1',
        latitude: 0.0,
        longitude: 1.1,
        url: 'https://www.google.com'
    },
    {
        id: '321',
        name: 'Aloha 2',
        latitude: 3.0,
        longitude: 3.1,
        url: 'https://www.gmail.com'
    }
];

This is my attempt (I know this is wrong, I have a sequence of get/set operations...):

var transaction = db.runTransaction(t => {
    for (item in myList) {
        const ref = db.collection('superList').doc(item.id);
        const sanitizedEntry = {
            name: item.name,
            location: new admin.firestore.GeoPoint(item.lat, item.lon),
            url: item.url,
        }
        t.get(ref).then(doc => {
            if (!doc.exists) {
                t.set(ref, sanitizedEntry);
            }
        });
    }
})
.then(result => {
    console.log('Transaction success!');
})
.catch(err => {
    console.log('Transaction failure:', err);
});

The resulting DB should only contain superList/123, and superList/321 (plus whatever other documents are already in the collection).

Upvotes: 0

Views: 765

Answers (2)

Jason Berryman
Jason Berryman

Reputation: 4908

If you have a fixed number of items to check, you can use getAll to get many documents concurrently. This is an all or nothing result. You'll either get all documents or an error.

let superList = db.collection('superList');
let myRef0 = superList.doc(item[0].id);
let myRef1 = superList.doc(item[1].id);
let myRef2 = superList.doc(item[2].id);
let myRef3 = superList.doc(item[3].id);

return db.getAll(itemRef0, itemRef1, itemRef2, itemRef3).then(response => {
  // The data is returned in an array, here
}.catch(err => {
  console.error(`An error happened ${err}`);
});

Upvotes: 1

regisin
regisin

Reputation: 127

As per suggestion, not using transactions worked fine (edit: fixed code):

for (item in myList) {
     const itemRef = db.collection('superList').doc(item.id);
     const itemDoc = itemRef.get()
                .then(itemSnap => {
                    if (!itemSnap.exists) {
                        const itemObj = {
                            name: gymitemRaw.name,
                            location: new admin.firestore.GeoPoint(item.llatitude, item.longitude),
                            url: item.url,
                        };
                        itemRef.set(itemObj);
                    }else{
                        console.log('Item already exists in DB');
                    }
                });
}

Upvotes: 0

Related Questions