Firestore查询子集合

时间:2017-10-04 19:47:45

标签: firebase google-cloud-firestore

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

  • 舞蹈[收藏]
    • danceName
    • 歌曲[收藏]
      • SONGNAME

我怎样才能查询“查找songName =='X'的所有舞蹈”

11 个答案:

答案 0 :(得分:96)

更新2019-05-07

今天我们发布了collection group queries,这些允许您查询子集合。

因此,例如在Web SDK中:

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

这将匹配任何集合中的文档,其中集合路径的最后部分是'歌曲'。

你原来的问题是关于找到舞蹈,其中songName ==' X',但这仍然不可能直接,但是,对于每个匹配你的歌曲都可以加载其父级。

原始回答

这是一项尚不存在的功能。它被称为"收集组查询"并允许您查询所有歌曲,无论哪个舞蹈包含它们。这是我们打算支持的,但没有具体的时间表来确定它何时会到来。

此时的替代结构是将歌曲设为顶级收藏,并使该歌曲成为歌曲属性的一部分。

答案 1 :(得分:18)

<强>更新 现在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
}

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

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

我希望这会有所帮助。

答案 2 :(得分:13)

如果您将歌曲存储为对象而不是收藏,该怎么办?每个舞蹈都是,歌曲作为一个字段:类型对象(不是集合)

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

答案 3 :(得分:7)

UPDATE 2019

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


上一个答案

正如吉尔·吉尔伯特(Gil Gilbert)所说,似乎集合组查询目前正在开发中。同时,最好使用根级别集合,并使用文档UID在这些集合之间建立链接。

对于那些尚不了解的人,Jeff Delaney为AngularFirebase上使用Firebase(和Angular)的任何人提供了一些不可思议的指南和资源。

Firestore NoSQL Relational Data Modeling-在这里,他介绍了NoSQL和Firestore DB结构的基础知识

Advanced Data Modeling With Firestore by Example-这些是更先进的技术,可让您随时记住。对于那些希望将其Firestore技能提高到一个新水平的人来说是一本不错的书

答案 4 :(得分:4)

2019年7月8日新更新:

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

答案 5 :(得分:1)

使用平面数据结构可能更好 文档指定了不同数据结构的优缺点here is an example

特别是关于具有子集合的结构的局限性:

  

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

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

  

根级别集合提供最大的灵活性和可伸缩性,以及每个集合中的强大查询。

答案 6 :(得分:1)

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);
        });

答案 7 :(得分:1)

  

查询限制

     

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

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

  2.   
  3. 跨多个集合或子集合的单个查询。每个查询针对单个文档集合运行。欲了解更多   有关数据结构如何影响查询的信息,请参见   Choose a Data Structure

  4.   
  5. 逻辑或查询。在这种情况下,您应该为每个OR条件创建一个单独的查询,然后将查询结果合并到您的应用中。
  6.   
  7. 带有!=子句的查询。在这种情况下,您应该将查询分为大于查询和小于查询。例如,尽管   查询子句where(“ age”,“!=”,“ 30”)不支持,您可以   通过组合两个查询获得相同的结果集,其中一个与子句   where(“ age”,“ <”,“ 30”)和带有子句where(“ age”,“>”,30)的子句。
  8.   

答案 8 :(得分:1)

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

这有点疯狂,我仍在学习有关可观察物的信息,但我可能过时了。但这是一个很好的练习。

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

  • songId $是可观察到的,将发出ID
  • dance $是一个可观察的对象,它读取该ID,然后仅获取第一个值。
  • 然后它查询所有歌曲的collectionGroup以查找它的所有实例。
  • 根据实例遍历到父级Dances并获取其ID。
  • 现在,我们有了所有需要查询的Dance ID,就可以查询它们以获取数据。但是我希望它表现良好,所以我不想一个接一个地查询,而是将它们按10个桶进行批处理(in查询的最大角度。
  • 我们最终得到了N个存储桶,需要在Firestore上进行N个查询才能获取其值。
  • 一旦我们在firestore上执行查询,我们仍然仍然需要实际解析其中的数据。
  • 最后,我们可以合并所有查询结果,以得到其中包含所有Dances的单个数组。
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进行测试。

答案 9 :(得分:0)

您可以像这样搜索: -

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;
      }
    })
);

答案 10 :(得分:0)

我找到了解决方案。 请检查一下。

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中查看“数据”,“规则”,“索引”,“使用情况”标签。 最后,您应该在“索引”标签中设置索引。enter image description here

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