使用相同的参考ID在不同的Firestore集合中创建文档

时间:2019-05-20 08:13:32

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

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

我正在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来创建第二个文档的方式,这不合法吗?

第二个问题,创建子集合比拥有两个集合会更好吗?如果是,我该怎么做?

非常感谢

1 个答案:

答案 0 :(得分:0)

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

因此,您应该按照以下方式修改代码:

        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)返回的文档。


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