Еще раз опираясь на @ Дуг ответ , Firestore - это проиндексированная база данных документов. Чтобы запросить данные, запрос должен быть выполнен к индексу за один раз, чтобы поддерживать производительность запросов в соответствии с тем, как устроена база данных.
Firebase не будет индексировать поля, которые являются строками по умолчанию, потому что это не ' Это эффективно, и это довольно обременительная операция в масштабе. Часто лучшим вариантом является другой подход.
Возьмем, к примеру, следующую функцию, которая разбивает входную строку на доступные для поиска части, которые затем могут быть добавлены в индекс. По мере увеличения длины входной строки количество подстрок, содержащихся внутри, быстро увеличивается.
function shatter(str, minLength = 1) {
let parts = [str]; // always have full string
let i, subLength = minLength;
let strLength = str.length;
while (subLength < strLength) {
for (i = 0; i < (strLength - subLength + 1); i++) {
parts.push(str.substring(i, i + subLength));
}
subLength++;
}
return parts;
}
Вот интерактивный фрагмент, демонстрирующий это:
function shatter(str, minLength = 1) {
let parts = [str]; // always have full string
let i, subLength = minLength;
let strLength = str.length;
while (subLength < strLength) {
for (i = 0; i < (strLength - subLength + 1); i++) {
parts.push(str.substring(i, i + subLength));
}
subLength++;
}
return parts;
}
let str = prompt('Please type out a string to shatter:', 'This is a test string');
let partsOfMin1 = shatter(str, 1);
console.log('Shattering into pieces of minimum length 1 gives:', partsOfMin1);
let partsOfMin3 = shatter(str, 3);
console.log('Shattering into pieces of minimum length 3 gives:', partsOfMin3);
let partsOfMin5 = shatter(str, 5);
console.log('Shattering into pieces of minimum length 5 gives:', partsOfMin5);
alert('The string "' + str + '" can be shattered into as many as ' + partsOfMin1.length + ' pieces.\r\n\r\nThis can be reduced to only ' + partsOfMin3.length + ' with a minimum length of 3 or ' + partsOfMin5.length + ' with a minimum length of 5.');
Однако, используя эту вышеупомянутую функцию, мы можем использовать ее так, чтобы она сохраняла разбитые фрагменты в Firestore на /substringIndex/todos/workDesc
со ссылкой на документ, содержащий строку.
const firebase = require('firebase');
firebase.initializeApp(/* config here */);
const arrayUnion = firebase.firestore.FieldValue.arrayUnion;
const TODOS_COL_REF = firebase.firestore().collection('todos');
const SUBSTRING_INDEX_COL_REF = firebase.firestore().collection('substringIndex');
// splits given string into segments ranging from the given minimum length up to the full length
function shatter(str, minLength = 1) {
let parts = [str];
let i, subLength = minLength;
let strLength = str.length;
while (subLength < strLength) {
for (i = 0; i < (strLength - subLength + 1); i++) {
parts.push(str.substring(i, i + subLength));
}
subLength++;
}
return parts;
}
// upload data
const testData = {
workDesc: 'this is a prolonged string to break code',
assignDate: firebase.firestore.Timestamp.fromDate(new Date()),
assignTo: 'Ddy1QVOAO6SIvB8LfAE8Z0Adj4H3',
followers: ['Ddy1QVOAO6SIvB8LfAE8Z0Adj4H3'],
searchArray: ['v1', 'v2']
}
const todoDocRef = TODOS_COL_REF.doc();
const todoId = todoDocRef.id;
todoDocRef.set(testData)
.then(() => console.log('Uploaded test data!'))
.catch((err) => console.error('Failed to test data!', err));
// Note: in this example, I'm not waiting for the above promise to finish
// Normally, you would integrate it into the batched write operations below
// index each desired string field
const indexDocRef = SUBSTRING_INDEX_COL_REF.doc('todos');
const indexedFields = ["workDesc"];
const indexEntryMinLength = 3;
const indexUpdatePromises = indexedFields.map((fieldName) => {
const indexColRef = indexDocRef.collection(fieldName);
const fieldValue = testData[fieldName];
if (typeof fieldValue !== 'string') return Promise.resolve(undefined); // skip non-string values
const parts = shatter(fieldValue, indexEntryMinLength);
console.log('INFO: Consuming ' + (parts.length * 2) + ' write operations to index ' + fieldName);
// Each batched write can handle up to 500 operations, each arrayUnion counts as two
const partsBatches = [];
if (parts.length > 250) {
for (let i = 0; i < parts.length; i += 250) {
partsBatches.push(parts.slice(i, i + 250));
}
} else {
partsBatches.push(parts);
}
const batchCommitPromises = partsBatches
.map((partsInBatch) => {
const batch = firebase.firestore().batch();
partsInBatch.forEach((part) => {
batch.set(indexColRef.doc(part), {ids: arrayUnion(todoId)}, { merge: true })
})
return batch.commit();
});
return Promise.all(batchCommitPromises);
})
Promise.all(indexUpdatePromises)
.then(() => console.log('Uploaded substring index!'))
.catch((err) => console.error('Failed to upload index!', err));
Затем, когда вы хотите найти все документы, содержащие "impl"
, вы будете использовать следующее для получения массива совпадающих идентификаторов документов:
firebase.firestore().doc('substringIndex/todos/workDesc/impl').get()
.then(snap => snap.get('ids'))
.then(console.log, console.error)
Пока работает приведенный выше код, когда вы обновите индекс, вы достаточно быстро достигнете своих пределов чтения / записи, и вы, скорее всего, столкнетесь с проблемами параллелизма. Я также считаю это fr agile в том, что не-Engli sh символы и знаки препинания также сбивают его с толку - это включено только в качестве демонстрации. Именно из-за этих проблем в соответствующей документации Firebase рекомендуется использовать стороннюю службу поиска, такую как Algolia , для полнотекстового поиска.
TL: DR;
Лучшее решение - иметь удобочитаемую форму ваших данных ("sharu implementation quick response needed"
) и индексируемую форму ваших данных ({implementation: true, urgent: true, pending: true}
) в соответствии с @ Luis в их ответ .