更新 Firestore 中所有文档中的映射字段
Update a map field in all documents in Firestore
我正在 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;
}
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 用于事务和批次。
我正在 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;
}
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 用于事务和批次。