159

我以为我读到您可以使用新的 Firebase Firestore 查询子集合,但我没有看到任何示例。例如,我通过以下方式设置 Firestore:

  • 舞蹈[收藏]
    • 舞名
    • 歌曲[收藏]
      • 歌名

我如何能够查询“查找所有歌曲名称 == 'X' 的舞蹈”

4

11 回答 11

182

2019-05-07 更新

今天我们发布了集合组查询,这些允许您跨子集合进行查询。

因此,例如在 Web SDK 中:

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

这将匹配集合路径的最后一部分是“歌曲”的任何集合中的文档。

您最初的问题是关于查找歌曲名称 == 'X' 的舞蹈,但这仍然不可能直接进行,但是,对于匹配的每首歌曲,您都可以加载其父级。

原始答案

这是一个尚不存在的功能。它被称为“集合组查询”,允许您查询所有歌曲,而不管它们包含哪些舞蹈。这是我们打算支持的东西,但没有具体的时间表来说明它何时到来。

此时的替代结构是将歌曲制作为顶级集合,并使歌曲的哪个舞蹈成为歌曲属性的一部分。

于 2017-10-04T19:57:07.930 回答
24

更新 现在 Firestore 支持数组包含

拥有这些文件

    {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
}

以这种方式,您可以完成它:

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

我希望这有帮助。

于 2017-10-05T12:33:19.413 回答
18

2019 年更新

Firestore 已发布集合组查询。请参阅上面 Gil 的回答或官方Collection Group Query Documentation


上一个答案

正如 Gil Gilbert 所说,似乎集合组查询目前正在进行中。同时,最好使用根级别的集合,并使用文档 UID 在这些集合之间进行链接。

对于那些还不知道的人,Jeff Delaney 为在AngularFirebase上使用 Firebase(和 Angular)的任何人提供了一些令人难以置信的指南和资源。

Firestore NoSQL 关系数据建模- 在这里,他分解了 NoSQL 和 Firestore 数据库结构的基础知识

通过示例使用 Firestore 进行高级数据建模- 这些是更高级的技术,请牢记在心。对于那些想要将他们的 Firestore 技能提升到一个新水平的人来说,这是一本很好的读物

于 2018-06-27T07:29:41.887 回答
18

如果您将歌曲存储为对象而不是集合会怎样?每个舞蹈都作为,以歌曲作为字段:类型对象(不是集合)

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

然后您可以使用 aNameOfASong 查询所有舞蹈:

db.collection('Dances')
  .where('songs.aNameOfASong', '==', true)
  .get()
  .then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
      console.log(doc.id, " => ", doc.data());
    });
   })
   .catch(function(error) {
     console.log("Error getting documents: ", error);
    });
于 2017-10-25T20:51:52.007 回答
10

2019 年 7 月 8 日新更新:

db.collectionGroup('Songs')
  .where('songName', isEqualTo:'X')
  .get()
于 2019-08-07T03:47:14.193 回答
4

你总是可以这样搜索:-

    this.key$ = new BehaviorSubject(null);

    return this.key$.switchMap(key =>
      this.angFirestore
        .collection("dances").doc("danceName").collections("songs", ref =>
          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 };
            });
          } else {
            return false;
          }
        })
    );
于 2018-05-19T10:39:48.603 回答
4

我找到了解决方案。请检查这个。

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

然后您可以从 console.firebase.google.com 在您的云 Firestore 中查看数据、规则、索引、使用情况选项卡。最后,您应该在索引选项卡中设置索引。在此处输入图像描述

在此处填写集合 ID 和一些字段值。然后选择集合组选项。好好享受。谢谢

于 2019-11-11T11:50:19.513 回答
3

查询限制

Cloud Firestore 不支持以下类型的查询:

  1. 在不同字段上使用范围过滤器的查询。

  2. 跨多个集合或子集合的单个查询。每个查询都针对单个文档集合运行。有关数据结构如何影响查询的更多信息,请参阅 选择数据结构

  3. 逻辑或查询。在这种情况下,您应该为每个 OR 条件创建一个单独的查询,并在您的应用程序中合并查询结果。

  4. 带有 != 子句的查询。在这种情况下,您应该将查询拆分为大于查询和小于查询。例如,虽然不支持查询子句 where("age", "!=", "30"),但是可以通过组合两个查询得到相同的结果集,其中一个是 where("age", "< ", "30") 和一个带有 where("age", ">", 30) 的子句。

于 2018-10-26T02:17:56.537 回答
2

我在这里使用 Observables 和AngularFire包装器,但这就是我设法做到这一点的方法。

这有点疯狂,我还在学习 observables,我可能做得过火了。但这是一个很好的练习。

一些解释(不是 RxJS 专家):

  • songId$ 是一个会发出 id 的 observable
  • dance$ 是一个 observable,它读取该 id,然后只获取第一个值。
  • 然后它查询所有歌曲的 collectionGroup 以找到它的所有实例。
  • 根据实例,它遍历父 Dances 并获取它们的 id。
  • 现在我们有了所有的 Dance id,我们需要查询它们以获取它们的数据。但我希望它表现良好,所以我不是一个一个地查询,而是将它们分成 10 个桶(最大角度将用于in查询。
  • 我们最终得到了 N 个存储桶,需要在 firestore 上进行 N 次查询以获取它们的值。
  • 一旦我们在 firestore 上进行查询,我们仍然需要实际解析其中的数据。
  • 最后我们可以合并所有的查询结果,得到一个包含所有舞蹈的数组。
type Song = {id: string, name: string};
type Dance = {id: string, name: string, songs: Song[]};

const songId$: Observable<Song> = new Observable();
const dance$ = songId$.pipe(
  take(1), // Only take 1 song name
  switchMap( v =>
    // Query across collectionGroup to get all instances.
    this.db.collectionGroup('songs', ref =>
      ref.where('id', '==', v.id)).get()
  ),
  switchMap( v => {
    // map the Song to the parent Dance, return the Dance ids
    const obs: string[] = [];
    v.docs.forEach(docRef => {
      // We invoke parent twice to go from doc->collection->doc
      obs.push(docRef.ref.parent.parent.id);
    });
    // Because we return an array here this one emit becomes N
    return obs;
  }),
  // Firebase IN support up to 10 values so we partition the data to query the Dances
  bufferCount(10),
  mergeMap( v => { // query every partition in parallel
    return this.db.collection('dances', ref => {
      return ref.where( firebase.firestore.FieldPath.documentId(), 'in', v);
    }).get();
  }),
  switchMap( v => {
    // Almost there now just need to extract the data from the QuerySnapshots
    const obs: Dance[] = [];
    v.docs.forEach(docRef => {
      obs.push({
        ...docRef.data(),
        id: docRef.id
      } as Dance);
    });
    return of(obs);
  }),
  // And finally we reduce the docs fetched into a single array.
  reduce((acc, value) => acc.concat(value), []),
);
const parentDances = await dance$.toPromise();

我复制粘贴了我的代码并将变量名称更改为您的,不确定是否有任何错误,但它对我来说很好。如果您发现任何错误,或者可以建议更好的方法来测试它,也许可以使用一些模拟 Firestore,请告诉我。

于 2020-05-15T18:24:37.693 回答
2
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);
        });
于 2018-08-08T07:38:45.280 回答
1

使用平面数据结构可能会更好。文档在此页面上
指定了不同数据结构的优缺点。

特别是关于带有子集合的结构的限制:

您无法轻松删除子集合,或跨子集合执行复合查询。

与平面数据结构的所谓优势相比:

根级集合提供最大的灵活性和可扩展性,以及每个集合内的强大查询。

于 2018-03-14T22:54:05.980 回答