MongoDB संबंध: एम्बेड या संदर्भ?


524

मैं MongoDB के लिए नया हूं - एक रिलेशनल डेटाबेस पृष्ठभूमि से आ रहा हूं। मैं कुछ टिप्पणियों के साथ एक प्रश्न संरचना तैयार करना चाहता हूं, लेकिन मुझे नहीं पता कि टिप्पणियों के लिए किस संबंध का उपयोग करना है: embedया reference?

स्टैकओवरफ़्लो जैसी कुछ टिप्पणियों के साथ एक प्रश्न इस तरह की संरचना होगा:

Question
    title = 'aaa'
    content = bbb'
    comments = ???

सबसे पहले, मैं एम्बेड की गई टिप्पणियों का उपयोग करना चाहता हूं (मुझे लगता embedहै कि मोंगोबीडी में सिफारिश की गई है), इस तरह:

Question
    title = 'aaa'
    content = 'bbb'
    comments = [ { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'} ]

यह स्पष्ट है, लेकिन मैं इस मामले में चिंतित हूं: यदि मैं एक निर्दिष्ट टिप्पणी संपादित करना चाहता हूं, तो मुझे इसकी सामग्री और इसका प्रश्न कैसे मिलेगा? _idमुझे न तो कोई खोजने देना है और न ही question_refमुझे इसका प्रश्न बताने देना है। (मैं बहुत नौसिखिया हूं, कि मुझे नहीं पता कि इसके बिना _idऔर ऐसा करने का कोई तरीका है या नहीं question_ref।)

क्या मुझे उपयोग refनहीं करना है embed? फिर मुझे टिप्पणियों के लिए एक नया संग्रह बनाना होगा?


सभी मैंगो ऑब्जेक्ट एक _ID के साथ बनाए जाते हैं, चाहे आप फ़ील्ड बनाएं या नहीं। इसलिए तकनीकी रूप से प्रत्येक टिप्पणी में अभी भी एक आईडी होगी।
रोबी गुइलफॉयल

25
@RobbieGuilfoyle true-- देख नहीं stackoverflow.com/a/11263912/347455
pennstatephil

13
मैं दुरुस्त खड़ा हूं, धन्यवाद @pennstatephil :)
रॉबी

4
क्या वह शायद मतलब है कि सभी है नेवला वस्तुओं जो लोग इस ढांचे का उपयोग के लिए एक _ id के साथ बनाया जाता - देख नेवला subdocs
लुका Steeb

1
मोंगो डीबी रिश्तों को सीखने के लिए एक बहुत अच्छी किताब है "मोंगोबी एप्लाइड डिजाइन पैटर्न - ओ'रिली"। अध्याय एक, इस फैसले के बारे में बात करने के लिए, एम्बेड या संदर्भ के लिए?
फेलिप टोलेडो

जवाबों:


769

यह एक विज्ञान से अधिक एक कला है। स्कीमा पर मोंगो प्रलेखन एक अच्छा संदर्भ है, लेकिन यहाँ कुछ तथ्यों पर गौर करने के लिए कर रहे हैं:

  • जितना संभव हो उतना अंदर रखें

    डॉक्यूमेंट डेटाबेस की खुशी यह है कि यह बहुत सारे जॉइंट्स को खत्म कर देता है। आपकी पहली वृत्ति एक ही दस्तावेज में उतनी ही जगह होनी चाहिए जितनी आप कर सकते हैं। क्योंकि MongoDB दस्तावेजों में संरचना होती है, और क्योंकि आप कुशलता से उस संरचना के भीतर क्वेरी कर सकते हैं (इसका मतलब है कि आप उस दस्तावेज़ का हिस्सा ले सकते हैं जिसकी आपको ज़रूरत है, इसलिए दस्तावेज़ का आकार आपको अधिक चिंता नहीं करनी चाहिए) जैसे डेटा को सामान्य करने की तत्काल आवश्यकता नहीं है आप SQL में होंगे। विशेष रूप से कोई भी डेटा जो इसके मूल दस्तावेज़ के अलावा उपयोगी नहीं है, उसी दस्तावेज़ का हिस्सा होना चाहिए।

  • अलग-अलग डेटा जिसे कई स्थानों से अपने संग्रह में संदर्भित किया जा सकता है।

    यह इतना "संग्रहण स्थान" समस्या नहीं है क्योंकि यह "डेटा संगति" समस्या है। यदि कई रिकॉर्ड एक ही डेटा को संदर्भित करेंगे तो यह एक रिकॉर्ड को अद्यतन करने और अन्य स्थानों पर संदर्भ रखने के लिए अधिक कुशल और कम त्रुटि वाला है।

  • दस्तावेज़ का आकार विचार

    MongoDB एक एकल दस्तावेज़ पर 4MB (1.8 के साथ 16MB) आकार सीमा लगाता है। जीबी डेटा की दुनिया में यह छोटा लगता है, लेकिन यह 30 हजार ट्वीट या 250 विशिष्ट स्टैक ओवरफ्लो उत्तर या 20 झिलमिलाहट वाली तस्वीरें भी हैं। दूसरी ओर, यह एक विशिष्ट वेब पेज पर एक समय में प्रस्तुत करने की तुलना में कहीं अधिक जानकारी है। पहले विचार करें कि आपके प्रश्नों को क्या आसान बना देगा। कई मामलों में दस्तावेज़ के आकार के बारे में चिंता समय से पहले अनुकूलन होगी।

  • जटिल डेटा संरचनाएं:

    MongoDB मनमाने ढंग से गहरी नेस्टेड डेटा संरचनाओं को स्टोर कर सकता है, लेकिन उन्हें कुशलता से खोज नहीं सकता है। यदि आपका डेटा एक पेड़, जंगल या ग्राफ बनाता है, तो आपको प्रभावी रूप से प्रत्येक नोड और उसके किनारों को एक अलग दस्तावेज़ में संग्रहीत करने की आवश्यकता है। (ध्यान दें कि इस प्रकार के डेटा के लिए विशेष रूप से डिज़ाइन किए गए डेटा स्टोर हैं जिन्हें किसी को भी विचार करना चाहिए)

    यह भी इंगित किया गया है कि किसी दस्तावेज़ में तत्वों का सबसेट वापस करना असंभव है। यदि आपको प्रत्येक दस्तावेज़ के कुछ बिट्स को चुनने और चुनने की आवश्यकता है, तो उन्हें अलग करना आसान होगा।

  • डेटा संगतता

    MongoDB दक्षता और स्थिरता के बीच एक व्यापार बंद कर देता है। नियम एक एकल दस्तावेज़ में परिवर्तन हमेशा परमाणु होते हैं , जबकि कई दस्तावेज़ों के अपडेट को कभी भी परमाणु नहीं माना जाना चाहिए। सर्वर पर रिकॉर्ड को "लॉक" करने का भी कोई तरीका नहीं है (उदाहरण के लिए "लॉक" फ़ील्ड का उपयोग करके आप क्लाइंट के तर्क में इसे बना सकते हैं)। जब आप अपना स्कीमा डिज़ाइन करते हैं, तो विचार करें कि आप अपने डेटा को कैसे बनाए रखेंगे। आम तौर पर, जितना अधिक आप एक दस्तावेज़ में बेहतर रहते हैं।

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


1
मैं ओपी प्रश्न में जोड़ना चाहूंगा: मेरी टिप्पणियों के मॉडल में उपयोगकर्ता नाम और उसके अवतार का लिंक है। सबसे अच्छा तरीका क्या होगा, किसी उपयोगकर्ता को अपना नाम / अवतार संशोधित करने पर विचार करना चाहिए?
user1102018

5
'कॉम्प्लेक्स डेटा स्ट्रक्चर्स' के बारे में, ऐसा लगता है कि एकत्रीकरण ढांचे का उपयोग करते हुए किसी दस्तावेज़ में तत्वों का एक सबसेट वापस करना संभव है (कम तनावपूर्ण प्रयास करें)।
ईयाल रोथ

4
इर्र, यह तकनीक 2012 की शुरुआत में या तो व्यापक रूप से नहीं थी या मोंगोबीडी में व्यापक रूप से ज्ञात नहीं थी। इस प्रश्न की लोकप्रियता को देखते हुए, मैं आपको अपना स्वयं का अद्यतन उत्तर लिखने के लिए प्रोत्साहित करूंगा। मुझे डर है कि मैं MongoDB पर सक्रिय विकास से दूर हो गया हूं और आप मेरे मूल पोस्ट के भीतर टिप्पणी करने के लिए अच्छी स्थिति में नहीं हैं।
जॉन एफ मिलर

54
16 एमबी = 30 मिलियन ट्वीट? ths menas प्रति ट्वीट 0,5 बाइट के बारे में ?!
पाओलो

8
हां, ऐसा प्रतीत होता है कि मैं 1000 का एक कारक था और कुछ लोगों को यह महत्वपूर्ण लगता है। मैं पोस्ट को संपादित करूंगा। WRT 560bytes प्रति ट्वीट, जब मैंने 2011 में इसे रट लिया तो ट्विटर अभी भी टेक्स्ट मैसेज और रूबी 1.4 स्ट्रिंग्स से बंधा हुआ था; दूसरे शब्दों में अभी भी केवल ASCII चार्ट।
एफ पर जॉन एफ मिलर

39

सामान्य तौर पर, एम्बेडिंग अच्छा है यदि आपके पास संस्थाओं के बीच एक-से-एक या कई-कई संबंध हैं, और यदि आपके कई-कई संबंध हैं, तो संदर्भ अच्छा है।


10
क्या आप संदर्भ लिंक जोड़ सकते हैं? धन्यवाद।
db80

एक से कई के इस डिजाइन के साथ आपको एक विशिष्ट टिप्पणी कैसे मिलती है?
मॉरिशियो पास्टोरिनी


29

यदि मैं एक निर्दिष्ट टिप्पणी संपादित करना चाहता हूं, तो इसकी सामग्री और इसके प्रश्न कैसे प्राप्त करें?

आप उप-दस्तावेज़ द्वारा क्वेरी कर सकते हैं db.question.find({'comments.content' : 'xxx'}):।

यह संपूर्ण प्रश्न दस्तावेज़ लौटाएगा। निर्दिष्ट टिप्पणी को संपादित करने के लिए, आपको फिर क्लाइंट पर टिप्पणी ढूंढनी होगी, संपादन करना होगा और उस डीबी को वापस सहेजना होगा।

सामान्य तौर पर, यदि आपके दस्तावेज़ में ऑब्जेक्ट्स की एक सरणी है, तो आप पाएंगे कि उन सब-ऑब्जेक्ट्स को संशोधित क्लाइंट साइड की आवश्यकता होगी।


4
यह काम नहीं करेगा यदि दो टिप्पणियों में समान सामग्री हो। कोई यह तर्क दे सकता है कि हम खोज क्वेरी में लेखक को भी जोड़ सकते हैं, जो अभी भी काम नहीं करेगा यदि लेखक एक ही सामग्री के साथ दो समान टिप्पणियां करता है
स्टील ब्रेन

@SteelBrain: यदि उसने टिप्पणी सूचकांक रखा था, तो डॉट नोटेशन मदद कर सकता है। देखें stackoverflow.com/a/33284416/1587329
सर्व-

13
मुझे समझ नहीं आ रहा है कि इस उत्तर में 34 अपवोट्स हैं, दूसरे कई लोग एक ही टिप्पणी करते हैं कि पूरी प्रणाली टूट जाएगी। यह एक बहुत ही भयानक डिजाइन है और इसका उपयोग कभी नहीं किया जाना चाहिए।
@User

21

खैर, मैं थोड़ा लेट हो गया हूं लेकिन फिर भी स्कीमा निर्माण के अपने तरीके को साझा करना चाहूंगा।

मेरे पास हर चीज के लिए स्कीमा है जिसे एक शब्द द्वारा वर्णित किया जा सकता है, जैसे कि आप इसे शास्त्रीय ओओपी में करेंगे।

ईजी

  • टिप्पणी
  • लेखा
  • उपयोगकर्ता
  • ब्लॉग पोस्ट
  • ...

प्रत्येक स्कीमा को एक दस्तावेज़ या उपनिर्देशन के रूप में सहेजा जा सकता है, इसलिए मैं प्रत्येक स्कीमा के लिए यह घोषित करता हूं।

दस्तावेज़:

  • एक संदर्भ के रूप में इस्तेमाल किया जा सकता है। (जैसे उपयोगकर्ता ने टिप्पणी की -> टिप्पणी में "उपयोगकर्ता द्वारा संदर्भ" बनाया गया है)
  • आप आवेदन में एक "रूट" है। (जैसे ब्लॉगपोस्ट -> ब्लॉगपोस्ट के बारे में एक पेज है)

Subdocument:

  • केवल एक बार उपयोग किया जा सकता है / कभी भी संदर्भ नहीं है। (जैसे टिप्पणी ब्लॉगपोस्ट में सहेजी गई है)
  • आपके आवेदन में कभी "रूट" नहीं है। (टिप्पणी सिर्फ ब्लॉगपोस्ट पेज में दिखाई देती है लेकिन पेज अभी भी ब्लॉगपोस्ट के बारे में है)

20

मैं जबकि अपने दम पर इस सवाल का शोध इस छोटे से प्रस्तुति बारे में जाना। मुझे आश्चर्य था कि इसे कितनी अच्छी तरह से रखा गया था, इसकी जानकारी और प्रस्तुति दोनों।

http://openmymind.net/Multiple-Collections-Versus-Embedded-Documents

यह संक्षेप:

एक सामान्य नियम के रूप में, यदि आपके पास बहुत सारे [बच्चे के दस्तावेज] हैं या यदि वे बड़े हैं, तो एक अलग संग्रह सबसे अच्छा हो सकता है।

छोटे और / या कम दस्तावेज़ एम्बेड करने के लिए एक प्राकृतिक फिट होते हैं।


11
कितना है a lot? 3? 10? 100? क्या है large? 1KB? 1 एमबी? 3 फ़ील्ड? 20 क्षेत्र? क्या है smaller/ fewer?
ट्रैक्सो

1
यह एक अच्छा सवाल है, और मेरे पास एक विशिष्ट उत्तर नहीं है। उसी प्रस्तुति में एक स्लाइड शामिल थी जिसमें कहा गया था, "एक दस्तावेज, जिसमें उसके सभी एम्बेडेड दस्तावेज और सरणियां शामिल हैं, 16 एमबी से अधिक नहीं हो सकती", ताकि आपका कटऑफ हो सके, या बस वही करें जो आपकी विशिष्ट स्थिति के लिए उचित / आरामदायक लगता है। मेरी वर्तमान परियोजना में, एम्बेडेड दस्तावेज़ों में से अधिकांश 1: 1 संबंध या 1: कई हैं, जहां एम्बेडेड दस्तावेज़ वास्तव में सरल हैं।
क्रिस ब्लूम

@ John-f-miller द्वारा वर्तमान शीर्ष टिप्पणी को भी देखें, जिसमें थ्रेसहोल्ड के लिए विशिष्ट संख्या प्रदान नहीं करने पर भी कुछ अतिरिक्त बिंदु होते हैं जो आपके निर्णय को निर्देशित करने में मदद करते हैं।
क्रिस ब्लूम

16

मुझे पता है कि यह काफी पुराना है, लेकिन यदि आप ओपी के प्रश्न का उत्तर ढूंढ रहे हैं कि केवल निर्दिष्ट टिप्पणी कैसे वापस करें, तो आप इस तरह $ (क्वेरी) ऑपरेटर का उपयोग कर सकते हैं :

db.question.update({'comments.content': 'xxx'}, {'comments.$': true})

4
यह काम नहीं करेगा यदि दो टिप्पणियों में समान सामग्री हो। कोई यह तर्क दे सकता है कि हम खोज क्वेरी में लेखक को भी जोड़ सकते हैं, जो अभी भी काम नहीं करेगा यदि लेखक एक ही सामग्री के साथ दो समान टिप्पणियां करता है
स्टील ब्रेन

1
@SteelBrain: अच्छा खेला सर, अच्छा खेला।
जेकस्ट्रैंग

12

हां, हम डॉक्यूमेंट में संदर्भ का उपयोग कर सकते हैं। किसी अन्य डॉक्यूमेंट को ठीक उसी तरह से पॉप्युलेट करें जैसे कि मैं sql i जॉइन करता हूं। मोंगो db में वे एक से कई रिलेशनशिप डॉक्यूमेंट की मैपिंग में शामिल नहीं होते हैं। इसके अलावा कि हम इस परिदृश्य को पूरा करने के लिए पॉपुलेट का उपयोग कर सकते हैं ।

var mongoose = require('mongoose')
  , Schema = mongoose.Schema

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
  fans     : [{ type: Number, ref: 'Person' }]
});

