Я думал, что читал, что вы можете запрашивать вложенные коллекции с новым Firebase Firestore, но я не вижу никаких примеров. Например, мой Firestore настроен следующим образом:

  • Танцы [сборник]
    • танцевальное имя
    • Песни [сборник]
      • название песни

Как я могу запросить «Найти все танцы, где songName == 'X'»

Ответы (11)

Что, если вы храните песни как объект, а не как коллекцию? Каждый танец как, с песнями в виде поля: введите Object (не сборник)

{
  danceName: "My Dance",
  songs: {
    "aNameOfASong": true,
    "aNameOfAnotherSong": true,
  }
}

, тогда вы можете запросить все танцы с aNameOfASong:

db.collection ('Танцы')
  .where ('песни.aNameOfASong', '==', истина)
  .получить()
  .then (функция (querySnapshot) {
    querySnapshot.forEach (function (doc) {
      console.log (doc.id, "=>", doc.data ());
    });
   })
   .catch (функция (ошибка) {
     console.log («Ошибка при получении документов:», ошибка);
    });

Может быть лучше использовать плоскую структуру данных.
В документации указаны плюсы и минусы различных структур данных на этой странице.

Конкретно об ограничениях структур с подколлекциями:

Невозможно легко удалить вложенные коллекции или выполнять составные запросы по вложенным коллекциям.

В отличие от предполагаемых преимуществ плоской структуры данных:

Коллекции корневого уровня предлагают максимальную гибкость и масштабируемость, наряду с мощными запросами внутри каждой коллекции.

НОВОЕ ОБНОВЛЕНИЕ 8 июля 2019 г .:

db.collectionGroup('Songs')
  .where('songName', isEqualTo:'X')
  .get()

ОБНОВЛЕНИЕ 2019

Firestore выпустил запросы группы сбора. См. Ответ Гила выше или официальную Документацию по запросу группы сбора


Предыдущий ответ

Как заявил Гил Гилберт, похоже, что запросы группы сбора в настоящее время находятся в разработке. В то же время, вероятно, лучше использовать коллекции корневого уровня и просто связать эти коллекции, используя UID документа.

Для тех, кто еще не знает, у Джеффа Делани есть несколько невероятных руководств и ресурсов для всех, кто работает с Firebase (и Angular) на AngularFirebase.

Моделирование реляционных данных Firestore NoSQL - Здесь он раскрывает основы структурирования БД NoSQL и Firestore

Расширенное моделирование данных с помощью Firestore на примере - это более сложные методы, о которых стоит помнить. Отличное чтение для тех, кто хочет поднять свои навыки Firestore на новый уровень

Я работаю с Observables здесь и с оболочкой AngularFire, но вот как мне это удалось.

Это какое-то безумие, я все еще изучаю наблюдаемые и, возможно, перестарался. Но это было хорошее упражнение.

