Cloud Firestore:如何在我的集合查询中获取文档引用并将其映射为 JSON 值?
Posted
技术标签:
【中文标题】Cloud Firestore:如何在我的集合查询中获取文档引用并将其映射为 JSON 值?【英文标题】:Cloud Firestore: how to fetch a document reference inside my collection query and map it as a JSON value? 【发布时间】:2018-04-03 09:14:24 【问题描述】:假设我有一个 cmets 集合。每个评论对象对发布的用户都有一个“doc ref”。我需要一个查询,它会返回一个包含每个用户引用的值的 cmets 列表,所以我的查询返回一个格式很好的 Json 注释对象。
【问题讨论】:
【参考方案1】:What is firestore Reference data type good for? 在这里提出了类似的问题,我认为根据这个答案https://***.com/a/46570119/473453 做你所问的事情是不可能的。
您必须自己加载每个引用,例如
const comments = []
firebase.firestore().collection('/comments').get().then(snapshot =>
snapshot.docs.forEach(doc =>
const comment = doc.data()
comment.userRef.get().then(snap =>
comment.user = snap.data()
comments.push(comment)
)
)
)
对于许多 cmets,这会增加很多开销。也许您可以编写一个 CloudFunction,在服务器端为您完成所有工作并返回一个格式化的 JSON。
看起来他们可能会在未来努力支持这一点:https://***.com/a/46614683/473453
【讨论】:
【参考方案2】:我建议您在每条评论中复制用户数据。在评论文档中创建一个名为 user
的对象的字段,并提供显示评论所需的最少信息量。所以你的comment
文档可能看起来像...
id: 'CEXwQAHpk61vC0ulSdZy',
text: 'This is a comment',
user:
id: 'd5O4jTsLZHXmD1VJXwoE,
name: 'Charlie Martin',
avatar: 'https://...'
现在,您拥有显示评论所需的一切。如果有人点击评论的作者,您可以获取该 id 并使用它来加载评论者的完整个人资料。
在关系数据库中数据重复是不受欢迎的,因为它们是为使用外键处理这些场景而构建的。
然而,在 Firestore 等 NoSQL 数据库中,实际上鼓励数据复制以简化查询并减少通过网络发送的数据量。
如果您为每条评论加载了完整的 user
文档,那么您加载的用户信息可能比显示评论所需的信息多得多。
【讨论】:
请注意,使用这种方法,您将获得过时的数据,例如,如果用户更改其用户名(除非每次用户更新您都会更新所有出现的数据) 正确。不过,这仍然是 Google 推荐的方法。这并不重要,因为 ID 仍然有效。例如,我可能会在评论线程中看到用户“Joe”的帖子,但是当我单击该用户时,我看到他们已将姓名更改为“Bob”。没什么大不了的。 好吧,在我看来,这笔交易的规模更大;)我点击了“Joe”,突然我进入了某个 Bob 的个人资料——我变成了一个非常困惑的用户:“是我的手指这么胖?我的视力是不是很模糊?我很确定我想看看乔的个人资料,这是我从未听说过的鲍勃……”根据我的经验,普通用户对软件和数据库一无所知 - 他们只知道应用程序没有按照他们期望的那样工作 现在想想,这实际上会很奇怪。在这种情况下,我会编写一个云函数,在更新用户记录时更新所有重复数据 @CharlieMartin 我认为这是正确的方法。我认为偶尔更新其他地方的用户记录而不是多次阅读文档更好,成本更低【参考方案3】:这也让我非常恼火。我做了一个实用助手,可以自动填充第一级。
助手
import get, set from 'lodash'
const getReference = async documentReference =>
const res = await documentReference.get()
const data = res.data()
if (data && documentReference.id)
data.uid = documentReference.id
return data
const hydrate = async (document, paths = []) => Promise.all(
paths.map(async path =>
const documentReference = get(document, path)
if (!documentReference || !documentReference.path)
return console.warn(
`Error hydrating documentReference for path "$path": Not found or invalid reference`
)
const result = await getReference(documentReference)
set(document, path, result)
)
)
export hydrate
示例用法:
getUser = async uid =>
return this.db
.collection('users')
.doc(uid)
.get()
.then(async documentSnapshot =>
const data = documentSnapshot.data()
await hydrate(data, ['company', 'someOtherNestedRef-2', 'someOtherNestedRef-1'])
return data
)
限制:此示例不适用于深层嵌套引用
【讨论】:
这真的很有帮助!谢谢克里斯。【参考方案4】:添加到 ChrisRich response。如果所需字段是引用数组,则可以使用以下代码:
import get, set from 'lodash'
const getReference = async documentReference =>
const res = await documentReference.get()
const data = res.data()
if (data && documentReference.id)
data.uid = documentReference.id
return data
export default async (document, paths = []) => Promise.all(
paths.map(async path =>
const documentField = get(document, path)
if (documentField.constructor.name === 'Array')
for (let i = 0; i < documentField.length; i++)
const documentReference = documentField[i];
if (!documentReference || !documentReference.path)
return console.warn(
`Error hydrating documentReference for path "$path": Not found or invalid reference`
)
const result = await getReference(documentReference)
documentField[i] = result;
else
const documentReference = documentField;
if (!documentReference || !documentReference.path)
return console.warn(
`Error hydrating documentReference for path "$path": Not found or invalid reference`
)
const result = await getReference(documentReference)
set(document, path, result)
)
)
【讨论】:
克里斯回答的这个扩展非常有帮助。谢谢路易斯!【参考方案5】:FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("Stock").whereEqualTo("user", userName).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>()
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task)
if (task.isComplete())
Log.d(TAG, "onComplete stock : "+ task.getResult().getDocuments().toString());
List<DocumentSnapshot> list = task.getResult().getDocuments();
if (list != null && list.size()>0)
for (DocumentSnapshot doc: list)
StockData stockData = new StockData();
stockData.setCatergory(doc.get("catergory").toString());
stockData.setDate(doc.get("date").toString());
stockData.setUser(doc.getString("date_time"));
stockData.setUser(doc.getString("user"));
stockData.setSale_price(doc.getDouble("sale_price"));
);
【讨论】:
【参考方案6】:我为此创建了一个解决方案,至少对我有用!
假设你有 2 个集合:用户和朋友,用户有一个文档:用户 1,朋友也有一个文档:朋友 1。
所以 User1 有一个包含此文本的参考字段:Friends/Friend1。
您可以获取所有用户,并且您可以为每个用户构建这样的地图:
[
"User1":"Friend1"
"Another User":"Another Friend"
]
【讨论】:
以上是关于Cloud Firestore:如何在我的集合查询中获取文档引用并将其映射为 JSON 值?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Javascript 在 Cloud Firestore 中创建集合?
使用规则禁用 Firebase Cloud Firestore 中的查询集合