在Firestore中,如果用户有他们可以访问的单独文档列表,我该如何查询这些文档 [英] In firestore, if a user has a separate list of documents they can access, how can I query for those documents
问题描述
我有这样的数据库结构:
I have a database structure like this:
companies (collection)
userMessages (collection)
message1 (document)
messageId: message1 (field)
title: ...
...
users (collection)
companyMessages (collection)
message1 (document)
createdAt: ... (field)
我有如下安全规则:
match /users/{userId}/companyMessages/{messageId} {
allow read: if request.auth.uid == userId
}
match /{path=**}/userMessages/{messageId} {
allow read: if
exists(/databases/$(database)/documents/users/$(request.auth.uid)/companyMessages/$(messageId))
}
现在,用户可以通过这样做来查询最近的10条帖子
Now the user can query for their 10 most recent posts by doing
firestore.collection("users").doc(userId).collection("companyMessages").orderBy("createdAt").limit(10)
然后,我想使用该查询的结果来实际获取消息,因此我想执行一个集合组查询:
Then I want to use the result of that query to actually get the messages, so I want to do a collection group query:
firestore.collectionGroup("userMessages").where("messageId", "in", idsFromPreviousQuery)
但是,这将导致"FirebaseError:缺少权限或权限不足".错误.我检查了我的安全规则,因为我可以执行以下查询:
However this will cause a "FirebaseError: Missing or insufficient permissions." error. I have checked that my security rules works because I can do a query like this:
firestore.collection("companies").doc(companyId).collection("userMessages").doc(messageIdThatIsInUserCollection)
但是用 where()
进行查询不起作用.
However doing a query with where()
does not work.
firestore.collection("companies").doc(companyId).collection("userMessages").where("messageId", "==", messageIdThatIsInUserCollection)
我做错什么了吗?或者这种结构不可能吗?我该如何构建数据结构以允许查询用户应该能够获取最近的n条消息,但他们仍然只能访问其集合中列出的消息?
Am I doing something wrong or is this kind of structure not possible? How can i structure my data to allow queries where users should be able to get their last n messages, but they still should only have access to the messages that is listed in their collection?
推荐答案
按文档ID查询
要按文档ID查询,请使用 firebase.firestore.FieldPath.documentId()
.这将返回一个特殊的对象,Firestore将其理解为使用文档的ID作为搜索的属性".重要的是,当没有找到特定的ID时,结果中会简单地省略它们-不会引发任何错误.
Query by Document ID
To query by a document ID, you use firebase.firestore.FieldPath.documentId()
. This returns a special object that Firestore understands as "use the document's ID as the property to search on". Importantly, when a particular ID is not found, they are simply omitted from the results - no error is thrown.
对于基本的 CollectionReference
,您可以使用:
For a basic CollectionReference
, you can use:
const idsFromPreviousQuery = await firestore
.collection("users").doc(userId)
.collection("companyMessages")
.orderBy("createdAt")
.limit(10)
.then(querySnapshot => querySnapshot.map(doc => doc.id)); // return array of document IDs
const messageDocs = firestore
.collection("companies")
.doc(companyId)
.collection("userMessages")
.where(firebase.firestore.FieldPath.documentId(), "in", idsFromPreviousQuery)
.get()
.then(querySnapshot => querySnapshot.docs) // return only the documents array
但是,对于收集组查询,当以这种方式使用 documentId()
时,必须指定完整路径.为此,您需要在邮件列表中存储邮件的路径和/或相关的公司:
However, for a collection group query, you must specify the full path instead when using documentId()
this way. For this, you will need to store the message's path and/or the relevant company in your list of messages:
"users/someUserId/companyMessages/someMessageId": {
createdAt: /* ... */,
company: "companyA"
}
或
"users/someUserId/companyMessages/someMessageId": {
createdAt: /* ... */,
path: "companies/companyA/userMessages/someMessageId"
}
const myMessagePaths = await firestore
.collection("users").doc(userId)
.collection("companyMessages")
.orderBy("createdAt")
.limit(10)
.then(querySnapshot => querySnapshot.map(doc => doc.get("path"))); // return array of document paths
const messageDocs = firestore.collectionGroup("userMessages")
.where(firebase.firestore.FieldPath.documentId(), "in", myMessagePaths)
.get()
.then(querySnapshot => querySnapshot.docs) // return only the documents array
另一种选择是将消息的ID存储在消息文档中,以便可以使用收集组查询来查询它.
Another option is to store the message's ID inside the message document so that it can be queried against using a collection group query.
"companies/someCompanyId/userMessages/someMessageId": {
"_id": "someMessageId",
/* ... other data ... */
}
const idsFromPreviousQuery = await firestore
.collection("users").doc(userId)
.collection("companyMessages")
.orderBy("createdAt")
.limit(10)
.then(querySnapshot => querySnapshot.map(doc => doc.id)); // return array of document IDs
firestore
.collection("companies")
.doc(companyId)
.collection("userMessages")
.where("_id", "in", idsFromPreviousQuery)
.get()
.then(querySnapshot => querySnapshot.docs) // return only the documents array
where("field","in",someArray)
的过滤器限制一个 where()
过滤器的 in
运算符一次被限制为10个值.搜索超过10个ID时,应将ID数组分成10个项目的块.
Filter limitations for where("field", "in", someArray)
A where()
filter's in
operator is limited to 10 values at a time. When searching more than 10 IDs, you should split the array of IDs up into blocks of 10 items.
/** splits array `arr` into chunks of max size `n` */
const chunkArr = (arr, n) => {
if (n <= 0) throw new Error("n must be greater than 0");
return Array
.from({length: Math.ceil(arr.length/n)})
.map((_, i) => arr.slice(n*i, n*(i+1)))
}
const idsFromPreviousQuery = [ /* ... */ ];
const idsInChunks = chunkArr(idsFromPreviousQuery, 10);
const getDocPromises = idsInChunks.map((idsInThisChunk) => (
firestore.collectionGroup("userMessages")
.where("_id", "in", idsInThisChunk)
.get()
.then(querySnapshot => querySnapshot.docs) // return only the documents array
));
const allFoundDocs = await Promise.all(getDocPromises)
.then(getDocResults => (
getDocResults.reduce((acc, arr) => acc.push(...arr), []) // flatten the documents into one array
);
// allFoundDocs now contains an array of QueryDocumentSnapshot objects that match the given IDs
这篇关于在Firestore中,如果用户有他们可以访问的单独文档列表,我该如何查询这些文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!