我目前正在使用带有Cordova CLI 7.1.0的Ionic CLI 3.19(@ ionic-app-script 3.1.4)
我目前面临的问题是,每当相关数据从其他地方发生变化时,我应该同时更新朋友节点值。我想用一些截图来澄清我的目标,以使其更清晰。
从下图中可以看出,每个子节点都包含一个用户数组,该用户数组的用户ID为friends节点的键。我作为一个数组存储的原因是因为每个用户可能有很多朋友。 在这个例子中,Jeff Kim有一个朋友,John Doe反之亦然。
当用户节点中的数据由于某种原因发生变化时,我希望在friends节点中的相关数据也希望它们也被更新。
例如,当Jeff Kim更改了他的个人资料照片或statusMessage时,所有与朋友节点相同的uid都需要根据用户更改的内容进行更新。
用户-service.ts
constructor(private afAuth: AngularFireAuth, private afDB: AngularFireDatabase,){
this.afAuth.authState.do(user => {
this.authState = user;
if (user) {
this.updateOnConnect();
this.updateOnDisconnect();
}
}).subscribe();
}
sendFriendRequest(recipient: string, sender: User) {
let senderInfo = {
uid: sender.uid,
displayName: sender.displayName,
photoURL: sender.photoURL,
statusMessage: sender.statusMessage,
currentActiveStatus: sender.currentActiveStatus,
username: sender.username,
email: sender.email,
timestamp: Date.now(),
message: 'wants to be friend with you.'
}
return new Promise((resolve, reject) => {
this.afDB.list(`friend-requests/${recipient}`).push(senderInfo).then(() => {
resolve({'status': true, 'message': 'Friend request has sent.'});
}, error => reject({'status': false, 'message': error}));
});
}
fetchFriendRequest() {
return this.afDB.list(`friend-requests/${this.currentUserId}`).valueChanges();
}
acceptFriendRequest(sender: User, user: User) {
let acceptedUserInfo = {
uid: sender.uid,
displayName: sender.displayName,
photoURL: sender.photoURL,
statusMessage: sender.statusMessage,
currentActiveStatus: sender.currentActiveStatus,
username: sender.username,
email: sender.email
}
this.afDB.list(`friends/${sender.uid}`).push(user);
this.afDB.list(`friends/${this.currentUserId}`).push(acceptedUserI
this.removeCompletedFriendRequest(sender.uid);
}
根据我刚刚观看过的clip,看起来我做了一个名为Denormalization
的事情,解决方案可能正在使用Multi-path updates
来更改数据的一致性。 Data consistency with Multi-path updates。但是,完全理解并开始编写一些代码有点棘手。
我已经做过某种练习,以确保在多个位置更新数据,而无需两次调用.update方法。
// I have changed updateUsername method from the code A to code B
// Code A
updateUsername(username: string) {
let data = {};
data[username] = this.currentUserId;
this.afDB.object(`users/${this.currentUserId}`).update({'username': username});
this.afDB.object(`usernames`).update(data);
}
// Code B
updateUsername(username: string) {
const ref = firebase.database().ref();
let updateUsername = {};
updateUsername[`usernames/${username}`] = this.currentUserId;
updateUsername[`users/${this.currentUserId}/username`] = username;
ref.update(updateUsername);
}
我并不是说这是一个完美的代码。但是我已经尝试自己解决这个问题了,而且到目前为止我已经完成了这件事。
假设我目前以杰夫身份登录。
当我运行此代码时,所有与朋友节点中的Jeff关联的数据都会发生变化,并且用户节点中的Jeff数据会同时更新。
代码需要由其他firebase专家进行改进,并且还应该在真实的测试代码上进行测试。
根据以下thread,once('value'
(一般来说,使用Firebase获得最佳性能的好主意)。 我应该找出为什么这很糟糕。
friend.ts
getFriendList() {
const subscription = this.userService.getMyFriendList().subscribe((users: any) => {
users.map(u => {
this.userService.testMultiPathStatusMessageUpdate({uid: u.uid, statusMessage: 'Learning Firebase:)'});
});
this.friends = users;
console.log("FRIEND LIST@", users);
});
this.subscription.add(subscription);
}
用户-service.ts
testMultiPathStatusMessageUpdate({uid, statusMessage}) {
if (uid === null || uid === undefined)
return;
const rootRef = firebase.database().ref();
const query = rootRef.child(`friends/${uid}`).orderByChild('uid').equalTo(this.currentUserId);
return query.once('value').then(snapshot => {
let key = Object.keys(snapshot.val());
let updates = {};
console.log("key:", key);
key.forEach(key => {
console.log("checking..", key);
updates[`friends/${uid}/${key}/statusMessage`] = statusMessage;
});
updates[`users/${this.currentUserId}/statusMessage`] = statusMessage;
return rootRef.update(updates);
});
}
以下代码在将状态更新为在线但未脱机时正常工作。
我认为这不是正确的方法。
updateOnConnect() {
return this.afDB.object('.info/connected').valueChanges()
.do(connected => {
let status = connected ? 'online' : 'offline'
this.updateCurrentUserActiveStatusTo(status)
this.testMultiPathStatusUpdate(status)
})
.subscribe()
}
updateOnDisconnect() {
firebase.database().ref().child(`users/${this.currentUserId}`)
.onDisconnect()
.update({currentActiveStatus: 'offline'});
this.testMultiPathStatusUpdate('offline');
}
private statusUpdate(uid, status) {
if (uid === null || uid === undefined)
return;
let rootRef = firebase.database().ref();
let query = rootRef.child(`friends/${uid}`).orderByChild('uid').equalTo(this.currentUserId);
return query.once('value').then(snapshot => {
let key = Object.keys(snapshot.val());
let updates = {};
key.forEach(key => {
console.log("checking..", key);
console.log("STATUS:", status);
updates[`friends/${uid}/${key}/currentActiveStatus`] = status;
});
return rootRef.update(updates);
});
}
testMultiPathStatusUpdate(status: string) {
this.afDB.list(`friends/${this.currentUserId}`).valueChanges()
.subscribe((users: any) => {
users.map(u => {
console.log("service U", u.uid);
this.statusUpdate(u.uid, status);
})
})
}
它确实在控制台中显示offline
,但更改未显示在Firebase数据库中。
有没有人可以帮助我? :(
答案 0 :(得分:2)
我认为你正确地做了这种非规范化,你的多路径更新是正确的方向。但假设有几个用户可以有几个朋友,我会错过朋友们的循环。表。
您应该拥有表格users
,friends
和userFriend
。最后一个表就像是在friends
中找到用户的快捷方式,在这个表格中你需要迭代每个朋友以找到需要更新的用户。
我在first_app_example [angular 4 + firebase]中采用了不同的方法。我从客户端删除了该进程,并通过Cloud函数中的onUpdate()将其添加到服务器中。
在code bellow用户更改其名称时,云功能会在用户已编写的每个评论中执行并更新名称。在我的情况下,客户端不知道非规范化。
//Executed when user.name changes
exports.changeUserNameEvent = functions.database.ref('/users/{userID}/name').onUpdate(event =>{
let eventSnapshot = event.data;
let userID = event.params.userID;
let newValue = eventSnapshot.val();
let previousValue = eventSnapshot.previous.exists() ? eventSnapshot.previous.val() : '';
console.log(`[changeUserNameEvent] ${userID} |from: ${previousValue} to: ${newValue}`);
let userReviews = eventSnapshot.ref.root.child(`/users/${userID}/reviews/`);
let updateTask = userReviews.once('value', snap => {
let reviewIDs = Object.keys(snap.val());
let updates = {};
reviewIDs.forEach(key => { // <---- note that I loop in review. You should loop in your userFriend table
updates[`/reviews/${key}/ownerName`] = newValue;
});
return eventSnapshot.ref.root.update(updates);
});
return updateTask;
});
修改强>
问:我是否正确构建了朋友节点
我更喜欢仅复制(非规范化)我需要的信息。遵循这个想法,你应该复制&#39; userName&#39;和&#39; photoURL&#39;例如。你可以远离所有的朋友。信息分两步:
let friends: string[];
for each friend in usrService.getFriend(userID)
friends.push(usrService.getUser(friend))
问:你的意思是我应该创建一个Lookup表吗?
你问题中提到的clip,David East给了我们一个如何反规范化的例子。原来他有users
和events
。在非规范化中,他创建了一个类似于vlookup的eventAttendees(就像你很伤心)。
问:你能举个例子吗?
不确定。我删除了一些用户的信息,并添加了一个额外的字段friendshipTypes
users
xxsxaxacdadID1
currentActiveStatus: online
email: zinzzkak@gmail.com
gender: Male
displayName: Jeff Kim
photoURL: https://firebase....
...
trteretteteeID2
currentActiveStatus: online
email: hahehahaheha@gmail.com
gender: Male
displayName: Joeh Doe
photoURL: https://firebase....
...
friends
xxsxaxacdadID1
trteretteteeID2
friendshipTypes: bestFriend //<--- extra information
displayName: Jeff Kim
photoURL: https://firebase....
trteretteteeID2
xxsxaxacdadID1
friendshipTypes: justAfriend //<--- extra information
displayName: John Doe
photoURL: https://firebase....
userfriends
xxsxaxacdadID1
trteretteteeID2: true
hgjkhgkhgjhgID3: true
trteretteteeID2
trteretteteeID2: true