Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firestore: How to check if document exists, and create one if it doesn't #1272

Closed
avilao opened this issue Oct 15, 2017 · 35 comments
Closed

Comments

@avilao
Copy link

avilao commented Oct 15, 2017

*AngularFireStore:

Is there a way to check if a document already exists in the database?

Couldn't find an example.

Thank you

@larssn
Copy link
Contributor

larssn commented Oct 16, 2017

Don't think you can do it with AFS, and might have to rely on the underlying framework:

this.afs.firestore.doc('/users/asfaf').get()
      .then(docSnapshot => {
        if (docSnapshot.exists) {
          // do something
        }
      });

@jamesdaniels
Copy link
Member

this.afs.doc('/path').take(1).do(d => d.payload.exists())

As of 5.0 rc3 empty sets/docs should be handled correctly.

I am working on an API to make return or create easier.

@ButhSitha
Copy link

@jamesdaniels I import the operator take already but i got error
i can't use doc with take operator.

@dhawken
Copy link

dhawken commented Oct 26, 2017

I'm doing it manually - hopefully there is a better way?

  this.dataDoc = this.myDataCollectionRef.doc(id); 

  this.data = this.dataDoc.snapshotChanges().map(action => {
        if (action.payload.exists === false) {
            return null;
        } else {
            const data = action.payload.data() as Whatever;
            const id = action.payload.id;
      return { id, ...data };;  
   });

Might be incorrect usage of snapshotChanges(), but its able to determine if something is there. If you wanted to manually create it, I believe you could do that once you've determined it doesn't exist.

@sneurlax
Copy link

Just popping in to report that neither take() nor do() work (exist) for me on AngularFirestoreDocument

@sneurlax
Copy link

Currently handling this by attempting an update and setting on error as such:

this.afs.doc(`users/${uid}`)
  .update({data})
  .then(() => {
    // update successful (document exists)
  })
  .catch((error) => {
    // console.log('Error updating user', error); // (document does not exists)
    this.afs.doc(`users/${result.uid}`)
      .set({data});
  });

@vaurelios
Copy link

vaurelios commented Dec 15, 2017

@jamesdaniels example is missing somethings:

  • .snapshotChanges()
  • exists isn't a function, is a property.

So,

this.afs.doc('/path').snapshotChanges().take(1).do(d => d.payload.exists)

@Vicky443
Copy link

Vicky443 commented Dec 17, 2017

i check simply , not sure if that is the right way to do ..

const userRef: AngularFirestoreDocument = this.afs.doc(users/${user.uid});
userRef.valueChanges().subscribe(res=>{
if(res){
// do your perform
}
} );

@buchmiller
Copy link

Here's a variation of @sneurlax's example:

//Create an empty document, or do nothing if it exists.
this.afs.doc('users/sam').set({ }, { merge: true });

//Create a document with name property set to 'Sam'.
//If document already exists, it will still set the name property to 'Sam'
this.afs.doc('users/sam').set({ name: 'Sam' }, { merge: true });

@Zufarsk8
Copy link

Zufarsk8 commented Feb 5, 2018

@buchmiller
But by doing so, it will be a write in the document.
A write in the document is more costly in terms of pricing according to firebase pricing.
If we can read before we write it will be more cost efficient :)

@olaini-lature
Copy link

How about searching 2 parameters, like username and email?
I am looking for username and email that I type in input.

I think it could be in 2 ways:

  1. Maybe we can do all in one query in where clause using OR statement. I am not sure if firestore support it.

  2. I think we can search the username first. If it return null, we continue search the email. I am looking for this possible way. Any suggestion?

@math-a3k
Copy link

FTR: https://angularfirebase.com/lessons/firestore-advanced-usage-angularfire/#4-Upsert-Update-or-Create-Method

// *** Usage
this.db.upsert('notes/xyz', { content: 'hello dude'})
// *** Code
upsert<T>(ref: DocPredicate<T>, data: any) {
  const doc = this.doc(ref).snapshotChanges().take(1).toPromise() 
  return doc.then(snap => {
    return snap.payload.exists ? this.update(ref, data) : this.set(ref, data)  })
}

