Я столкнулся с ограничением firebase «IN», равным 10. Хотя обходное решение уже было дано здесь:
Есть ли обходной путь для ограничения «IN» запросов Firebase до 10?
Ни одно из решений в этом потоке не работает с прослушивателем «onSnapshot». Для моего варианта использования (Vue 3) у меня есть составной/функциональный вызов I, который запрашивает передачу firebase в массив, который может иметь до 100 значений идентификатора документа, и возвращает объект, как показано ниже.
Это возможно?
import { ref, watchEffect } from 'vue'
import { db } from '@/firebase/config'
import { collection, onSnapshot, query, where, documentId } from 'firebase/firestore'
const getUsersList = (idList) => {
// idList is an array of document ID's
const documents = ref(null)
let collectionRef = collection(db, 'users')
collectionRef = query(collectionRef, where(documentId(), 'in', idList))
// this fails if I pass in more than 10 elements in the array
const unsub = onSnapshot(collectionRef, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
})
// update values
documents.value = results
})
watchEffect((onInvalidate) => {
onInvalidate(() => unsub())
})
return { documents }
}
export default getCollectionRt
Я был бы согласен с этим, но не могли бы вы показать пример с несколькими слушателями, которые возвращают один объект, содержащий результаты всех из них, как было задано?
Зачем вообще нужны слушатели, если нужен только один результат? Если это все, что вам нужно, просто get()
каждый документ отдельно.
Мне нужен один результат, который обновляется, если базовые данные обновляются, следовательно, используется onSnapshot и тема этого поста.
Потребуется много работы, чтобы собрать это вместе, независимо от того, используете ли вы несколько «входящих» запросов или отдельные прослушиватели документов. Не будет единого прослушивателя запросов, который может делать все это.
Правильно, «... массив, который может иметь до 100 значений идентификатора документа», я бы предположил, что потребуется до 10 слушателей.
Вам нужно будет инициализировать несколько слушателей, то есть такое же количество запросов, но с onSnapshot()
(может быть лучше, чем настраивать прослушиватель для каждого отдельного документа). Пытаться:
import { ref } from 'vue';
import { collection, query, where, documentId, onSnapshot } from 'firebase/firestore'
const users = ref({})
const dataLoaded = ref(false)
const addFirestoreListeners = async () => {
const idList = [];
for (let i = 0; i < idList.length; i += 10) {
const items = idList.slice(i, i + 10)
const q = query(collection(db, 'users'), where(documentId(), 'in', items))
onSnapshot(q, (snapshot) => {
if (dataLoaded.value) {
snapshot.docChanges().forEach((change) => {
if (change.type === 'added' || change.type === 'modified') {
users.value[change.doc.id] = change.doc.data()
} else (change.type === 'removed') {
users.value[change.doc.id] = null
}
})
} else {
snapshot.forEach((doc) => {
users.value[doc.id] = doc.data()
})
dataLoaded.value = true
}
})
}
}
Флаг dataLoaded
проверяет, получили ли прослушиватели данные в первый раз или получили обновление. Я использую карту, где ключом является идентификатор документа, поэтому его можно легко удалить, но я заменяю его на массив или любую другую необходимую структуру.
@BGMX мой код продолжает обновлять один и тот же объект каждый раз, когда слушатель получает обновление. Вы можете использовать Object.values(users)
для отображения данных в пользовательском интерфейсе. Что Вы ищете?
Будет ли субъект работать так же, как оригинал, даже с несколькими слушателями, или соединение сохранится?
Поскольку ни один ответ здесь не дал полного ответа на вопрос, в итоге я заплатил фрилансеру, чтобы он посмотрел, и вот что они придумали. Кажется, что у решения есть случайная проблема, которую я пытаюсь решить, когда основные изменения изменятся, одна из записей исчезнет. Он работает, находится в рамках первоначального вопроса и, похоже, решил проблему.
import { ref, watchEffect } from 'vue'
import { db } from '@/firebase/config'
import { collection, onSnapshot, query, where, documentId } from 'firebase/firestore'
const getUserList = (idList) => {
console.info('idList', idList)
let documents = ref(null)
let collectionRef = collection(db, 'users')
let unsub, unsubes = [], resultsList = [{}];
for (let i = 0; i < idList.length; i += 10) {
let idList1 = idList.slice(i, i + 10); //console.info(idList1);
let collectionRef1 = query(collectionRef, where(documentId(), 'in', idList1))
unsub = onSnapshot(collectionRef1, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
})
resultsList.splice(resultsList.length, 0, ...results);
console.info('results', results)
documents.value = results
})
unsubes.push(unsub);
}
watchEffect((onInvalidate) => {
onInvalidate(() =>{ unsubes.forEach(unsub => { unsub(); console.info("unsut", unsub); }) });
})
Promise.all(unsubes);
resultsList.shift(0);
console.info("docu", documents.value);
return { documents };
}
export default getUserList
Вам действительно следует вообще отказаться от использования «in» и просто настроить прослушиватели для каждого отдельного документа. При этом нет реальных затрат или снижения производительности, и это упростит ваш код. Если у вас есть какие-то особые требования к использованию «in», вы должны указать это в своем вопросе. В противном случае ваш вопрос на самом деле ничем не отличается от оригинала, на который вы ссылались, - это точно такая же проблема с теми же решениями, которые используют прослушиватели вместо прямого потребления результатов.