【发布时间】:2019-07-25 05:54:27
【问题描述】:
我以为我读到您可以使用新的 Firebase Firestore 查询子集合,但我没有看到任何示例。例如,我通过以下方式设置 Firestore:
- 舞蹈[收藏]
- 舞名
- 歌曲[收藏]
- 歌名
如何查询“查找所有歌曲名称 == 'X' 的舞蹈”
【问题讨论】:
标签: firebase google-cloud-firestore
我以为我读到您可以使用新的 Firebase Firestore 查询子集合,但我没有看到任何示例。例如,我通过以下方式设置 Firestore:
如何查询“查找所有歌曲名称 == 'X' 的舞蹈”
【问题讨论】:
标签: firebase google-cloud-firestore
2019-05-07 更新
今天我们发布了collection group queries,这些允许您跨子集合进行查询。
因此,例如在 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);
我希望这会有所帮助。
【讨论】:
songName_Title3: false 有什么用?如果我没记错的话,它只能用于搜索没有特定歌曲名称的舞蹈,假设我们需要 songName_Title3: false 以使 dances.where("songName_"+songTitle, "==", false); 返回这样的结果,它不会让每支舞蹈都有意义每个可能的歌曲名称的布尔标志...
如果您将歌曲存储为对象而不是集合会怎样?每个舞曲,以歌曲为字段:类型Object(不是集合)
{
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 年更新
Firestore 已发布集合组查询。见上面吉尔的回答或者官方Collection Group Query Documentation
上一个答案
正如 Gil Gilbert 所说,似乎集合组查询目前正在进行中。同时,最好使用根级集合,并使用文档 UID 链接这些集合。
对于那些还不知道的人,Jeff Delaney 在AngularFirebase 上为任何使用 Firebase(和 Angular)的人提供了一些令人难以置信的指南和资源。
Firestore NoSQL Relational Data Modeling - 在这里他分解了 NoSQL 和 Firestore 数据库结构的基础知识
Advanced Data Modeling With Firestore by Example - 这些是更先进的技术,请牢记在心。对于那些希望将 Firestore 技能提升到新水平的人来说,这是一本很好的读物
【讨论】:
2019 年 7 月 8 日新更新:
db.collectionGroup('Songs')
.where('songName', isEqualTo:'X')
.get()
【讨论】:
我找到了解决办法。 请检查这个。
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 和一些字段值。 然后选择集合组选项。 好好享受。谢谢
【讨论】:
你总是可以这样搜索:-
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;
}
})
);
【讨论】:
查询限制
Cloud Firestore 不支持以下类型的查询:
在不同字段上使用范围过滤器进行查询。
跨多个集合或子集合的单个查询。每个查询针对单个文档集合运行。更多 有关数据结构如何影响查询的信息,请参阅 Choose a Data Structure.
逻辑或查询。在这种情况下,您应该为每个 OR 条件创建一个单独的查询,并在您的应用中合并查询结果。
带有 != 子句的查询。在这种情况下,您应该将查询拆分为大于查询和小于查询。例如,虽然 不支持查询子句 where("age", "!=", "30"),可以 通过组合两个查询获得相同的结果集,一个带有子句 where("age", "", 30) 的子句。
【讨论】:
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);
});
【讨论】:
我在这里使用 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 进行测试,请告诉我。
【讨论】:
最好使用平面数据结构。
文档详细说明了不同数据结构 on this page 的优缺点。
特别是关于带有子集合的结构的限制:
您无法轻松删除子集合,或跨子集合执行复合查询。
与平面数据结构的所谓优势相比:
根级集合提供最大的灵活性和可扩展性,以及每个集合内的强大查询。
【讨论】: