2015-06-07 13 views
24

में denormalized डेटा कैसे लिखना है मैंने Stucturing Data पर फायरबेस डॉक्स पढ़ा है। डेटा भंडारण सस्ता है, लेकिन उपयोगकर्ता का समय नहीं है। हमें संचालन के लिए अनुकूलित करना चाहिए, और कई स्थानों पर लिखना चाहिए।फायरबेस

तो फिर मैं एक सूची नोड और एक सूची सूचकांक नोड दोनों के बीच कुछ डुप्लिकेट किए गए डेटा के साथ बहुत कम से कम सूची नाम पर संग्रहीत कर सकता है,,।

मैं ईएस 6 का उपयोग कर रहा हूं और एसिंक प्रवाह को संभालने के लिए अपने जावास्क्रिप्ट ऐप में वादा करता हूं, मुख्य रूप से पहले डेटा पुश के बाद फायरबेस से एक रेफ कुंजी लाने के लिए।

let addIndexPromise = new Promise((resolve, reject) => { 
    let newRef = ref.child('list-index').push(newItem); 
    resolve(newRef.key()); // ignore reject() for brevity 
}); 
addIndexPromise.then(key => { 
    ref.child('list').child(key).set(newItem); 
}); 

कैसे मुझे यकीन है कि डेटा सभी स्थानों में सिंक में रहता है, केवल ग्राहक पर मेरे ऐप जानते हुए भी चलाता है कैसे करूं?

मानसिक स्वास्थ्य की जांच के लिए, मैं अपने वादे में एक setTimeout की स्थापना की और अपने ब्राउज़र बंद होने से पहले ही हल हो गई, और वास्तव में मेरी डेटाबेस नहीं रह अनुरूप था, एक अतिरिक्त सूचकांक एक इसी सूची बिना बचाया साथ।

कोई सलाह?

+3

* मैं नीचे एक उचित उत्तर लिखूंगा। * वह पहला वाक्य मुझे खुश करता है। क्या आप इसे जन्मदिन की पार्टियों पर दोहरा सकते हैं? खासकर जब देव दोस्त मौजूद हैं। :-) –

+0

यह प्रासंगिक लगता है, भी .. https://stackoverflow.com/questions/47334382 – Fattie

उत्तर

47

शानदार सवाल। मैं इसके तीन दृष्टिकोणों के बारे में जानता हूं, जिन्हें मैं नीचे सूचीबद्ध करूंगा।

मैं इसके लिए थोड़ा अलग उदाहरण लेगा, अधिकतर क्योंकि यह मुझे स्पष्टीकरण में अधिक ठोस शब्दों का उपयोग करने की अनुमति देता है।

कहें कि हमारे पास चैट एप्लिकेशन है, जहां हम दो इकाइयों को स्टोर करते हैं: संदेश और उपयोगकर्ता। स्क्रीन में जहां हम संदेश दिखाते हैं, हम उपयोगकर्ता का नाम भी दिखाते हैं। इसलिए पढ़ने की संख्या को कम करने के लिए, हम प्रत्येक चैट संदेश के साथ उपयोगकर्ता का नाम भी स्टोर करते हैं।

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" 

तो हम users नोड में उपयोगकर्ता के प्रोफ़ाइल के प्राथमिक प्रतिलिपि संग्रहीत। संदेश में हम uid (इसलिए: 20 9 103 और इसलिए: 3648524) स्टोर करते हैं ताकि हम उपयोगकर्ता को देख सकें। लेकिन हम भी संदेशों में उपयोगकर्ता का नाम संग्रहीत करते हैं, ताकि जब हम संदेशों की सूची प्रदर्शित करना चाहते हैं तो हमें प्रत्येक उपयोगकर्ता के लिए इसे देखना नहीं है।

तो अब जब मैं चैट सेवा पर प्रोफाइल पेज पर जाता हूं और "फ्रैंक वैन पफेलन" से अपना नाम बदलता हूं तो बस "पुफ" में बदल जाता है।

लेन-देन संबंधी अद्यतन

