什么是Firebase Firestore '参考'数据类型好?

5q4ezhmt  于 2022-12-19  发布在  其他
关注(0)|答案(9)|浏览(116)

我只是在探索新的Firebase Firestore,它包含一个名为reference的数据类型,我不清楚它的作用。

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

a11xaf1n1#

在Firestore中使用参考资料添加以下对我有效的内容。
就像其他答案说的,它就像一个外键,但是reference属性不返回引用单据的数据,比如我有一个产品列表,产品上的属性之一是userRef引用,得到产品列表,提供了创建该产品的用户的参考。但它没有提供该参考中的用户的详细信息。我'我使用其他后端作为服务,在此之前使用指针“populate:true”标志,它返回用户的详细信息,而不仅仅是用户的引用id,如果在这里有这个标志就太好了(希望将来能改进)。
下面是一些示例代码,我使用它们来设置引用,以及获取产品列表集合,然后从给定的用户引用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) });

希望这能帮上忙

piwo6bdm

piwo6bdm2#

引用与外键非常相似。
当前发布的SDK无法存储对其他项目的引用。在项目中,引用可以指向任何其他集合中的任何其他文档。
可以像使用任何其他值一样在查询中使用引用:用于筛选、排序和分页(startAt/startAfter)。
与SQL数据库中的外键不同,引用对于在单个查询中执行联接没有用处。您可以将它们用于相关查找(看起来像联接),但要小心,因为每一跳都将导致到服务器的另一次往返。

fhity93d

fhity93d3#

对于那些寻找通过引用查询的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

kb5ga3dv

kb5ga3dv4#

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

bnlyeluc

bnlyeluc5#

很多答案都提到它只是对另一个文档的引用,但不返回该引用的数据,但我们可以使用它单独获取数据。
下面是一个如何在firebase JavaScript SDK 9, modular版本中使用它的示例。
让我们假设你的firestore有一个名为products的集合,它包含以下文档。

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

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

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

这里collection, getDocs, getDoc, query, where是firestore相关的模块,我们可以在任何需要的时候使用它来获取数据。我们使用从products文档直接返回的用户引用来获取那个引用的用户文档,使用下面的代码,

let userData = await getDoc(newItem.userRef);

要阅读更多关于如何使用模块化版本SDK的信息,请参阅官方文档以了解更多信息。

qybjjes1

qybjjes16#

如果不使用Reference数据类型,则需要更新每个文档
例如,您有2个集合**"categories""products",您将categories中的类别名称"Fruits"存储到products中的"Apple""Lemon"的每个文档中,如下所示。但是,如果您更新categories中的类别名称"Fruits",您还需要更新产品"苹果""柠檬"的每个文档中的类别名称"水果"**:

collection | document | field

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

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

但是,如果您将类别中的"水果"引用存储到产品中的"苹果""柠檬"的每个文档,则在更新类别中的类别名称"水果"时,无需更新"苹果""柠檬"**的每个文档:

collection | document | field

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

这就是Reference数据类型的优点。

uxhixvfz

uxhixvfz7#

迟来的是,this blog带来了两个好处:

如果我希望按评级、发布日期或最多赞成票对餐馆评论进行排序,我可以在releves子集合中完成,而不需要复合索引。在较大的顶级集合中,我需要为其中的每一个创建单独的复合索引,并且我也有200个复合索引的限制。
我不会有200个综合指数,但有一些限制。
此外,从安全规则的Angular 来看,根据父文档中存在的某些数据来限制子文档是相当常见的,当您在子集合中设置数据时,这要容易得多。
例如,如果用户在父集合的字段中没有权限,则限制插入子集合。

ltskdhd1

ltskdhd18#

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

0dxa2lsx9#

    • 更新12/18/22**-我把这个放在一个包裹里。

Original Blog Post
这个包使用RXJS循环遍历文档中的每个字段。如果文档类型是Reference类型,集合版本获取集合中所有文档中每个引用字段的外键值。您还可以手动输入要解析的字段以加快搜索速度(参见我的帖子)。这绝对不像使用Firebase函数进行手动聚合那样高效,因为你会因为阅读每个文档而收取大量阅读费用,但对于那些想要快速在前端连接数据的人来说,它可能会派上用场。
如果您缓存数据并且实际上只需要执行一次,那么这也会派上用场。
J型

    • 安装**
npm i j-firebase
    • 导入**
import { expandRef, expandRefs } from 'j-firebase';

https://github.com/jdgamble555/j-firebase

原始帖子

自动连接:

    • 文件**
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))
  );
}
    • 收藏**
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)
    )
  );
}

只需将此函数放在观察对象周围,它就会自动扩展所有引用数据类型,提供自动连接。

    • 用法**
this.posts = expandRefs(
  collectionData(
    query(
      collection(this.afs, 'posts'),
      where('published', '==', true),
      orderBy(fieldSort)
    ), { idField: 'id' }
  )
);
    • 注意:**现在您还可以输入要展开的字段作为数组中的第二个参数。

第一个月
这将提高速度!
添加.pipe(take(1)).toPromise();在最后为一个承诺的版本!
更多信息请参见here。适用于Firebase 8或9!
简单!
J型

相关问题