【问题标题】:Create documents in different firestore collections, with same reference ID在不同的 Firestore 集合中创建具有相同参考 ID 的文档
【发布时间】:2019-05-20 08:13:31
【问题描述】:

我的问题实际上是双重的,所以我不确定我应该在一个帖子中同时询问还是创建另一个帖子。无论如何,这里是:

我在 firestore 数据库中创建用户。我不想将所有详细信息放在一个文档中,因为它会被大量请求,并且即使不需要,也会检索所有详细信息。所以我决定创建一个集合members_full,其中包含我可能不经常需要的用户的所有详细信息,以及另一个名为members_header 的集合以保留一些最重要的细节。在创建新用户时,我希望两个集合中的参考 ID 对于特定用户来说是相同的。

- members_full -+
                |
                + --- abnGMbre --- +
                                   |
                                   + --- mother : 'His mom'
                                   + --- Father:  'daddy'

- members_header+
                |   
                + ---- abnGMbre -- +
                                   |
                                   + ---- fullname: 'john Doe'
                                   + ---- pictURL: 'path to his profile pic'

我想要看起来像上面的东西。 所以这就是我在云函数中所做的:

/** Create / Update a member
 * ------------------------- */
exports.updateMember = functions.https.onCall( (data, context) =>{
  // root member and secretaries are allowed to update members
  const authParams:any = {
    uid:      context.auth.uid,
    email:    context.auth.token.email,
  };
  // Check if user is allowed to perform operation
  return checkPermission(authParams, ['root', 'secretary']).then(res => {
    if(res==false){
      return { // Permission denied
        status: STATUS.permission_denied,
      }
    }
    // set object to add/ update
    const member:any = data;
    // Check if uid of member object is present (true:update, false: create)
    var fullRef : admin.firestore.DocumentReference;
    var headRef : admin.firestore.DocumentReference;
    var countRef: admin.firestore.DocumentReference;
    var createNewMember  = false;
    if(member.uid!==undefined && member.uid!==null){ // update
      fullRef  = fsDB.collection('members_full').doc(member.uid);
      headRef  = fsDB.collection('members_header').doc(member.uid);
    } else {
      fullRef  = fsDB.collection('members_full').doc();
      headRef  = fsDB.collection('members_header').doc(fullRef.id);
      countRef = fsDB.collection('counters').doc('members');
      createNewMember  = true;
    }

    return fsDB.runTransaction(t => {
      return t.get(fullRef).then(doc => {
        // Update full details
        t.set(fullRef, {
          surname     : member.surname     ,
          firstName   : member.firstName   ,
          birthDate   : member.birthDate   ,
          birthPlace  : member.birthPlace  ,
          email       : member.email       ,
          phone       : member.phone       ,
          occupation  : member.occupation  ,
          father      : member.father      ,
          mother      : member.mother      ,
          spouse      : member.spouse      ,
          children    : member.children    ,
          addressHome : member.addressHome ,
          addressLocal: member.addressLocal,
          contactHome : member.contactHome ,
          contactLocal: member.contactLocal,
          comment     : member.comment     ,
          regDate     : member.regDate     ,
        });
        // Update header details
        t.set(headRef, {
          fullName    : member.fullName    ,
          gender      : member.gender      ,
          active      : member.active      ,
          picURL      : member.picURL      ,
        });
        // Increment number of members
        if(createNewMember ){
          t.update(countRef, {count: admin.firestore.FieldValue.increment(1)});
        }

      }).then(() => {
        return { status : STATUS.ok  }
      }).catch(err => {
        return {
          status: STATUS.fail,
          message: err.message,
          error: err
        }
      });
    }).then(() => {
      return { status : STATUS.ok  }
    }).catch(error =>{
      return {
        status: STATUS.fail,
        message: error.message,
        debug:  'run transaction err',
        error: error
      }
    });
  }).catch(err => {
    return {
      status:   STATUS.fail,
      message:  err.message,
      debug:  'check permission err',
      error:    err
    }
  });
});


