简体   繁体   English

Firebase Firestore“参考”数据类型有什么用?

[英]What is Firebase Firestore 'Reference' data type good for?

I'm just exploring the new Firebase Firestore and it contains a data type called reference .我只是在探索新的 Firebase Firestore,它包含一个名为reference的数据类型。 It is not clear to me what this does.我不清楚这是做什么的。

  • Is it like foreign key?它像外键吗?
  • Can it be used to point to a collection that is located somewhere else?它可以用来指向位于其他地方的集合吗?
  • If reference is an actual reference, can I use it for queries?如果reference是实际参考,我可以将其用于查询吗? For example can I have a reference that points directly to the user, instead of storing the userId in a text field?例如,我可以有一个直接指向用户的引用,而不是将 userId 存储在文本字段中吗? And can I use this user reference for querying?我可以使用此用户参考进行查询吗?

Adding below what worked for me using references in Firestore.在 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.但是,reference 属性不返回参考文档的数据。 For example, I have a list of products, with a userRef reference as one of the attributes on the product.例如,我有一个产品列表,将 userRef 引用作为产品的属性之一。 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).我已经使用其他后端作为带有指针的服务,之前有一个“populate:true”标志,该标志可以返回用户详细信息,而不仅仅是用户的参考 ID,这在此处会很棒(希望将来能改进) )。

Below is some example code that I used to set the reference as well as get the collection of products list then get the user details from the user reference id given.下面是一些示例代码,我用来设置引用以及获取产品列表的集合,然后从给定的用户引用 ID 中获取用户详细信息。

Set a reference on a collection:在集合上设置引用:

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

Get a collection (products) and all references on each document (user details):获取集合(产品)和每个文档上的所有引用(用户详细信息):

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

Hope this helps希望这可以帮助

References are very much like foreign keys.引用非常像外键。

The currently released SDKs cannot store references to other projects.当前发布的 SDK 无法存储对其他项目的引用。 Within a project, references can point to any other document in any other collection.在一个项目中,引用可以指向任何其他集合中的任何其他文档。

You can use references in queries like any other value: for filtering, ordering, and for paging (startAt/startAfter).您可以像任何其他值一样在查询中使用引用:用于过滤、排序和分页 (startAt/startAfter)。

Unlike foreign keys in a SQL database, references are not useful for performing joins in a single query.与 SQL 数据库中的外键不同,引用对于在单个查询中执行连接没有用。 You can use them for dependent lookups (which seem join like), but be careful because each hop will result in another round trip to the server.您可以将它们用于依赖查找(看起来像连接),但要小心,因为每跳都会导致到服务器的另一次往返。

For those looking for a Javascript solution to querying by reference - the concept is that, you need to use a 'document reference' object in the query statement对于那些寻找通过引用查询的 Javascript 解决方案的人 - 概念是,您需要在查询语句中使用“文档引用”对象

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

