Как я могу объединить два запроса Firestore, используя rxfire и rxjs (запрос OR)

Цель проста: объединить два запроса FireStore, используя rxjs, rxfireи rnfirebase реагировать родную библиотеку.

Я прочитал несколько уроков 1, 2 по объединению запросов, но все они терпят неудачу с разными ошибками.

//Simple test for collectionData
import { collectionData } from 'rxfire/firestore';

this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
collectionData(this.myQuery, 'id').subscribe(docs => console.log(docs))
//Fails with error: this._next is not a function.

С другой стороны,

this.publicQuery = this.props.docRef.collection('messages').where('public', '==', true) 
this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
const myQuery$ = new Rx.Subject();
const publicQuery$ = new Rx.Subject();
this.myQuery.onSnapshot((querySnapshot) => {
    myQuery$.next(querySnapshot.docs.map(d => d.data()  ));
});
this.publicQuery.onSnapshot((querySnapshot) => {
    publicQuery$.next(querySnapshot.docs.map(d => d.data()  ));
});
const orQuery$ = combineLatest(this.myQuery, this.publicQuery).switchMap((docs) => {
    var [one, two] = docs;
    var combined = one.concat(two);
    return Rx.Observable.of(combined);
})
orQuery$.subscribe((result) => {
    console.log('>>>> ', result)
})
//TypeError: undefined is not a function (near ...switchMap)

Как я могу успешно присоединиться к двум запросам пожарного магазина (ИЛИ)?

1 ответ

Решение

Вы уже очень близки к решению. Давайте пройдемся по шагам шаг за шагом.

Во-первых, не нужно создавать Subject просто чтобы превратить ваш результат из onSnapshot, Вместо этого:

this.myQuery.onSnapshot((querySnapshot) => {
    myQuery$.next(querySnapshot.docs.map(d => d.data()))
});

Мы можем достичь того же, используя "операторы преобразования с конвейером":

const myQuery$ = this.myQuery.onSnapshot.pipe(
    map(querySnapshot => querySnapshot.docs.map(d => d.data()))
);

То же самое относится и к другому запросу:

const publicQuery$ = this.publicQuery.onSnapshot.pipe(
    map(querySnapshot => querySnapshot.docs.map(d => d.data())
);

Во-вторых, чтобы присоединиться к этим двум запросам, combineLatest действительно правильная функция создания.

Однако ваша ошибка может быть вызвана тем, что вы используете более новую версию RxJS, которая больше не поддерживает "текущие" операторы ( официально называемые "операторы патчей"). Они были заменены "трубными операторами" начиная с RxJS 6 и далее. В качестве примера, myObs$.map(...) стал myObs$.pipe(map(...)), В руководствах, вероятно, используется более старая версия RxJS, где первая все еще возможна.

Кроме того, не должно быть необходимости использовать switchMap если внутренняя наблюдаемая это просто of оператор. В этом случае достаточно использовать map оператор, который будет вести себя одинаково.

Использование нового синтаксиса RxJS 6+ вместе с map, комбинация будет выглядеть так:

const orQuery$ = combineLatest(myQuery$, publicQuery$).pipe(
    map(([one, two]) => one.concat(two))
)

Остальная часть вашего кода должна быть правильной.

Примечание: имейте в виду, что эквивалент вашего кода в SQL UNION (не JOIN). Чтобы JOIN программно вам нужно будет объединить каждый объект набора результатов A с каждым объектом набора результатов B и создать объединенный объект для каждой пары. Такая функция для безключевой OUTER JOIN будет выглядеть так (помещается в вашем map труба):

one.map(a => 
   two.map(b => Object.assign({}, a, b)))
.reduce((p, c) => p.concat(c), [])

Если вы хотите иметь UNION без повторяющихся объектов, конкатенируйте только те элементы из two которые не имеют соответствующего первичного ключа в списке one, Это будет ваша функция отображения:

one.concat(two.filter(twoItem => !one.some(oneItem => oneItem.id == twoItem.id)))

ДЕМО: Полная рабочая демонстрация с приведенным выше кодом и смоделированным FireStore может быть найдена здесь:

https://stackblitz.com/edit/rxjs-mefynu?devtoolsheight=60

Другие вопросы по тегам