How do I get the document ID for a Firestore document using kotlin data classes

前端 未结 6 1204
慢半拍i
慢半拍i 2020-12-13 11:22

I have kotlin data class

data class Client(

    val name: String = \"\",
    val email: String = \"\",
    val phone: String =\"\") {
constructor():this(\"         


        
相关标签:
6条回答
  • 2020-12-13 11:26

    I just improved the solution of @iCediCe, and add methods to DocumentSnapshot and QuerySnapshot.

    interface HasId {
        var id : String
    }
    
    inline fun <reified T : HasId> DocumentSnapshot.toObjectWithId(): T {
        return this.toObject(T::class.java)!!.also {
            it.id = this.id
        }
    }
    
    inline fun <reified T : HasId> QuerySnapshot.toObjectsWithId(): List<T> {
        return this.documents.map {
            it.toObjectWithId<T>()
        }
    }
    

    And usage:

    data class User(
        @get:Exclude
        override var id: String,
        ...
    ): HasId
    
    
    val user = documentSnapshot.toObjectWithId<User>()
    val users = querySnapshot.toObjectsWithId<User>()
    
    0 讨论(0)
  • 2020-12-13 11:27

    Here is how I solved the problem.

    data class Client(
        val name: String = "",
        val email: String = "",
        val phone: String ="",
        @get:Exclude var id: String = "") {
    constructor():this("","","")
    }
    

    I use @get:Exclude on the id to make sure the id doesn't get sent to Firestore on save, and then when fetching the list of clients do:

    snapshot.documents.mapTo(list) {
                var obj = it.toObject(Client::class.java)
                obj.id = it.id
                obj
            }
    

    Setting the id of the new object to the id of the document reference.

    0 讨论(0)
  • 2020-12-13 11:30

    Yes, it's possible to get id without storing it, using DocumentSnapshot. I will try to build complete examples here.

    I created a general Model class to hold the id:

    @IgnoreExtraProperties
    public class Model {
        @Exclude
        public String id;
    
        public <T extends Model> T withId(@NonNull final String id) {
            this.id = id;
            return (T) this;
        }
    }
    

    Then you extend it with any model, no need to implement anything:

    public class Client extends Model
    

    If I have list of clients here, trying to query the list to get only clients with age == 20:

    clients.whereEqualTo("age", 20)
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (DocumentSnapshot documentSnapshot : task.getResult().getDocuments()) {
                            // here you can get the id. 
                            Client client = document.toObject(client.class).withId(document.getId());
                           // you can apply your actions...
                        }
                    } else {
    
                    }
                }
            });
    

    And if you are using EventListener, you can also get the id like the following:

    clients.addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
            for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
                // here you can get the id. 
                QueryDocumentSnapshot document = change.getDocument();
                Client client = document.toObject(client.class).withId(document.getId());
                           // you can apply your actions...
            }
        }
    });
    

    documentSnapshot.getId()) will get you the id of the Document in the collection without saving the id into the document.

    Using Model will not let you edit any of your models, and don't forget using @IgnoreExtraProperties

    0 讨论(0)
  • 2020-12-13 11:38

    I solved it by creating a extension method on QueryDocumentSnapshot like this:

    inline fun <reified T : HasId>QueryDocumentSnapshot.toObjectWithId(): T {
        val model = this.toObject(T::class.java)
        model.id = this.id
        return  model
    }
    

    Now I can map like this (which I find to look nice and clean):
    myModelWithId = it.toObjectWithId<MyModel>()

    For this to work I made a simple hasId interface that the model needs to implement:

    interface HasId{
        var id : String
    }
    
    @IgnoreExtraProperties
    data class MyModel(
            @get:Exclude override var id : String    = "",
            val data                     : String    = "",
    
    ): Serializable, HasId
    
    0 讨论(0)
  • 2020-12-13 11:39

    Great solutions. Looks like Firestore came up with a way to add an annotation to your model to solve this problem.

    You could do:

    @DocumentId
    val documentId: String
    

    And then Firestore will generate that field when calling toObject or toObjects. When you .set() the document in Firestore, it will exclude this field.

    0 讨论(0)
  • 2020-12-13 11:51

    I just fetch an id from Firestore before and set it as a field on my object before I create it in Firebase:

        override fun addTodo(todo: Todo) =
        Completable.fromAction {
            val id = firestore
                .collection(COLLECTION_TODOS)
                .document()
                .id
    
            val newTodo = todo.copy(id = id)
    
            firestore
                .collection(COLLECTION_TODOS)
                .document(id)
                .set(newTodo)
        }
    
    0 讨论(0)
提交回复
热议问题