/** Check if authenticated user's roles are among the ones allowed 
 * --------------------------------------------------------------- */
function checkPermission(authParams:any, allowedRoles:any[]):Promise<boolean>{
  // Check if authenticated user as any of the roles in array 'allowedRoles'
  return new Promise((resolve, reject) => {
    // If one of allowed roles is root, check against global variables
    if(allowedRoles.indexOf('root')>=0 && 
        ( root_auth.email.localeCompare(authParams.email)==0 || 
          root_auth.uid.localeCompare(authParams.uid)==0)){
      resolve(true);
    }
    // Get autID
    const uid = authParams.uid;
    // Get corresponding user in collection roles
    admin.firestore().collection('userRoles').doc(uid).get().then(snap => {
      // Get roles of user and compare against all roles in array 'allowedRoles'
      const memRoles  = snap.data().roles;
      var   found     = false;
      var   zz        = memRoles.length;
      for(let z=0; z<zz; z++){
        if(allowedRoles.indexOf(memRoles[z])){
          found = true;
          break;
        }
      }
      resolve(found);
    }).catch(err => {
      reject(err);
    });
  });
}

当我调用这个云函数时,它只写入文档members_full,并增加成员数。它不会在members_header 中创建条目。 我的第一个问题:我哪里出错了?我从第一个文档中获取 ID 以创建第二个文档的方式,是否有效?

第二个问题,创建子集合比创建两个集合更好吗?如果是,我该如何在交易中做到这一点?

帮助非常感谢

【问题讨论】:

    标签: firebase ionic3 google-cloud-firestore google-cloud-functions


    【解决方案1】:

    您需要在事务中链接方法调用。在文档中不是很清楚,但是如果您查看事务的参考文档(https://firebase.google.com/docs/reference/node/firebase.firestore.Transaction),您会看到update()set() 方法返回一个事务,即 “事务实例。[并且]用于链接方法调用”。

    因此,您应该按照以下方式调整您的代码:

            return fsDB.runTransaction(t => {
                return t.get(fullRef)
                .then(doc => {
                    t.set(fullRef, {
                         surname     : member.surname     ,
                         firstName   : member.firstName  
                         //....   
                    })
                    .set(headRef, {
                         //....
                         gender      : member.gender   
                         //....
                    })
                    .update(countRef, {count: admin.firestore.FieldValue.increment(1)});
                });
            });
    

    您还需要正确链接所有不同的 Promise,如下所示:

    return checkPermission(authParams, ['root', 'secretary'])
    .then(res => {
        //...
        return fsDB.runTransaction(t => {
          //.....
        });
    .then(t => {
        return { status : STATUS.ok  }
    })
    .catch(error => {...})
    

    但是,您可以使用batched write 代替事务,因为您似乎没有在事务中使用t.get(fullRef) 返回的文档。


    对于您的第二个问题,恕我直言,没有理由使用子集合而不是两个(根)集合。

    【讨论】:

    • 嗨! @renaud-tarnec。非常感谢您的回答。不幸的是,在尝试了您提出的两种方法之后,没有任何效果!没有任何东西被写入或更新。我仍在审查我的代码,看看我在修改它时是否遗漏了什么......
    • 您能否修改您的问题以添加云函数的整个代码(包括触发器部分,而不仅仅是内部代码)。
    • 我修改了问题以包含完整的功能
    • 看看更新的答案。实际上,您似乎应该更好地使用批量写入。 (另请注意,在我的答案的第一个版本中,链接事务写入方法的第一种方法是错误的!)
    • 您好,再次感谢。我的代码实际上并不算太糟糕,但遗漏了一些东西。而你的要好得多。实际上,在使用您的解决方案后,它仍然无法正常工作,因为我错误地删除了发送给函数的对象中的一个字段。非常感谢!
    猜你喜欢
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    相关资源
    最近更新 更多