更新Firestore中所有文档中的地图字段

时间:2019-02-21 19:10:26

标签: angular typescript firebase google-cloud-firestore angularfire2

我正在Angular 6和库angularfire2中创建一个Web应用程序。我有两个集合,teamsusers。如果更改了团队的名称,则还必须在用户文档中更改团队的名称。

export interface User {
  firstName: string;
  emailAddress: string;
  team: TeamId;
}

export interface UserId extends User {
  id: string;
}

export interface Team {
  name: string;
  abbreviation?: string;
}

export interface TeamId extends Team {
  id: string;
}

班级:

const team: Team = {
  name: 'Yankees',
  abbreviation: 'YKS'
};

this.teamService.updateTeam('kbdf673bksbdj', team)
  .then(async res => {
    // The name has changed
    if ('Yankees' !== team.name) {
      await this.teamService.updateTeamNameInOthers('kbdf673bksbdj', team.name);
    }
  }, err => {
  });

通过服务:

private teamsCollection: AngularFirestoreCollection<Team>;
teams: Observable<TeamId[]>;

constructor(
  private afs: AngularFirestore) {
  this.teamsCollection = afs.collection<Team>('teams');

  this.teams = this.teamsCollection.snapshotChanges().pipe(
    map(actions => actions.map(a => {
      const data = a.payload.doc.data() as Team;
      const id = a.payload.doc.id;
      return {id, ...data};
    }))
  );
}

updateTeamNameInOthers(id: string, newName: string) {
  this.userService.getUsers().subscribe(users => {
    users.forEach(user => {
      if (user.team.id === id) {
        this.afs.collection('users').doc(user.id)
          .set({team: {name: newName}}, {merge: true});

        // I have tried
        // .update({team: {name: newName}});
      }
    });
  });
}

我已经尝试过(交易):

updateTeamNameInOthers(id: string, newName: string) {
    this.userService.getUsers().subscribe(users => {
      users.forEach(user => {
        if (user.team.id === id) {
          const userRef = this.afs.collection(config.collection_users).doc(user.id).ref;

          this.afs.firestore.runTransaction(transaction => {
            transaction.get(userRef).then(userDoc => {
              transaction.update(userRef, {team: {name: newname}}, {merge: true});
            });
          });
        }
      });
    });
  }

您现在可以更新Team的任何属性,但是,如果要同时更改Team的所有属性,则不会更新用户的文档。无论如何,我不确定这是否是最好的方法。

我的目标是,如果更改团队名称,请在属于该团队的所有用户文档中更改该团队的名称。

1 个答案:

答案 0 :(得分:1)

有很多方法可以做到这一点。我不建议从前端进行这些更改。

如果您的数据库结构是:

teams/team

users/user {
  team: { (not as a subcollection)
    id: string;
  }
}

然后,您必须像查询自己一样查询所有用户。区别在于,如果有错误,您将要使用transaction一起执行操作,或者根本不执行。然后,您将使用事务从链接的答案db.firestore.runTransaction(transaction =>transaction.X中截取这样的数据库操作,其中X是您的数据库方法。

或者,我建议使用Cloud Functions做这种功能。这样,您可以监听记录中的更改,而不必依赖客户端进行更改。如果用户进行了有效的更改,则Cloud Functions可以进行事务性更改,而不是在客户端不可靠地执行更改。

编辑后1 您应该将this.afs.firestore.runTransaction(transaction => {行移到users.forEach(user => {上方,以便所有操作都可以共享同一事务。这样,如果其中一个更新出现错误,则不会更新。

聊天后 最终的解决方案是将async / await与firestore.runTransaction一起使用,返回承诺,并使用transaction.update更新记录,之后再解决承诺。解决方案的第二部分是确保您没有订阅更新中要更新的集合!

// service method
getData(value: string): Promise<FancyType[]> {
  return this.afs.collection<FancyType>(
    'collection',
    ref => ref.where('value', '==', value)
  ).snapshotChanges()
  .pipe(
    take(1),
  ).toPromise();
}

// update method 1 transaction
const data: FancyType[] = await this.service.getData();

if (data) {
  await this.afs.firestore.runTransaction(t => {
    return new Promise((res, rej) => {
      data.forEach(d => {
        const dataRef = this.afs.collection('collection').doc(d.id).ref;
        transaction.update(dataRef, {...d, hello: 'World'});
      });
      res();
    });
  });
}

// alternate update method 2 batch
const data: FancyType[] = await this.service.getData();
const batch = this.afs.firestore.batch();

if (data) {
  data.forEach(d => {
    const dataRef = this.afs.collection('collection').doc(d.id).ref;
    batch.update(dataRef, {...d, hello: 'World'});
  });

  batch.commit()
    .then(res => {
      //on success
    }).catch(err => {
      //on error
    });
}

Here is a reference处理交易和批次。