Некоторое пояснение (не эксперт RxJS):

  • songId $ - это наблюдаемое, которое будет выдавать идентификаторы
  • dance $ - это наблюдаемое, которое считывает этот идентификатор и затем получает только первое значение.
  • затем он запрашивает collectionGroup всех песен, чтобы найти все его экземпляры.
  • В зависимости от экземпляров, которые он переходит к родительским Танцам и получает их идентификаторы.
  • Теперь, когда у нас есть все идентификаторы Dance, нам нужно запросить их, чтобы получить их данные. Но я хотел, чтобы он работал хорошо, поэтому вместо того, чтобы запрашивать один за другим, я группирую их в группы по 10 (максимальный angular потребуется для запроса в.
  • У нас осталось N корзин, и нам нужно выполнить N запросов в firestore, чтобы получить их значения.
  • после того, как мы выполним запросы в firestore, нам все равно нужно будет анализировать данные из него.
  • и, наконец, мы можем объединить все результаты запроса, чтобы получить единый массив со всеми Танцами в нем.
тип Song = {id: string, name: string};
введите Dance = {id: строка, имя: строка, песни: Song []};

const songId $: Observable  = new Observable ();
const dance $ = songId $ .pipe (
  take (1), // берем только 1 название песни
  switchMap (v =>
    // Запрос через collectionGroup, чтобы получить все экземпляры.
    this.db.collectionGroup ('песни', ref =>
      ref.where ('идентификатор', '==', v.id)). get ()
  ),
  switchMap (v => {
    // сопоставляем песню с родительским Dance, возвращаем идентификаторы Dance
    const obs: строка [] = [];
    v.docs.forEach (docRef => {
      // Мы вызываем родительский элемент дважды, чтобы перейти от doc-> collection-> doc
      obs.push (docRef.ref.parent.parent.id);
    });
    // Поскольку мы возвращаем здесь массив, этот испускаемый элемент становится N
    вернуть обс;
  }),
  // Firebase IN поддерживает до 10 значений, поэтому мы разделяем данные для запроса танцев
  bufferCount (10),
  mergeMap (v => {// запрашиваем все разделы параллельно
    return this.db.collection ('танцы', ref => {
      вернуть ref.where (firebase.firestore.FieldPath.documentId (), 'in', v);
    }).получить();
  }),
  switchMap (v => {
    // Почти готово, осталось извлечь данные из QuerySnapshots
    const obs: Dance [] = [];
    v.docs.forEach (docRef => {
      obs.push ({
        ... docRef.data (),
        id: docRef.id
      } как танец);
    });
    возврат (набл.);
  }),
  // И, наконец, мы сокращаем полученные документы в единый массив.
  reduce ((acc, value) => acc.concat (value), []),
);
const parentDances = ждать танец $ .toPromise ();

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

var songs = []    
db.collection('Dances')
      .where('songs.aNameOfASong', '==', true)
      .get()
      .then(function(querySnapshot) {
        var songLength = querySnapshot.size
        var i=0;
        querySnapshot.forEach(function(doc) {
           songs.push(doc.data())
           i ++;
           if(songLength===i){
                console.log(songs
           }
          console.log(doc.id, " => ", doc.data());
        });
       })
       .catch(function(error) {
         console.log("Error getting documents: ", error);
        });

Ограничения запроса

Cloud Firestore не поддерживает следующие типы запросов:

  1. Запросы с фильтрами диапазонов по разным полям.

  2. Отдельные запросы к нескольким коллекциям или вложенным коллекциям. Каждый запрос выполняется для одного набора документов. Для большего информацию о том, как ваша структура данных влияет на ваши запросы, см. Выберите структуру данных.

  3. Логические запросы ИЛИ. В этом случае вы должны создать отдельный запрос для каждого условия ИЛИ и объединить результаты запроса в своем приложении.

  4. Запросы с предложением! =. В этом случае следует разделить запрос на запрос «больше» и «меньше». Например, хотя предложение запроса, где ("age", "! =", "30") не поддерживается, вы можете получить тот же набор результатов, объединив два запроса, один с предложением где ("age", "<", "30") и один с предложением where ("age", ">", 30).

Вы всегда можете искать так: -

 this.key $ = новый BehaviorSubject (null);

    вернуть this.key $ .switchMap (key =>
      this.angFirestore
        .collection ("танцы"). doc ("danceName"). collections ("песни", ref =>
          ссылка
            .where ("songName", "==", X)
        )
        .snapshotChanges ()
        .map (actions => {
          if (actions.toString ()) {
            return actions.map (a => {
              const data = a.payload.doc.data () as Dance;
              const id = a.payload.doc.id;
              return {id, ... data};
            });
          } еще {
            вернуть ложь;
          }
        })
    );

Я нашел решение. Пожалуйста, проверьте это.

var museums = Firestore.instance.collectionGroup('Songs').where('songName', isEqualTo: "X");
        museums.getDocuments().then((querySnapshot) {
            setState(() {
              songCounts= querySnapshot.documents.length.toString();
            });
        });

Затем вы можете увидеть вкладки «Данные», «Правила», «Индексы», «Использование» в облачном хранилище на console.firebase.google.com. Наконец, вы должны установить индексы на вкладке индексов.enter image description here

Введите сюда ID коллекции и какое-нибудь значение поля. Затем выберите опцию группы сбора. Наслаждайся этим. Спасибо

Обновление 2019-05-07

Сегодня мы выпустили запросы групп сбора, которые позволяют выполнять запросы по вложенным коллекциям.

Так, например, в веб-SDK:

db.collectionGroup('Songs')
  .where('songName', '==', 'X')
  .get()

Это будет соответствовать документам в любой коллекции, где последняя часть пути коллекции - «Песни».

Ваш первоначальный вопрос заключался в поиске танцев, где songName == 'X', и это все еще невозможно напрямую, однако для каждой совпавшей песни вы можете загрузить ее родительскую.

Оригинальный ответ

Это функция, которой пока нет. Это называется «запрос группы сбора» и позволяет запрашивать все песни, независимо от того, в каком танце они содержатся. Это то, что мы намерены поддерживать, но пока не сообщаем конкретных сроков, когда это произойдет.

Альтернативная структура на этом этапе состоит в том, чтобы сделать песни сборником верхнего уровня и сделать, какой танец песня является частью свойства песни.

ОБНОВЛЕНИЕ Теперь Firestore поддерживает array-contains

Имея эти документы

    {danceName: 'Danca name 1', songName: ['Title1','Title2']}
    {danceName: 'Danca name 2', songName: ['Title3']}

так сделать

collection("Dances")
    .where("songName", "array-contains", "Title1")
    .get()...

@Nelson.b.austin Поскольку в firestore этого еще нет, я предлагаю вам иметь плоскую структуру, то есть:

Dances = {
    danceName: 'Dance name 1',
    songName_Title1: true,
    songName_Title2: true,
    songName_Title3: false
}

Having it in that way, you can get it done:

var songTitle = 'Title1';
var dances = db.collection("Dances");
var query = dances.where("songName_"+songTitle, "==", true);

Надеюсь, это поможет.

2022 WebDevInsider