更新Firestore中所有文档中的地图字段 [英] Update a map field in all documents in Firestore

查看:93
本文介绍了更新Firestore中所有文档中的地图字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm doing a web application in Angular 6 and the library angularfire2. I have two collections, teams and users. If a team's name is changed, the team's name must also be changed inside of the user's document.

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

我已经尝试过(交易):

I have tried (transaction):

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的所有属性,则不会更新用户的文档.我不确定这是否是最好的方法.

You can update any property of Team right now but, if you want to change all the properties of the team at the same time, the user's document is not updated. I'm not sure if this is the best way to do it anyway.

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

My goal is, if the team's name changed, change the team's name in all user's document who belongs to that team.

推荐答案

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

there are a lot of ways you can do this. I don't recommend making these changes from the front end.

如果您的数据库结构是:

If your database structure is:

teams/team

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

然后,您必须像查询自己一样查询所有用户.区别在于,您需要使用交易来执行如果有错误,则全部或全部不进行操作.然后,您将使用事务执行从链接的答案 db.firestore.runTransaction(transaction => transaction.X 处摘录的此类数据库操作,其中X是您的数据库方法.

Then you'll have to query all of the users like you are. The difference is that you'll want to use a transaction to perform the operations all together or not at all if there is an error. Then you'll use the transaction to do the DB operations like this snipped from the linked answer db.firestore.runTransaction(transaction => and transaction.X where X is your DB method.

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

Alternatively, I recommend doing this kind of functionality with Cloud Functions. With this, you can listen to changes on the record without having to rely on the client side to make the changes. If the user makes a valid change, the Cloud Functions can do the transactional changes instead of performing the changes unreliably on the client side.

编辑后1 您应该将 this.afs.firestore.runTransaction(transaction => {行移到 users.forEach(user => {同样的交易.这样,如果其中一个更新出现错误,则所有更新都不会更新.

After edit 1 You should move the this.afs.firestore.runTransaction(transaction => { line above the users.forEach(user => { so all of the operations can share the same transaction. This way if there is an error with one of the updates, none of them will update.

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

After chat The final solution was to use async/await with firestore.runTransaction, return a promise, update the records with transaction.update, resolving the promise after. A second part of the solution was to ensure you are not subscribed to the collection you are updating within the 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
    });
}

此处是交易和批次的参考.

这篇关于更新Firestore中所有文档中的地图字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