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

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

问题描述

我正在 Angular 6 和库 angularfire2 中开发 Web 应用程序.我有两个集合,teamsusers.如果更改了团队名称,则该团队的名称也必须在用户文档中进行更改.

导出接口用户{名字:字符串;电子邮件地址:字符串;团队:TeamId;}导出接口 UserId 扩展 User {id:字符串;}导出接口团队{名称:字符串;缩写?:字符串;}导出接口 TeamId 扩展 Team {id:字符串;}

类:

const 团队:团队 = {name: '洋基队',缩写:'YKS'};this.teamService.updateTeam('kbdf673bksbdj', team).then(async res => {//名字变了如果('洋基队'!== team.name){等待 this.teamService.updateTeamNameInOthers('kbdf673bksbdj', team.name);}}, 错误 =>{});

来自服务:

private teamCollection: AngularFirestoreCollection;团队:Observable;构造函数(私人 afs:AngularFirestore) {this.teamsCollection = afs.collection('teams');this.teams = this.teamsCollection.snapshotChanges().pipe(地图(动作 => 动作.地图(a => {const data = a.payload.doc.data() 作为团队;const id = a.payload.doc.id;返回 {id, ...数据};})));}updateTeamNameInOthers(id: string, newName: string) {this.userService.getUsers().subscribe(users => {users.forEach(用户 => {if (user.team.id === id) {this.afs.collection('users').doc(user.id).set({team: {name: newName}}, {merge: true});//我试过了//.update({team: {name: newName}});}});});}

我已经尝试过(交易):

updateTeamNameInOthers(id: string, newName: string) {this.userService.getUsers().subscribe(users => {users.forEach(用户 => {if (user.team.id === id) {const userRef = this.afs.collection(config.collection_users).doc(user.id).ref;this.afs.firestore.runTransaction(transaction => {事务.get(userRef).then(userDoc => {transaction.update(userRef, {team: {name: newname}}, {merge: true});});});}});});}

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

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

解决方案

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

如果您的数据库结构是:

团队/团队用户/用户{团队:{(不作为子集合)id:字符串;}}

然后您必须像您一样查询所有用户.不同之处在于您需要使用 事务 来执行如果出现错误,则全部操作或根本不操作.然后,您将使用事务来执行数据库操作,例如从链接的答案 db.firestore.runTransaction(transaction =>transaction.X 中截取的 X 是您的数据库方法.

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

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

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

//服务方法getData(value: string): Promise{返回 this.afs.collection('收藏',参考 =>ref.where('值', '==', 值)).snapshotChanges().管道(采取(1),).承诺();}//更新方法 1 事务const 数据:FancyType[] = await this.service.getData();如果(数据){等待 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, 你好: 'World'});});资源();});});}//替代更新方法 2 批次const 数据:FancyType[] = await this.service.getData();const batch = this.afs.firestore.batch();如果(数据){data.forEach(d => {const dataRef = this.afs.collection('collection').doc(d.id).ref;batch.update(dataRef, {...d, 你好:'世界'});});批处理提交().then(res => {//成功}).catch(err => {//错误});}

这里是交易和批次的参考.>

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

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

From the service:

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

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

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.

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.

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.

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

Here is a reference for transactions and batches.

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

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