我以为我读到您可以使用新的 Firebase Firestore 查询子集合,但我没有看到任何示例。例如,我通过以下方式设置 Firestore:
- 舞蹈[收藏]
- 舞名
- 歌曲[收藏]
- 歌名
我如何能够查询“查找所有歌曲名称 == 'X' 的舞蹈”
我以为我读到您可以使用新的 Firebase Firestore 查询子集合,但我没有看到任何示例。例如,我通过以下方式设置 Firestore:
我如何能够查询“查找所有歌曲名称 == 'X' 的舞蹈”
2019-05-07 更新
今天我们发布了集合组查询,这些允许您跨子集合进行查询。
因此,例如在 Web SDK 中:
db.collectionGroup('Songs')
.where('songName', '==', 'X')
.get()
这将匹配集合路径的最后一部分是“歌曲”的任何集合中的文档。
您最初的问题是关于查找歌曲名称 == 'X' 的舞蹈,但这仍然不可能直接进行,但是,对于匹配的每首歌曲,您都可以加载其父级。
原始答案
这是一个尚不存在的功能。它被称为“集合组查询”,允许您查询所有歌曲,而不管它们包含哪些舞蹈。这是我们打算支持的东西,但没有具体的时间表来说明它何时到来。
此时的替代结构是将歌曲制作为顶级集合,并使歌曲的哪个舞蹈成为歌曲属性的一部分。
更新 现在 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);
我希望这有帮助。
2019 年更新
Firestore 已发布集合组查询。请参阅上面 Gil 的回答或官方Collection Group Query Documentation
上一个答案
正如 Gil Gilbert 所说,似乎集合组查询目前正在进行中。同时,最好使用根级别的集合,并使用文档 UID 在这些集合之间进行链接。
对于那些还不知道的人,Jeff Delaney 为在AngularFirebase上使用 Firebase(和 Angular)的任何人提供了一些令人难以置信的指南和资源。
Firestore NoSQL 关系数据建模- 在这里,他分解了 NoSQL 和 Firestore 数据库结构的基础知识
通过示例使用 Firestore 进行高级数据建模- 这些是更高级的技术,请牢记在心。对于那些想要将他们的 Firestore 技能提升到一个新水平的人来说,这是一本很好的读物
如果您将歌曲存储为对象而不是集合会怎样?每个舞蹈都作为,以歌曲作为字段:类型对象(不是集合)
{
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);
});
2019 年 7 月 8 日新更新:
db.collectionGroup('Songs')
.where('songName', isEqualTo:'X')
.get()
你总是可以这样搜索:-
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;
}
})
);
我找到了解决方案。请检查这个。
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 和一些字段值。然后选择集合组选项。好好享受。谢谢
查询限制
Cloud Firestore 不支持以下类型的查询:
在不同字段上使用范围过滤器的查询。
跨多个集合或子集合的单个查询。每个查询都针对单个文档集合运行。有关数据结构如何影响查询的更多信息,请参阅 选择数据结构。
逻辑或查询。在这种情况下,您应该为每个 OR 条件创建一个单独的查询,并在您的应用程序中合并查询结果。
带有 != 子句的查询。在这种情况下,您应该将查询拆分为大于查询和小于查询。例如,虽然不支持查询子句 where("age", "!=", "30"),但是可以通过组合两个查询得到相同的结果集,其中一个是 where("age", "< ", "30") 和一个带有 where("age", ">", 30) 的子句。
我在这里使用 Observables 和AngularFire包装器,但这就是我设法做到这一点的方法。
这有点疯狂,我还在学习 observables,我可能做得过火了。但这是一个很好的练习。
一些解释(不是 RxJS 专家):
in
查询。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,请告诉我。
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);
});
使用平面数据结构可能会更好。文档在此页面上
指定了不同数据结构的优缺点。
特别是关于带有子集合的结构的限制:
您无法轻松删除子集合,或跨子集合执行复合查询。
与平面数据结构的所谓优势相比:
根级集合提供最大的灵活性和可扩展性,以及每个集合内的强大查询。