@jwv
Copy link

jwv commented May 21, 2018

You can use

db.doc('collectionName/documentId').ref.get().then((documentSnapshot) => {
  console.log(documentSnapshot.exists);
});

@miglrodri
Copy link

miglrodri commented Aug 31, 2018

Hello,

I am using firestore from firebase(npm) and the property .exists was not available on the querySnapshot:

https://firebase.google.com/docs/firestore/query-data/get-data

db.collection("cities").get().then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
        // doc.data() is never undefined for query doc snapshots
        console.log(doc.id, " => ", doc.data());
    });
});

querySnapshot in that context have a property .empty that I use to know if I get a document or not from the query. Hope this helps too.

@Vino16491
Copy link

@mk4yukkalab do you got workaround to ur concern

And if i need a query like : this.afs.collection('users', ref => ref.where('email', '==', email)) how i can do that?

@Hanan0o0
Copy link

And if i need a query like : this.afs.collection('users', ref => ref.where('email', '==', email)) how i can do that?

do you know how do that ??

@Hanan0o0
Copy link

@mk4yukkalab do you got workaround to ur concern

And if i need a query like : this.afs.collection('users', ref => ref.where('email', '==', email)) how i can do that?

do you know how do that ??

@hafizmowais
Copy link

hafizmowais commented Nov 12, 2018

firestore
.collection('YourCollection')
.doc('YourDocumentID').set(
{a: {b: {c: true}}},
{merge: true}
)

This one should work

@JeffGreat
Copy link

@mk4yukkalab @Hanan0o0 @Vino16491
in angular 6 I manage like that

this.afs.collection('users', (ref) => ref.where('email', '==', email).limit(1)).get().subscribe(users => { if(users.size >= 0) ...do smthg })

@funct7
Copy link

funct7 commented Dec 16, 2018

Currently handling this by attempting an update and setting on error as such:

this.afs.doc(`users/${uid}`)
  .update({data})
  .then(() => {
    // update successful (document exists)
  })
  .catch((error) => {
    // console.log('Error updating user', error); // (document does not exists)
    this.afs.doc(`users/${result.uid}`)
      .set({data});
  });

Trying to do something similar in iOS. I think this should be done in a transaction though because there's plenty of time for data to be written with the ID between the update response and the set. Also, I'm questioning whether this is a good solution since we're using an error in order to proceed.

@xgenem
Copy link

xgenem commented Mar 27, 2019

@larssn solution still works as of date.

@yuricamara
Copy link

@buchmiller
But by doing so, it will be a write in the document.
A write in the document is more costly in terms of pricing according to firebase pricing.
If we can read before we write it will be more cost efficient :)

Are you sure? For me it makes more sense, not be a cost in case of update. So, the cost will be the same as @sneurlax method.

@vasiledoe
Copy link

vasiledoe commented Nov 6, 2019

For Android in Kotlin:

fun insertOrUpdate() {
        val mapFields = hashMapOf(
            "key1" to "val1",
            "key2" to "val2",
            "key3" to "val3"
        )

        val dbInstance = FirebaseFirestore.getInstance()

        val docRef: DocumentReference = dbInstance.collection("my_collection_id")
            .document("my_doc_id")

        docRef.get()
            .addOnSuccessListener {

                if (it.exists()) {
                    docRef.update(mapFields)
                        .addOnSuccessListener { }
                        .addOnFailureListener { }

                } else {
                    docRef.set(mapFields)
                        .addOnSuccessListener { }
                        .addOnFailureListener { }
                }
            }
            .addOnFailureListener { }
    }

@MikeC711
Copy link

And if i need a query like : this.afs.collection('users', ref => ref.where('email', '==', email)) how i can do that?