एक लेन-देन संबंधी अद्यतन प्रदर्शन एक है कि शायद शुरू में सबसे डेवलपर्स के मन में पॉप है। हम हमेशा संबंधित प्रोफाइल में name से मेल खाने के लिए संदेशों में username संदेश चाहते हैं।

बहुपथ का उपयोग करते हुए लिखते हैं (20,150,925 पर जोड़ा)

Firebase 2.3 (JavaScript) और 2.4 (Android और iOS के लिए), आप एक ही बहुपथ अद्यतन का उपयोग करके काफी आसानी से परमाणु अपडेट प्राप्त कर सकते हैं के बाद से:

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); 
    }); 
} 

यह फ़ायरबेस में एक एकल अपडेट कमांड भेजेगा जो उपयोगकर्ता के नाम को उनके प्रोफ़ाइल में और प्रत्येक संदेश में अपडेट करेगा।

पिछला परमाणु दृष्टिकोण

तो जब उपयोगकर्ता परिवर्तन उनके प्रोफ़ाइल में name है:

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 */); 

सुंदर शामिल और चतुर पाठक है कि मैं संदेशों की हैंडलिंग में धोखा देखेंगे। पहली धोखा यह है कि श्रोता के लिए मैंने कभी भी off पर कॉल नहीं किया, लेकिन मैं लेनदेन का भी उपयोग नहीं करता हूं।

हम सुरक्षित रूप से ग्राहक से आपरेशन के इस प्रकार करना चाहते हैं, तो हम आवश्यकता होगी:

  1. सुरक्षा नियम है कि दोनों स्थानों मैच में नाम हैं। लेकिन नियमों को बदलते समय नियमों को अस्थायी रूप से भिन्न होने के लिए पर्याप्त लचीलापन की अनुमति देने की आवश्यकता है। तो यह एक बहुत दर्दनाक दो चरण प्रतिबद्ध योजना में बदल जाता है।
    1. परिवर्तन so:209103null करने से संदेशों के लिए सभी username क्षेत्रों (कुछ जादू मूल्य)
    2. परिवर्तन so:209103 कि है puf को null द्वारा 'PUF'
    3. परिवर्तन username हर संदेश में करने के लिए उपयोगकर्ता so:209103 की name
    4. उस क्वेरी को दो स्थितियों में से and की आवश्यकता होती है, जो Firebase क्वेरी का समर्थन नहीं करते हैं। तो हम एक अतिरिक्त संपत्ति uid_plus_name (मूल्य so:209103_puf के साथ) के साथ समाप्त हो जाएंगे, जिसे हम पूछ सकते हैं।
  2. क्लाइंट-साइड कोड जो इन सभी संक्रमणों को लेनदेन से संभालता है।

इस प्रकार का दृष्टिकोण मेरे सिर को चोट पहुंचाता है। और आमतौर पर इसका मतलब है कि मैं कुछ गलत कर रहा हूं। लेकिन यहां तक ​​कि यदि यह सही दृष्टिकोण है, तो सिर के साथ दर्द होता है, मैं कोडिंग गलतियों को करने की अधिक संभावना रखता हूं। इसलिए मैं एक सरल समाधान की तलाश करना पसंद करता हूं।

अंततः स्थिरता

अद्यतन (20,150,925): Firebase अनेक पथों को परमाणु राईट अनुमति देने के लिए एक सुविधा का विमोचन किया। यह नीचे दृष्टिकोण के समान काम करता है, लेकिन एक ही कमांड के साथ। यह कैसे काम करता है पढ़ने के लिए उपरोक्त अद्यतन अनुभाग देखें। 209,103 और हर संदेश में:

दूसरा दृष्टिकोण उपयोगकर्ता कार्रवाई ("मैं 'PUF' के लिए मेरे नाम बदलना चाहते हैं") है कि कार्रवाई के निहितार्थ से बंटवारे पर निर्भर करता है ("हम प्रोफ़ाइल इसलिए में नाम को अपडेट करना कि user = so:209103 है)

मैं एक स्क्रिप्ट है कि हम एक सर्वर पर चलने में नाम बदलने को संभालने था मुख्य विधि कुछ इस तरह होगा:।।

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 }); 
    }) 
    }); 
} 

