更新 Firestore 中所有文档中的映射字段

Update a map field in all documents in Firestore

我正在 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;
}

Class:

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

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

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

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

如果你的数据库结构是:

teams/team

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

然后您将不得不像您一样查询所有用户。不同之处在于,如果出现错误,您将希望使用 transaction 一起执行所有操作或根本不执行操作。然后,您将使用事务来执行从链接答案 [​​=12=] 和 transaction.X 中截取的数据库操作,其中 X 是您的数据库方法。

或者,我建议用Cloud Functions来实现这种功能。有了这个,您就可以在不依赖客户端进行更改的情况下收听记录中的更改。如果用户进行了有效更改,云功能可以进行事务性更改,而不是在客户端不可靠地执行更改。

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

聊天后 最终的解决方案是使用 async/await 和 firestore.runTransaction,return 一个承诺,用 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 用于事务和批次。