数据结构:
houses (collection)
name (string)
users (map)
90c234jc23 (map)
percentage: 100% (string/number)
规则:
allow read: request.auth.uid in resource.data.users;
问题是当我尝试查询用户拥有的房屋时:
FirebaseFirestore.getInstance().collection(House.COLLECTION)
// .whereArrayContains(House.USERS_FIELD, currentUser.getUid()) // does not work
.whereEqualTo("users." + currentUser.getUid(), currentUser.getUid()) // does not work either
.get()
没有返回结果。
您可以使用
orderBy
来查找map中包含某个key的文档。使用此示例文档:
{
"users": {
"bob": {},
"sam": {},
}
}
.orderBy('users.bob')
只会查找包含 users.bob
的文档。
您无法在 firestore 中执行此类查询,因为没有“map-contains-key”运算符。然而,有一些非常简单的解决方法可以通过对数据结构进行轻微调整来实现这一点。
要求: 要使此解决方案发挥作用,每个映射值必须在 Firestore 查询中唯一可识别,这意味着它不能是映射或数组。
如果您的案例满足列出的要求,您可以采用@Dennis Alund 的解决方案,该解决方案建议使用以下数据结构:
{
name: "The Residence",
users: {
uid1: 80,
uid2: 20
}
}
如果您的映射值是映射或数组,则需要向每个值添加一个属性,该属性在该类型的所有创建的值中保持不变。这是一个例子:
{
name: "The Residence",
users: {
uid1: {
exists: true,
percentage: 80,
...
},
uid2: {
exists: true,
percentage: 20,
...
},
}
}
现在您可以简单地使用查询:
_firestore.collection('houses').whereEqualTo('users.<uid>.exists', true)
编辑:
正如 @Johnny Oshika 正确指出的那样,您还可以使用
orderBy()
按字段名称进行过滤。
此查询不起作用,因为您的
users
字段是映射而不是数组。
.whereArrayContains(House.USERS_FIELD, currentUser.getUid())
此查询
.whereEqualTo("users." + currentUser.getUid(), currentUser.getUid())
不起作用,因为
users.<uid>
的映射值是一个表示 percentage: xx%
的字符串,并且该语句正在测试 percentage: xx% === <uid>
是否存在,这是错误的。
这种策略会有问题,因为您无法进行查询来查找“不为空”或“字符串不为空”等的项目。
我假设该百分比是用户对房屋的所有权(?)。如果是这样,如果您想保持与问题中相同的文档结构,那么您可能会更幸运地尝试像这样构建您的家庭文档数据
{
name: "The Residence",
users: {
uid1: 80,
uid2: 20
}
}
这将允许您执行诸如
之类的查询.whereGreaterThan("users." + currentUser.getUid(), 0)
查找对该房屋拥有部分所有权份额的用户。
但是有一点警告,一旦您需要复合索引,您就会开始在维护该结构时遇到问题。相反,您可能需要考虑存储拥有该房屋的用户数组以便于查询。
一种解决方案是将用户 ID 存储在用户映射字段旁边的数组字段中
{
"users": {
"uid1": {},
"uid2": {},
},
"userIds": ["uid1", "uid2"]
}
您现在应该使用
FirebaseFirestore.getInstance().collection(House.COLLECTION)
.whereArrayContains(House.USER_IDS_FIELD, currentUser.getUid())
.get()