जनसंख्या अन्य संग्रह (ओं) से दस्तावेज़ में निर्दिष्ट पथों को स्वचालित रूप से प्रतिस्थापित करने की प्रक्रिया है। हम एक एकल दस्तावेज़, एक से अधिक दस्तावेज़, सादे ऑब्जेक्ट, एकाधिक सादे ऑब्जेक्ट, या सभी ऑब्जेक्ट क्वेरी से वापस आ सकते हैं। आइए कुछ उदाहरण देखें।

बेहतर आप अधिक जानकारी प्राप्त कर सकते हैं कृपया देखें: http://mongoosejs.com/docs/populate.html


5
Mongoose प्रत्येक आबादी वाले क्षेत्र के लिए एक अलग अनुरोध जारी करेगा। यह SQL JOINS के लिए भिन्न है क्योंकि वे सर्वर पर किए जाते हैं। इसमें ऐप सर्वर और मोंगोडब सर्वर के बीच अतिरिक्त ट्रैफ़िक शामिल है। जब आप अनुकूलन कर रहे हों, तब आप इस पर विचार कर सकते हैं। फिर भी, आपका एवर अभी भी सही है।
मैक्स

6

वास्तव में, मैं काफी उत्सुक हूं कि किसी ने यूएमएल विनिर्देशों के बारे में क्यों नहीं कहा। अंगूठे का एक नियम यह है कि यदि आपके पास एक एकत्रीकरण है, तो आपको संदर्भों का उपयोग करना चाहिए। लेकिन अगर यह एक रचना है, तो युग्मन अधिक मजबूत है, और आपको एम्बेडेड दस्तावेजों का उपयोग करना चाहिए।

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

वास्तव में नहीं जानते कि दोनों रिश्तों में क्या अंतर है? यहां एक लिंक उन्हें समझाया गया है: यूएमएल में एकत्रीकरण बनाम रचना


क्यों -1 कृपया एक स्पष्टीकरण दें जो कारण स्पष्ट करेगा
बोंजौर १२


1

यदि मैं एक निर्दिष्ट टिप्पणी संपादित करना चाहता हूं, तो मुझे इसकी सामग्री और इसका प्रश्न कैसे मिलेगा?

यदि आपने टिप्पणियों की संख्या और उस टिप्पणी की अनुक्रमणिका पर नज़र रखी है जिसे आप बदलना चाहते हैं, तो आप डॉट ऑपरेटर ( SO उदाहरण ) का उपयोग कर सकते हैं ।

आप कर सकते हैं f.ex.

db.questions.update(
    {
        "title": "aaa"       
    }, 
    { 
        "comments.0.contents": "new text"
    }
)

(प्रश्न के अंदर टिप्पणियों को संपादित करने का एक और तरीका)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.