我正在Angular 6和库angularfire2中创建一个Web应用程序。我有两个集合,teams
和users
。如果更改了团队的名称,则还必须在用户文档中更改团队的名称。
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的所有属性,则不会更新用户的文档。无论如何,我不确定这是否是最好的方法。
我的目标是,如果更改团队名称,请在属于该团队的所有用户文档中更改该团队的名称。
答案 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处理交易和批次。