Документы запросов Firestore с аналогичным массивом (Node.js / Admin SDK)

Aug 16 2020

Представьте, что у вас есть этот массив

[10.3, 14, 12.4, 3.5]

и в вашей БД у вас также есть два документа с этими массивами:

First document -> [10, 13.1, 0, -10]
Second document -> [0, 0, 0, 0]

Теперь представьте, что у вас есть +1000000 документов с одинаковыми двумя массивами (чередующимися между собой) ... Есть ли способ получить все документы, которые имеют аналогичные значения, как ваш текущий массив?

Я имею в виду, что-то вроде этого:

   ...
   .where("array", "isSimilar", yourArray)
   .get()

Чтобы получить все документы, в которых есть массив:

[10, 13.1, 0, -10]

Или единственный способ - загрузить каждый документ, что может быть очень медленным, а затем выполнить итерацию и найти наиболее похожие? Я думаю, это может быть действительно интересно, когда мы говорим о «схожих позициях», «неделях, в течение которых пользователь похудел примерно так же», ...

Спасибо.

Ответы

3 JayCodist Aug 16 2020 at 09:20

На данный момент firestore не поддерживает такие запросы. Поэтому я предлагаю вам обновить свою структуру, включив в нее поле String для сравнения массивов. Итак, каждый документ будет выглядеть так:

{
   array: [12, 11, 8, 9],
   arrayStr: "12,11,8,9",
   ...
}

Вы можете получить эту структуру, просто вызвав doc.array.join(",")все существующие документы и сохранив значение в документе.

Затем можно будет провести сравнение с запросом firestore, например:

const arrToCompare = [12, 11, 8, 9];
const snapshot = await firestore().collection(collection).where("arrayStr", "==", arrToCompare.join(",")).get();
...

ОБНОВЛЕНИЕ: для сравнения по сходству, а не по равенству, возможный подход - применить логику «подобия» к arrayStrполю во время создания. Например, если вы хотите, чтобы допускались различия менее 0,5, вы можете использовать Math.round()их для элементов массива перед сохранением в виде строки. Вот так:

const array = [12.2, 10.7, 8.111, 9.0];
const arrayStr = array.map(num => Math.round(num)).join(","); //"12,11,8,9"

Тогда вы будете запрашивать вот так:

const arrToCompare = [12, 11, 8, 9];
const snapshot = await firestore().collection(collection).where("arrayStr", "==", arrToCompare.map(num => Math.round(num)).join(",")).get();
// Results would include arrays like [12.2, 10.7, 8.111, 9.0]
...

Конечно, вы можете изменить переданный аргумент Math.round(), чтобы увеличить или уменьшить уровень допуска для сравнений.