एक बार फिर मैं यहाँ कुछ शॉर्टकट ले, जैसे कि once('value' (जो आमतौर पर फायरबेस के साथ इष्टतम प्रदर्शन के लिए एक बुरा विचार है) का उपयोग करना। लेकिन कुल मिलाकर दृष्टिकोण सरल है, वें एक ही समय में सभी डेटा पूरी तरह अद्यतन नहीं होने की लागत। लेकिन आखिर में संदेशों को नए मूल्य से मेल खाने के लिए अपडेट किया जाएगा।

सभी का सबसे सरल

देखभाल नहीं तीसरे दृष्टिकोण है: कई मामलों में आप वास्तव में डुप्लिकेट किए गए डेटा को अद्यतन करने की जरूरत नहीं है। उदाहरण में हमने यहां उपयोग किया है, आप कह सकते हैं कि प्रत्येक संदेश ने उस नाम को रिकॉर्ड किया जैसा मैंने उस समय उपयोग किया था। मैंने अभी तक अपना नाम नहीं बदला है, इसलिए यह समझ में आता है कि पुराने संदेश उस समय दिखाए गए नाम को दिखाते हैं। यह कई मामलों में लागू होता है जहां द्वितीयक डेटा प्रकृति में लेनदेन होता है। यह हर जगह लागू नहीं होता है, लेकिन जहां यह "देखभाल नहीं" लागू होता है, वह सभी का सबसे आसान तरीका है।

सारांश

जबकि ऊपर कैसे आप इस समस्या को हल कर सकता है की सिर्फ व्यापक वर्णन दिए गए हैं और वे निश्चित रूप से पूरा नहीं कर रहे हैं, मुझे लगता है कि हर बार जब मैं डुप्लिकेट डेटा बाहर प्रशंसक की जरूरत है यह इन बुनियादी से एक के लिए वापस आता है दृष्टिकोण।

+1

महान जवाब के लिए फ्रैंक धन्यवाद! दरअसल, मैं 'देखभाल नहीं' दृष्टिकोण के साथ गया था। मैंने अपने लेखन ऑप्स को स्विच किया था, इसलिए आइटम इंडेक्स से पहले आता है, इसलिए मुझे सूची में कोई आइटम रखने का जोखिम नहीं है जो कहीं भी डेटा के साथ लिंक नहीं करता है (लेकिन अब मैं अनाथ सूची आइटम के साथ समाप्त हो सकता हूं, जो कि नहीं है अतिमहत्वपूर्ण)। – legolandbridge

+0

इस तरह का एक बड़ा जवाब। आपके द्वारा उल्लिखित सलाह बहुत उपयोगी है और केवल कई परिदृश्यों पर लागू होती है, न केवल फायरबेस। –

+1

सावधान रहें कि सर्वर कार्य को * अंतिम स्थिरता * में देरी करना सुनिश्चित करें और जांचें कि नाम अभी तक बदला नहीं गया है। – AJcodez

2

फ्रैंक में महान उत्तर जोड़ने के लिए, मैंने Firebase Cloud Functions के सेट के साथ अंतिम स्थिरता दृष्टिकोण लागू किया। जब भी कोई प्राथमिक मान (जैसे उपयोगकर्ता नाम) बदल जाता है, तो फ़ंक्शंस ट्रिगर हो जाते हैं, और उसके बाद denormalized फ़ील्ड में परिवर्तन प्रसारित करते हैं।

यह लेनदेन जितना तेज़ नहीं है, लेकिन कई मामलों के लिए इसे होने की आवश्यकता नहीं है।

+1

ग्रेट एडिफे यूएफएफ। क्लाउड फ़ंक्शंस उस के लिए बहुत अच्छे हैं, जब तक आपके पास कड़े रीयलटाइम या ऑफ़लाइन आवश्यकताएं न हों। –