Câu hỏi hay. Tôi biết ba cách tiếp cận này, mà tôi sẽ liệt kê dưới đây.
Tôi sẽ lấy một ví dụ hơi khác cho ví dụ này, chủ yếu là vì nó cho phép tôi sử dụng các thuật ngữ cụ thể hơn trong phần giải thích.
Giả sử chúng tôi có một ứng dụng trò chuyện, nơi chúng tôi lưu trữ hai thực thể: tin nhắn và người dùng. Trong màn hình hiển thị thông báo, chúng tôi cũng hiển thị tên người dùng. Vì vậy, để giảm thiểu số lần đọc, chúng tôi cũng lưu trữ tên người dùng với từng tin nhắn trò chuyện.
users
so:209103
name: "Frank van Puffelen"
location: "San Francisco, CA"
questionCount: 12
so:3648524
name: "legolandbridge"
location: "London, Prague, Barcelona"
questionCount: 4
messages
-Jabhsay3487
message: "How to write denormalized data in Firebase"
user: so:3648524
username: "legolandbridge"
-Jabhsay3591
message: "Great question."
user: so:209103
username: "Frank van Puffelen"
-Jabhsay3595
message: "I know of three approaches, which I'll list below."
user: so:209103
username: "Frank van Puffelen"
Vì vậy, chúng tôi lưu trữ bản sao chính tiểu sử của người dùng trong nút users
. Trong thông báo, chúng tôi lưu trữ uid
(vì vậy: 209103 và như vậy: 3648524) để chúng tôi có thể tra cứu người dùng. Nhưng chúng tôi cũng lưu trữ tên của người dùng trong thư để chúng tôi không phải tìm kiếm điều này cho từng người dùng khi chúng tôi muốn hiển thị danh sách thư.
Vì vậy, bây giờ điều gì xảy ra khi tôi truy cập trang Tiểu sử trên dịch vụ trò chuyện và đổi tên của tôi từ "Frank van Puffelen" thành "puf".
Cập nhật giao dịch
Thực hiện cập nhật giao dịch là bản cập nhật có thể xuất hiện trong hầu hết các nhà phát triển. Chúng tôi luôn muốn có username
trong tin nhắn để khớp với name
trong cấu hình tương ứng.
Sử dụng đa viết (gia tăng đối với 20.150.925)
Kể từ khi căn cứ hỏa lực 2.3 (cho JavaScript) và 2.4 (dành cho Android và iOS), bạn có thể đạt được thông tin cập nhật nguyên tử khá dễ dàng bằng cách sử dụng một bản cập nhật đa duy nhất:
function renameUser(ref, uid, name) {
var updates = {}; // all paths to be updated and their new values
updates['users/'+uid+'/name'] = name;
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
updates['messages/'+messageSnapshot.key()+'/username'] = name;
})
ref.update(updates);
});
}
Điều này sẽ gửi một lệnh cập nhật tới Firebase cập nhật tên người dùng trong tiểu sử của họ và trong mỗi thư.
trước cách tiếp cận nguyên tử
Vì vậy, khi sự thay đổi người sử dụng là name
trong hồ sơ của họ:
var ref = new Firebase('https://mychat.firebaseio.com/');
var uid = "so:209103";
var nameInProfileRef = ref.child('users').child(uid).child('name');
nameInProfileRef.transaction(function(currentName) {
return "puf";
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('Transaction aborted by our code.');
} else {
console.log('Name updated in profile, now update it in the messages');
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.on('child_added', function(messageSnapshot) {
messageSnapshot.ref().update({ username: "puf" });
});
}
console.log("Wilma's data: ", snapshot.val());
}, false /* don't apply the change locally */);
Khá liên quan và người đọc tinh ý sẽ nhận thấy rằng tôi lừa gạt trong việc xử lý các thông điệp. Cheat đầu tiên là tôi không bao giờ gọi off
cho người nghe, nhưng tôi cũng không sử dụng một giao dịch.
Nếu chúng ta muốn một cách an toàn làm kiểu này hoạt động từ khách hàng, chúng tôi cần:
quy tắc an ninh
- đảm bảo các tên trong cả hai trận đấu nơi. Nhưng các quy tắc cần cho phép đủ linh hoạt để chúng tạm thời khác nhau trong khi chúng tôi thay đổi tên. Vì vậy, điều này biến thành một chương trình cam kết hai pha khá đau đớn.
- thay đổi tất cả
username
lĩnh vực cho các tin nhắn bằng so:209103
để null
(một số giá trị kỳ diệu)
- thay đổi các
name
về thành viên so:209103
để 'PUF'
- thay đổi các
username
trong mỗi tin nhắn của so:209103
đó là null
để puf
.
- truy vấn đó yêu cầu
and
trong hai điều kiện mà truy vấn Firebase không hỗ trợ. Vì vậy, chúng tôi sẽ kết thúc với một tài sản bổ sung uid_plus_name
(với giá trị so:209103_puf
) mà chúng tôi có thể truy vấn.
- mã phía máy khách xử lý tất cả các chuyển tiếp này một cách giao dịch.
Cách tiếp cận này làm tôi đau đầu. Và thường có nghĩa là tôi đang làm điều gì sai. Nhưng ngay cả khi đó là cách tiếp cận đúng đắn, với một cái đầu làm tôi đau đớn hơn thì tôi có nhiều khả năng mắc lỗi mã hóa hơn. Vì vậy, tôi thích tìm một giải pháp đơn giản hơn.
nhất cuối cùng
Cập nhật (20.150.925): căn cứ hỏa lực phát hành một tính năng cho phép ghi nguyên tử để nhiều đường. Điều này hoạt động tương tự như cách tiếp cận bên dưới, nhưng với một lệnh duy nhất. Xem phần cập nhật ở trên để đọc cách hoạt động của phần này.
Cách tiếp cận thứ hai phụ thuộc vào việc chia nhỏ hành động của người dùng ("Tôi muốn đổi tên thành 'puf'") từ các tác động của hành động đó ("Chúng tôi cần cập nhật tên trong tiểu sử như vậy: 209103 và trong mọi thư có user = so:209103
)
tôi muốn xử lý các đổi tên trong một kịch bản mà chúng tôi chạy trên một máy chủ phương pháp chính sẽ là một cái gì đó như thế này:..
function renameUser(ref, uid, name) {
ref.child('users').child(uid).update({ name: name });
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
messageSnapshot.update({ username: name });
})
});
}
một lần nữa tôi mất một vài phím tắt ở đây, chẳng hạn như sử dụng once('value'
(nói chung là một ý tưởng tồi để có hiệu suất tối ưu với Firebase). e chi phí không có tất cả dữ liệu được cập nhật hoàn toàn cùng một lúc. Nhưng cuối cùng các tin nhắn sẽ được cập nhật để phù hợp với giá trị mới.
Không chăm sóc
Cách tiếp cận thứ ba là đơn giản nhất của tất cả: trong nhiều trường hợp bạn không thực sự cần phải cập nhật các dữ liệu trùng lặp ở tất cả. Trong ví dụ chúng tôi đã sử dụng ở đây, bạn có thể nói rằng mỗi tin nhắn đã ghi lại tên như tôi đã sử dụng vào thời điểm đó. Tôi đã không thay đổi tên của tôi cho đến bây giờ, do đó, nó có ý nghĩa rằng tin nhắn cũ cho thấy tên tôi được sử dụng tại thời điểm đó. Điều này áp dụng trong nhiều trường hợp trong đó dữ liệu thứ cấp là giao dịch trong tự nhiên. Nó không áp dụng ở khắp mọi nơi tất nhiên, nhưng nơi mà nó áp dụng "không quan tâm" là cách tiếp cận đơn giản nhất của tất cả.
Tóm tắt
Trong khi ở trên là những mô tả chỉ rộng như thế nào bạn có thể giải quyết vấn đề này và họ chắc chắn chưa hoàn chỉnh, tôi thấy rằng mỗi khi tôi cần phải quạt ra dữ liệu trùng lặp nó trở lại với một trong những cơ bản phương pháp tiếp cận.
* Tôi sẽ viết một câu trả lời thích hợp bên dưới. * Câu đầu tiên đó làm tôi hạnh phúc. Bạn có thể lặp lại nó tại bữa tiệc sinh nhật không? Đặc biệt là khi bạn bè của dev có mặt. :-) –
điều này dường như có liên quan, cũng .. https://stackoverflow.com/questions/47334382 – Fattie