I am not an expert so there may be better ways, but here is what I did in that situation:
this.afs.collection('eicpeople', ref => ref.where('familyKey', '==', person.familyKey))
.valueChanges().pipe(take(1)).subscribe(matchingRecs => {
if (matchingRecs.length > 0) // this is my equivalent to exists
else // this is no match

@jluz88
Copy link

jluz88 commented Jun 11, 2020

Maybe a bit late, but this worked for me for a web app.

db.collection('users') .where('user_id', '==', user.id) .get() .then(querySnapshot => { if(querySnapshot.docChanges().length === 0){ //Add new user to DB db.collection('users').add( user ); } }).catch((err) => { console.log(err); });

@kylebradshaw
Copy link

kylebradshaw commented Jun 16, 2020

The way I went about this a combo of creating my own unique ID based on the data by using a sha library (js-sha256), just JSON.stringify(data) to get your id then follow the approach above.

const sha = sha256(JSON.stringify({...data})).substring(0, 10);
this.db.doc(`${DATA}/${sha}`).set(data, {merge: true})

Data is always overwritten.

@Awais-mohammad
Copy link

Don't think you can do it with AFS, and might have to rely on the underlying framework:

this.afs.firestore.doc('/users/asfaf').get()
      .then(docSnapshot => {
        if (docSnapshot.exists) {
          // do something
        }
      });

.then is not applicable after .get or valuechanges

@Awais-mohammad
Copy link

I am also trying to find a solution for angular 8

@abhishrek
Copy link

All these code examples which use "docSnapshot.exists" checking, arent they forgetting that this is not a transaction, so it can not guarantee that by the time the client find out that doc does not exist and it triggers a create new doc, someone else might have created that doc, in between get() and set() calls ? (although its probability is less but not zero)

@davecoffin
Copy link

Thought Id share this...I am doing it like so:

const docRef = this.db.collection("user_flags").doc(this.user.id);
docRef.get().subscribe(doc => {
  docRef[doc.exists ? 'update' : 'set']({
    exampleFlag: true
  })
})

any objections? If the doc exists it uses update, otherwise set.

@surajsahani
Copy link

Here's a variation of @sneurlax's example:

//Create an empty document, or do nothing if it exists.
this.afs.doc('users/sam').set({ }, { merge: true });

//Create a document with name property set to 'Sam'.
//If document already exists, it will still set the name property to 'Sam'
this.afs.doc('users/sam').set({ name: 'Sam' }, { merge: true });

yes but in this case, the previous record of sam would override how to handle that?

@surajsahani
Copy link

firestore
.collection('YourCollection')
.doc('YourDocumentID').set(
{a: {b: {c: true}}},
{merge: true}
)

This one should work

but in this case previous recod would be override with the new one right? how can i check if previous was present don't write new one. or on't update new values

@surajsahani
Copy link

Thought Id share this...I am doing it like so:

const docRef = this.db.collection("user_flags").doc(this.user.id);
docRef.get().subscribe(doc => {
  docRef[doc.exists ? 'update' : 'set']({
    exampleFlag: true
  })
})

any objections? If the doc exists it uses update, otherwise set.

how to check if doc exist show an toast that doc exist and if not set the value?

@smaity35
Copy link

smaity35 commented Jun 9, 2021

this.afs.doc(`users/${uid}`)
  .update({data})
  .then(() => {
    // update successful (document exists)
  })
  .catch((error) => {
    // console.log('Error updating user', error); // (document does not exists)
    this.afs.doc(`users/${result.uid}`)
      .set({data});
  });

Am So happy It Working. Thank You

@whyboris
Copy link

whyboris commented Jan 13, 2022

It looks like you need .ref in the chain to check for document's existence now 🙄

import { AngularFirestore } from '@angular/fire/compat/firestore';

constructor(public db: AngularFirestore){};

const collectionRef = this.db.collection('my-collection').doc('my-document').collection('sub-collection');

collectionRef.doc('abc').ref.get().then(snapshot => {
  if (snapshot.exists) {
    console.log('document abc exists');
  } else {
    console.log('document abc DOES NOT exist');
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests