我只是在探索新的Firebase Firestore,它包含一个称为引用的数据类型。我不清楚这是干什么的。

它像外键吗? 它可以用来指向位于其他地方的集合吗? 如果引用是一个实际的引用,我可以使用它查询吗?例如,我是否可以有一个直接指向用户的引用,而不是将userId存储在文本字段中?我可以使用这个用户引用进行查询吗?


当前回答

根据#AskFirebase https://youtu.be/Elg2zDVIcLo?t=276 目前的主要用例是Firebase控制台UI中的链接

其他回答

在Firestore中添加下面对我有用的引用。

As the other answers say, it's like a foreign key. The reference attribute doesn't return the data of the reference doc though. For example, I have a list of products, with a userRef reference as one of the attributes on the product. Getting the list of products, gives me the reference of the user that created that product. But it doesn't give me the details of the user in that reference. I've used other back end as a services with pointers before that have a "populate: true" flag that gives the user details back instead of just the reference id of the user, which would be great to have here (hopefully a future improvement).

下面是一些示例代码,我用来设置引用以及获得产品列表集合,然后从给定的用户引用id获得用户详细信息。

在集合上设置引用:

let data = {
  name: 'productName',
  size: 'medium',
  userRef: db.doc('users/' + firebase.auth().currentUser.uid)
};
db.collection('products').add(data);

获取一个集合(产品)和每个文档上的所有引用(用户详细信息):

db.collection('products').get()
    .then(res => {
      vm.mainListItems = [];
      res.forEach(doc => {
        let newItem = doc.data();
        newItem.id = doc.id;
        if (newItem.userRef) {
          newItem.userRef.get()
          .then(res => { 
            newItem.userData = res.data() 
            vm.mainListItems.push(newItem);
          })
          .catch(err => console.error(err));
        } else {
          vm.mainListItems.push(newItem);  
        }
        
      });
    })
    .catch(err => { console.error(err) });

引用很像外键。

当前发布的sdk不能存储对其他项目的引用。在项目中,引用可以指向任何其他集合中的任何其他文档。

您可以像使用任何其他值一样在查询中使用引用:用于过滤、排序和分页(startAt/startAfter)。

与SQL数据库中的外键不同,引用对于在单个查询中执行连接没有用处。您可以使用它们进行依赖查找(看起来像是连接),但要小心,因为每次跳转都会导致到服务器的另一次往返。

很多答案提到它只是对另一个文档的引用,但不返回该引用的数据,但我们可以使用它单独获取数据。

下面是如何在firebase JavaScript SDK 9模块化版本中使用它的示例。

让我们假设您的壁炉有一个名为products的集合,其中包含以下文档。

{
  name: 'productName',
  size: 'medium',
  userRef: 'user/dfjalskerijfs'
}

在这里,用户有对用户集合中的文档的引用。我们可以使用下面的代码段获取产品,然后从引用检索用户。

import { collection, getDocs, getDoc, query, where } from "firebase/firestore";
import { db } from "./main"; // firestore db object

let productsWithUser = []
const querySnaphot = await getDocs(collection(db, 'products'));
querySnapshot.forEach(async (doc) => {
  let newItem = {id: doc.id, ...doc.data()};
  if(newItem.userRef) {
    let userData = await getDoc(newItem.userRef);
    if(userData.exists()) {
      newItem.userData = {userID: userData.id, ...userData.data()}
    }
    productwithUser.push(newItem);
  } else {
    productwithUser.push(newItem);
  }
});

这里的集合,getDocs, getDoc,查询,其中是与firestore相关的模块,我们可以在必要时使用它来获取数据。我们使用从产品文档返回的用户引用直接获取该引用的用户文档,使用以下代码:

let userData = await getDoc(newItem.userRef);

要阅读更多关于如何使用模块化ver SDK参考官方文档了解更多。

如果不使用Reference数据类型,则需要更新每个文档。

例如,您有2个集合“categories”和“products”,并将类别名称“Fruits”存储到产品中“Apple”和“Lemon”的每个文档中,如下所示。但是,如果在categories中更新了categories名称“Fruits”,那么在product中每一个关于“Apple”和“Lemon”的文档中也需要更新category名称“Fruits”:

collection | document | field

categories > 67f60ad3 > name: "Fruits"
collection | document | field

  products > 32d410a7 > name: "Apple", category: "Fruits"
             58d16c57 > name: "Lemon", category: "Fruits"

但是,如果您将“Fruits”在categories中的引用存储到产品中“Apple”和“Lemon”的每个文档中,则当您在categories中更新类别名称“Fruits”时,您不需要更新“Apple”和“Lemon”的每个文档:

collection | document | field

  products > 32d410a7 > name: "Apple", category: categories/67f60ad3
             58d16c57 > name: "Lemon", category: categories/67f60ad3

这就是引用数据类型的优点。

对于那些寻找通过引用查询的Javascript解决方案的人来说,概念是,你需要在查询语句中使用一个“文档引用”对象

teamDbRef = db.collection('teams').doc('CnbasS9cZQ2SfvGY2r3b'); /* CnbasS9cZQ2SfvGY2r3b being the collection ID */
//
//
db.collection("squad").where('team', '==', teamDbRef).get().then((querySnapshot) => {
  //
}).catch(function(error) {
  //
});

(答案在这里:https://stackoverflow.com/a/53141199/1487867)