gpt4 book ai didi

Firebase 非规范化数据一致性问题

转载 作者:行者123 更新时间:2023-12-01 03:17:59 25 4
gpt4 key购买 nike

我目前正在使用 Ionic CLI 3.19 和 Cordova CLI 7.1.0 (@ionic-app-script 3.1.4)

我目前面临的问题是,每次从其他地方更改相关数据时,我都应该同时更新 friend 节点值。我想通过一些屏幕截图来阐明我的目标,以使其更加清晰。

从下图中可以看出,每个子节点都由一个用户数组组成,该数组具有一个用户 ID 作为 friend 节点的键。我之所以存储为数组,是因为每个用户可以有很多 friend 。
在这个例子中,Jeff Kim 有一个 friend ,John Doe,反之亦然。

friends node image

当用户节点中的数据由于某种原因发生更改时,我希望 friend 节点中的相关数据也希望它们也被更新。

例如,当 Jeff Kim 更改他的个人资料照片或 statusMessage 时,所有与 Jeff Kim 的 uid 匹配的 friend 节点中的所有相同 uid 都需要根据用户的更改进行更新。

users node image

用户服务.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 的身份登录。

当我运行此代码时, friend 节点中与杰夫的所有关联数据都会更改,并且用户节点中杰夫的数据也会同时更新。

该代码需要由其他 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);
}

用户服务.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);
})
})
}

enter image description here

它确实显示 offline在控制台中,但更改不会出现在 Firebase 数据库中。

有没有人可以帮助我? :(

最佳答案

我认为您进行这种非规范化是正确的,并且您的多路径更新方向正确。但是假设几个用户可以有几个 friend ,我会错过 friend 表中的一个循环。

你应该有 table users , friends和一个 userFriend .最后一个表就像在friends中查找用户的快捷方式,没有它你需要迭代每个 friend 来找到需要更新的用户。

我在我的 first_app_example [angular 4 + firebase] 中做了一个不同的方法。我从客户端删除了该进程,并通过云函数中的 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;
});

编辑

Q: I structured friends node correctly or not



我更喜欢仅复制(非规范化)我更经常需要的信息。按照这个想法,您应该只复制 'userName' 和 'photoURL' 例如。您可以分两步访问所有好友的信息:
 let friends: string[];
for each friend in usrService.getFriend(userID)
friends.push(usrService.getUser(friend))

Q: you mean I should create a Lookup table?



clip在您的问题中提到,David East 给了我们一个如何非规范化的例子。原来他有 usersevents .在非规范化中,他创建了 eventAttendees,就像一个 vlookup(就像你悲伤一样)。

Q: Could you please give me an example?



当然。我删除了一些用户的信息并添加了一个额外的字段 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

关于Firebase 非规范化数据一致性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47732420/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com