首页 文章

如何在Cloud Firestore查询中加入多个文档?

提问于
浏览
8

我有一个具有以下结构的Cloud Firestore DB:

  • 个用户

  • [uid]

  • 名称:"Test User"

  • 帖子

  • [id]

  • 内容:"Just some test post."

  • 时间戳:( 2017年12月22日)

  • uid:[uid]

实际DB中存在更多数据,上面仅说明了集合/文档/字段结构 .

我在我的网络应用程序中有一个视图,我正在显示帖子,并希望显示发布的用户的名称 . 我正在使用以下查询来获取帖子:

let loadedPosts = {};
posts = db.collection('posts')
          .orderBy('timestamp', 'desc')
          .limit(3);
posts.get()
.then((docSnaps) => {
  const postDocs = docSnaps.docs;
  for (let i in postDocs) {
    loadedPosts[postDocs[i].id] = postDocs[i].data();
  }
});

// Render loadedPosts later

我想要做的是通过存储在post 's uid field, and add the user' name字段中的uid将用户对象查询到相应的loadedPosts对象中 . 如果我一次只加载一个帖子就没问题,只需等待查询返回一个对象,然后在 .then() 函数中再次查询用户文档,依此类推 .

但是因为我们很难找到如何在每个帖子的 user/[uid] 文档上调用 .get() 之后将正确的用户映射到正确的帖子,因为他们返回了异步方式 .

谁能想到这个问题的优雅解决方案?

4 回答

  • 7

    我会做1个用户doc调用和所需的帖子调用 .

    let users = {} ;
    let loadedPosts = {};
    db.collection('users').get().then((results) => {
      results.forEach((doc) => {
        users[doc.id] = doc.data();
      });
      posts = db.collection('posts').orderBy('timestamp', 'desc').limit(3);
      posts.get().then((docSnaps) => {
        docSnaps.forEach((doc) => {
        loadedPosts[doc.id] = doc.data();
        loadedPosts[doc.id].userName = users[doc.data().uid].name;
      });
    });
    
  • 12

    对我来说似乎相当简单:

    let loadedPosts = {};
    posts = db.collection('posts')
              .orderBy('timestamp', 'desc')
              .limit(3);
    posts.get()
    .then((docSnaps) => {
      docSnaps.forEach((doc) => {
        loadedPosts[doc.id] = doc.data();
        db.collection('users').child(doc.data().uid).get().then((userDoc) => {
          loadedPosts[doc.id].userName = userDoc.data().name;
        });
      })
    });
    

    如果要防止多次加载用户,可以缓存用户数据客户端 . 在这种情况下,我建议将用户加载代码分解为辅助函数 . 但它将是上述的变种 .

  • -2

    我的解决方案如下 .

    概念:您知道要获取信息的用户ID,在帖子列表中,您可以请求用户文档并将其保存为您的帖子项目中的承诺 . 在承诺解决后,您将获得用户信息 .

    注意:我不测试下面的代码,但它是我的代码的简化版本 .

    let posts: Observable<{}[]>;  // you can display in HTML directly with  | async tag
    this.posts = this.listenPosts()
             .map( posts => {
               posts.forEach( post => {
                    post.promise = this.getUserDoc( post.uid )
                                   .then( (doc: DocumentSnapshot) => {
                                      post.userName = doc.data().name;
                                   });
               }); // end forEach
               return posts;
             });
    
    // normally, i keep in provider
    listenPosts(): Observable<any> {
      let fsPath = 'posts';
      return this.afDb.collection( fsPath ).valueChanges();
    }
    
    // to get the document according the user uid
    getUserDoc( uid: string ): Promise<any> {
       let fsPath = 'users/' + uid;
       return this.afDb.doc( fsPath ).ref.get();
    }
    

    注意:afDb:AngularFirestore它在构造函数中初始化(通过angularFire lib)

  • 1

    我将只做一个userdoc查询和所需的postsdoc调用 .

    Let users={} ;
    let loadedPosts = {};
    db.collection('users').get().then((results) =>{
    Results.forEach( (doc) =>
    users[doc.id]=doc.data();
    }) 
    posts=db.collection('posts') .orderBy('timestamp', 'desc') 
    posts.get().then((docSnaps)=>{
    docSnaps.forEach((doc)=>{
    loadedPosts[doc.id] = doc.data();
    loadedPosts[doc.id].userName = users[doc.data().uid].name});
    })
    

相关问题