Cloud Firestore:如何在我的集合查询中获取文档引用并将其映射为JSON值?

时间:2017-10-22 20:34:23

标签: firebase cloud ref google-cloud-firestore

假设我有一系列评论。每个评论对象都有一个“doc ref”给发布的用户。我需要一个返回注释列表的查询,包括每个用户引用的值,所以我的查询返回一个很好的格式化的Json注释对象。

6 个答案:

答案 0 :(得分:10)

此处提出了类似的问题What is firestore Reference data type good for?,根据这个回答https://stackoverflow.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)
    })
  })
})

对于许多评论,这将增加很多开销。也许你可以在服务器端编写一个为你们所有人工作的CloudFunction,并返回一个格式化的JSON。

看起来他们可能会在未来继续支持这项工作:https://stackoverflow.com/a/46614683/473453

答案 1 :(得分:3)

我建议你在每条评论中复制用户数据。在注释文档中创建一个名为user的对象的字段,并提供显示注释所需的最少信息量。因此,您的comment doc可能看起来像......

{
  id: 'CEXwQAHpk61vC0ulSdZy',
  text: 'This is a comment',
  user: {
    id: 'd5O4jTsLZHXmD1VJXwoE,
    name: 'Charlie Martin',
    avatar: 'https://...'
  }
}

现在,您拥有显示评论所需的一切。如果有人点击评论的作者,您可以获取该ID并使用它来加载评论员的完整个人资料。

数据复制在关系数据库中不受欢迎,因为它们是为使用外键处理这些场景而构建的。

然而,在像firestore这样的NoSQL数据库中,实际上鼓励数据重复来简化查询并减少通过网络发送的数据量。

如果您为每条评论加载了完整的user文档,则可能会加载有关用户的更多信息,而不是显示评论所需的信息。

答案 2 :(得分:1)

这也惹恼了我。我做了一个实用程序助手,它可以自动填充第一级。

助手

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
    })
}

限制:此示例不适用于深层嵌套引用

答案 3 :(得分:1)

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"));
                }
            }
        }
    }
});

enter image description here

答案 4 :(得分:0)

我为此创建了一个解决方案,至少对我有用!

想象一下,您有2个集合:用户和朋友,而用户有一个文档:User1和朋友也有一个文档:Friend1。

因此,User1的参考字段为以下文本:Friends / Friend1。

U可以获取所有用户,并且您可以为每个用户建立一个这样的地图:

[
  {
    "User1":"Friend1"
  }
  {
    "Another User":"Another Friend"
  }
]

答案 5 :(得分:0)

添加到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)
        }
    })
)