Мне интересно, можно ли получить несколько документов по списку идентификаторов за одну поездку туда и обратно (сетевой вызов) в Firestore.
Google Firestore - как получить документ по нескольким идентификаторам за одну поездку туда и обратно?
Ответы (13)
если вы находитесь в Node:
https://github.com/googleapis/nodejs-firestore/blob/master/dev/src/index.ts#L978
/**
* Retrieves multiple documents from Firestore.
*
* @param {...DocumentReference} documents - The document references
* to receive.
* @returns {Promise<Array.<DocumentSnapshot>>} A Promise that
* contains an array with the resulting document snapshots.
*
* @example
* let documentRef1 = firestore.doc('col/doc1');
* let documentRef2 = firestore.doc('col/doc2');
*
* firestore.getAll(documentRef1, documentRef2).then(docs => {
* console.log(`First document: ${JSON.stringify(docs[0])}`);
* console.log(`Second document: ${JSON.stringify(docs[1])}`);
* });
*/
Это специально для серверного SDK
ОБНОВЛЕНИЕ. Cloud Firestore [SDK на стороне клиента] теперь поддерживает запросы IN!
https://firebase.googleblog.com/2019/11/cloud-firestore-now-supports-in-queries.html
myCollection.where(firestore.FieldPath.documentId(), 'in', ["123","456","789"])
firestore.getAll.call(null, arrayOfReferences)
, если оператор распространения еще не поддерживается?
- person Julian Paolo Dayag; 12.02.2019
firebase.firestore.FieldPath.documentId()
, а не 'id'
- person Maddocks; 26.01.2020
List<String> lst;
, FirebaseFirestore.getInstance().collection("collection_name").whereIn(FieldPath.documentId(), lst).get().addOnCompleteListener(...);
- person Alaa M.; 04.07.2020
Они только что объявили об этой функции, https://firebase.googleblog.com/2019/11/cloud-firestore-now-supports-in-queries.html.
Теперь вы можете использовать такие запросы, но помните, что размер ввода не может быть больше 10.
userCollection.where('uid', 'in', ["1231","222","2131"])
db.collection('users').where(firebase.firestore.FieldPath.documentId(), 'in',["123","345","111"]).get()
- person jeadonara; 03.12.2019
firebase.firestore.FieldPath.documentId()
- person Ivan Chernykh; 22.12.2019
На практике вы бы использовали firestore.getAll как это
async getUsers({userIds}) {
const refs = userIds.map(id => this.firestore.doc(`users/${id}`))
const users = await this.firestore.getAll(...refs)
console.log(users.map(doc => doc.data()))
}
или с синтаксисом обещания
getUsers({userIds}) {
const refs = userIds.map(id => this.firestore.doc(`users/${id}`))
this.firestore.getAll(...refs).then(users => console.log(users.map(doc => doc.data())))
}
Вы можете использовать такую функцию:
function getById (path, ids) {
return firestore.getAll(
[].concat(ids).map(id => firestore.doc(`${path}/${id}`))
)
}
Его можно вызвать с одним идентификатором:
getById('collection', 'some_id')
или массив идентификаторов:
getById('collection', ['some_id', 'some_other_id'])
Нет, прямо сейчас нет возможности пакетировать несколько запросов на чтение с помощью Cloud Firestore SDK и, следовательно, нет способа гарантировать, что вы можете прочитать все данные сразу.
Однако, как сказал Франк ван Пуффелен в комментариях выше, это не означает, что получение 3 документов будет в 3 раза медленнее, чем получение одного документа. Перед тем, как прийти к заключению, лучше всего провести собственные измерения.
Если вы используете флаттер, вы можете сделать следующее:
Firestore.instance.collection('your collection name').where(FieldPath.documentId, whereIn:[list containing multiple document IDs]).getDocuments();
Это вернет Future, содержащий List<DocumentSnapshot>
, который вы можете повторять по своему усмотрению.
Конечно, лучший способ сделать это - реализовать фактический запрос Firestore в облачной функции? Тогда будет только один звонок в оба конца от клиента к Firebase, что, похоже, именно то, что вы просите.
В любом случае вы действительно хотите сохранить всю логику доступа к данным, как на этой стороне сервера.
Внутри, вероятно, будет такое же количество вызовов самой Firebase, но все они будут осуществляться через сверхбыстрые межсоединения Google, а не через внешнюю сеть, и в сочетании с конвейерной обработкой, которую объяснил Фрэнк ван Пуффелен, вы должны получить отличную производительность от этот подход.
Вот как вы могли бы сделать что-то подобное в Kotlin с Android SDK.
Может не обязательно быть в одном цикле, но он эффективно группирует результат и позволяет избежать множества вложенных обратных вызовов.
val userIds = listOf("123", "456")
val userTasks = userIds.map { firestore.document("users/${it!!}").get() }
Tasks.whenAllSuccess<DocumentSnapshot>(userTasks).addOnSuccessListener { documentList ->
//Do what you need to with the document list
}
Обратите внимание, что выборка определенных документов намного лучше, чем выборка всех документов и фильтрация результата. Это связано с тем, что Firestore взимает плату за набор результатов запроса.
Надеюсь, это поможет вам, мне это поможет.
getCartGoodsData(id) {
const goodsIDs: string[] = [];
return new Promise((resolve) => {
this.fs.firestore.collection(`users/${id}/cart`).get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
goodsIDs.push(doc.id);
});
const getDocs = goodsIDs.map((id: string) => {
return this.fs.firestore.collection('goods').doc(id).get()
.then((docData) => {
return docData.data();
});
});
Promise.all(getDocs).then((goods: Goods[]) => {
resolve(goods);
});
});
});
}
Для тех, кто хочет сделать это с помощью Angular, вот пример:
Сначала необходим импорт некоторых библиотек: (должен быть предварительно установлен)
import * as firebase from 'firebase/app'
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore'
Некоторая конфигурация для коллекции:
yourCollection: AngularFirestoreCollection;
constructor(
private _db : AngularFirestore,
) {
// this is your firestore collection
this.yourCollection = this._db.collection('collectionName');
}
Вот метод для выполнения запроса: ('products_id' - это массив идентификаторов)
getProducts(products_ids) {
var queryId = firebase.firestore.FieldPath.documentId();
this.yourCollection.ref.where(queryId, 'in', products_ids).get()
.then(({ docs }) => {
console.log(docs.map(doc => doc.data()))
})
}
В настоящее время это невозможно в Firestore. Я не понимаю, почему ответ Александра принят, решение, которое он предлагает, просто возвращает все документы из коллекции «пользователей».
В зависимости от того, что вам нужно сделать, вам следует подумать о дублировании соответствующих данных, которые вам нужно отобразить, и запрашивать полный документ только при необходимости.
Да, это возможно. Пример в .NET SDK для Firestore:
/*List of document references, for example:
FirestoreDb.Collection(ROOT_LEVEL_COLLECTION).Document(DOCUMENT_ID);*/
List<DocumentReference> docRefList = YOUR_DOCUMENT_REFERENCE_LIST;
// Required fields of documents, not necessary while fetching entire documents
FieldMask fieldMask = new FieldMask(FIELD-1, FIELD-2, ...);
// With field mask
List<DocumentSnapshot> documentSnapshotsMasked = await FirestoreDb.GetAllSnapshotsAsync(docRefList, fieldMask);
// Without field mask
List<DocumentSnapshot>documentSnapshots = await FirestoreDb.GetAllSnapshotsAsync(docRefList);
Документация в .NET:
Лучшее, что вы можете сделать, - это не использовать Promise.all
в качестве клиента, а затем дождаться .all
чтения, прежде чем продолжить.
Итерируйте чтения и позвольте им разрешиться самостоятельно. На стороне клиента это, вероятно, сводится к тому, что в пользовательском интерфейсе несколько изображений загрузчика выполнения разрешаются в значения независимо. Однако это лучше, чем замораживание всего клиента до тех пор, пока .all
чтение не разрешится.
Следовательно, немедленно выгрузите все синхронные результаты в представление, а затем позвольте асинхронным результатам поступать по мере их разрешения, индивидуально. Это может показаться мелким различием, но если у вашего клиента плохое подключение к Интернету (как у меня сейчас в этом кафе), замораживание всего клиентского опыта на несколько секунд, скорее всего, приведет к тому, что это приложение - отстой.
a
,b
,c
, чтобы что-то сделать. Я запрашиваю все три параллельно в отдельных запросах.a
занимает 100 мс,b
занимает 150 мс, аc
занимает 3000 мс. В результате мне нужно подождать 3000 мс, чтобы выполнить задачу. Их будетmax
. Это будет более рискованно, когда количество документов, которые нужно получить, велико. В зависимости от статуса сети, я думаю, это может стать проблемой. - person Joon   schedule 13.10.2017SELECT * FROM docs WHERE id IN (a,b,c)
не займет одинаковое количество времени? Я не вижу разницы, так как соединение устанавливается один раз, а остальное передается по конвейеру. Время (после первоначального установления соединения) - это время загрузки всех документов + 1 поездка туда и обратно, одинаковая для обоих подходов. Если он ведет себя по-другому для вас, не могли бы вы поделиться образцом (как в моем связанном вопросе)? - person Frank van Puffelen   schedule 13.10.2017n
сетевые вызовы параллельно дляn
элементов, а не только один сетевой вызов, который выполняетn
запросы одновременно. - person Joon   schedule 14.10.2017N*(read_time+transfer_time+latency)
. Если они конвейерные, это ближе кN*(transfer_time)+read_time+latency
. - person Frank van Puffelen   schedule 14.10.2017