(Kudos to the answer here: https://stackoverflow.com/a/53141199/1487867 ) (感谢这里的答案: https : //stackoverflow.com/a/53141199/1487867

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

Automatic JOINS:自动连接:

DOC文件

expandRef<T>(obs: Observable<T>, fields: any[] = []): Observable<T> {
  return obs.pipe(
    switchMap((doc: any) => doc ? combineLatest(
      (fields.length === 0 ? Object.keys(doc).filter(
        (k: any) => {
          const p = doc[k] instanceof DocumentReference;
          if (p) fields.push(k);
          return p;
        }
      ) : fields).map((f: any) => docData<any>(doc[f]))
    ).pipe(
      map((r: any) => fields.reduce(
        (prev: any, curr: any) =>
          ({ ...prev, [curr]: r.shift() })
        , doc)
      )
    ) : of(doc))
  );
}

COLLECTION收藏

expandRefs<T>(
  obs: Observable<T[]>,
  fields: any[] = []
): Observable<T[]> {
  return obs.pipe(
    switchMap((col: any[]) =>
      col.length !== 0 ? combineLatest(col.map((doc: any) =>
        (fields.length === 0 ? Object.keys(doc).filter(
          (k: any) => {
            const p = doc[k] instanceof DocumentReference;
            if (p) fields.push(k);
            return p;
          }
        ) : fields).map((f: any) => docData<any>(doc[f]))
      ).reduce((acc: any, val: any) => [].concat(acc, val)))
        .pipe(
          map((h: any) =>
            col.map((doc2: any) =>
              fields.reduce(
                (prev: any, curr: any) =>
                  ({ ...prev, [curr]: h.shift() })
                , doc2
              )
            )
          )
        ) : of(col)
    )
  );
}

Simply put this function around your observable and it will automatically expand all reference data types providing automatic joins.简单地将这个函数放在你的 observable 周围,它会自动扩展所有提供自动连接的引用数据类型。

Usage用法

this.posts = expandRefs(
  collectionData(
    query(
      collection(this.afs, 'posts'),
      where('published', '==', true),
      orderBy(fieldSort)
    ), { idField: 'id' }
  )
);

Note: You can also now input the fields you want to expand as a second argument in an array.注意:您现在还可以输入要扩展的字段作为数组中的第二个参数。

['imageDoc', 'authorDoc']

This will increase the speed!这将提高速度!

Add .pipe(take(1)).toPromise();添加.pipe(take(1)).toPromise(); at the end for a promise version!最后是承诺版本!

See here for more info.请参阅此处了解更多信息。 Works in Firebase 8 or 9!适用于 Firebase 8 或 9!

Simple!简单的!

J J

2022 UPDATE 2022 更新

let coursesArray = [];
const coursesCollection = async () => {
    const queryCourse = query(
        collection(db, "course"),
        where("status", "==", "active")
    )
    onSnapshot(queryCourse, (querySnapshot) => {
        querySnapshot.forEach(async (courseDoc) => {

            if (courseDoc.data().userId) {
                const userRef = courseDoc.data().userId;
                getDoc(userRef)
                    .then((res) => {
                        console.log(res.data());
                    })
            }
            coursesArray.push(courseDoc.data());
        });
        setCourses(coursesArray);
    });
}

Belatedly, there are two advantages from this blog :姗姗来迟, 这个博客有两个优点:

在此处输入图片说明

if I expect that I'll want to order restaurant reviews by rating, or publish date, or most upvotes, I can do that within a reviews subcollection without needing a composite index.如果我希望按评级、发布日期或大多数赞成票对餐厅评论进行排序,我可以在评论子集中执行此操作,而无需复合索引。 In the larger top level collection, I'd need to create a separate composite index for each one of those, and I also have a limit of 200 composite indexes .在较大的顶级集合中,我需要为其中的每一个创建一个单独的复合索引,而且我还有200 个复合索引的限制。

I wouldn't have 200 composite indices but there are some constraints.我不会有 200 个复合指数,但有一些限制。

Also, from a security rules standpoint, it's fairly common to restrict child documents based on some data that exists in their parent, and that's significantly easier to do when you have data set up in subcollections.此外,从安全规则的角度来看,基于父文档中存在的一些数据来限制子文档是相当普遍的,当您在子集合中设置数据时,这会更容易做到。

One example would be restricting to insert a child collection if the user doesn't have the privilege in the parent's field.一个示例是如果用户在父字段中没有权限,则限制插入子集合。

A lot of answers mentioned it is just a reference to another document but does not return data for that reference but we can use it to fetch data separately.很多答案都提到它只是对另一个文档的引用,但不返回该引用的数据,但我们可以使用它来单独获取数据。

Here is an example of how you could use it in the firebase JavaScript SDK 9, modular version.以下是如何在 firebase JavaScript SDK 9, modular版本中使用它的示例。

let's assume your firestore have a collection called products and it contains the following document.假设您的 firestore 有一个名为products的集合,它包含以下文档。

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

here users have a reference to a document in the users collection.这里用户引用了users集合中的文档。 we can use the following code segment to get the product and then retrieve the user from the reference.我们可以使用以下代码段来获取产品,然后从引用中检索用户。

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

here collection, getDocs, getDoc, query, where are firestore related modules we can use to get data whenever necessary.这里是collection, getDocs, getDoc, query, where我们可以在需要时用来获取数据的collection, getDocs, getDoc, query, where相关模块collection, getDocs, getDoc, query, where we use user reference returned from the products document directly to fetch the user document for that reference using the following code,我们使用从products文档直接返回的用户引用来使用以下代码获取该引用的用户文档,

let userData = await getDoc(newItem.userRef);

to read more on how to use modular ver SDK refer to official documentation to learn more.要阅读有关如何使用模块化版本 SDK 的更多信息,请参阅官方文档以了解更多信息。

If you don't use Reference data type , you need to update every document .如果不使用Reference 数据类型,则需要更新每个文档

For example, you have 2 collections "categories" and "products" and you stored the category name "Fruits" in categories to every document of "Apple" and "Lemon" in products as shown below.例如,你有2个集合“类别”“产品”,你保存在类别中的类别名称“水果”“苹果”“柠檬”,产品的每一个文件,如下图所示。 But, if you update the category name "Fruits" in categories , you also need to update the category name "Fruits" in every document of "Apple" and "Lemon" in products :但是,如果你更新类别类别名称“水果”,你还需要更新“苹果”“柠檬”,产品的每一个文档中的类别名称“水果”:

collection | document | field

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

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

But, if you store the reference of "Fruits" in categories to every document of "Apple" and "Lemon" in products , you don't need to update every document of "Apple" and "Lemon" when you update the category name "Fruits" in categories :但是,如果你“水果”的类别参考存储“苹果”“柠檬”,产品的每一个文件,你不需要在更新类别名称更新“苹果”“柠檬”的每一个文件类别中的“水果”

collection | document | field

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

This is the goodness of Reference data type .这就是Reference 数据类型的优点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM