Как получить идентификатор документа для документа Firestore с использованием классов данных kotlin

У меня есть класс данных kotlin

data class Client(

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

У меня есть firestore, чтобы заполнить данные в классе просто отлично, однако я в затруднении, пытаясь понять, как получить идентификатор документа в классе данных, не устанавливая его в самом документе. Это возможно?

Ответ 1

Да, возможно получить идентификатор без его сохранения, используя DocumentSnapshot. Я попытаюсь привести здесь полные примеры.

Я создал общий класс модели для хранения 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;
    }
}

Затем вы расширяете его с помощью любой модели, не нужно ничего реализовать:

public class Client extends Model

Если у меня есть список клиентов, пытаясь запросить список, чтобы получить только клиентов с 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 {

                }
            }
        });

И если вы используете EventListener, вы также можете получить идентификатор, как показано ниже:

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

documentSnapshot.getId()) предоставит вам идентификатор документа в коллекции без сохранения идентификатора в документе.

Использование модели не позволит вам редактировать любую из ваших моделей и не забывайте использовать @IgnoreExtraProperties

Ответ 2

Я просто улучшил решение @iCediCe и добавил методы в DocumentSnapshot и 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>()
    }
}

И использование:

data class User(
    @get:Exclude
    override var id: String,
    ...
): HasId


val user = documentSnapshot.toObjectWithId<User>()
val users = querySnapshot.toObjectsWithId<User>()

Ответ 3

Вот как я решил проблему.

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

Я использую @get: Исключить идентификатор, чтобы убедиться, что идентификатор не отправлен в Firestore при сохранении, а затем, когда вы выбираете список клиентов:

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

Установка идентификатора нового объекта в идентификатор документа.

Ответ 4

Я решил это, создав метод расширения в QueryDocumentSnapshot следующим образом:

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

Теперь я могу отобразить это (что я считаю красивым и чистым):
 myModelWithId = it.toObjectWithId<MyModel>()

Для этого я создал простой интерфейс hasId, который требуется реализовать модели:

interface HasId{
    var id : String
}

@IgnoreExtraProperties
data class MyModel(
        @get:Exclude override var id : String    = "",
        val data                     : String    = "",

): Serializable, HasId

Ответ 5

Великолепные решения. Похоже, что Firestore предложил способ добавить аннотацию к вашей модели, чтобы решить эту проблему.

Вы могли бы сделать:

@DocumentId val documentId: String

И тогда Firestore сгенерирует это поле при вызове toObject или toObjects. Когда вы .set() документ в Firestore, это исключит это поле.

Ответ 6

Я просто извлекаю идентификатор из Firestore и устанавливаю его как поле на моем объекте, прежде чем создать его в 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)
    }