MongoDB ilişkileri: gömme veya referans?


524

İlişkisel veritabanı arka planından gelen MongoDB için yeniyim. Bazı yorumlarla soru yapısı tasarlamak istiyorum, ancak yorumlar için hangi ilişkiyi kullanacağımı bilmiyorum: embedveya reference?

Stackoverflow gibi bazı yorumların yer aldığı bir soru aşağıdaki gibi bir yapıya sahip olacaktır:

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

İlk başta, gömülü yorumları kullanmak istiyorum ( embedMongoDB'de tavsiye edilir), şöyle:

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

Açıktır, ancak bu durumdan endişe duyuyorum: Belirli bir yorumu düzenlemek istersem, içeriğini ve sorusunu nasıl alabilirim? Hiçbir yoktur _idbana birini bulmasını sağlamak için, ne de question_refbana onun sorusunu bulmasını sağlamak için. (Ben çok acemi, bunu _idve olmadan yapmanın bir yolu olup olmadığını bilmiyorum question_ref.)

Ben kullanmak zorunda mı refdeğil embed? O zaman yorumlar için yeni bir koleksiyon oluşturmak zorunda mıyım?


Alanı oluştursanız da oluşturmasanız da, tüm Moğol nesneleri _ID ile oluşturulur. Dolayısıyla teknik olarak her yorumun bir kimliği olacaktır.
Robbie Guilfoyle

25
@RobbieGuilfoyle doğru değil
pennstatephil

13
Düzeltilmiş duruyorum, teşekkürler @pennstatephil :)
Robbie

4
Belki de anlamı, bu tekdüzen nesnelerin bu çerçeveyi kullananlar için bir _id ile oluşturulmasıdır - bkz. Tek renkli alt dokümanlar
Luca Steeb

1
Mongo db ilişkilerini öğrenmek için çok iyi bir kitap "MongoDB Uygulamalı Tasarım Desenleri - O'Reilly" dir. Birinci bölüm, bu karar hakkında konuşmak, yerleştirmek veya referans vermek için mi?
Felipe Toledo

Yanıtlar:


769

Bu bir bilimden çok bir sanattır. Şemaları üzerinde Mongo Dokümantasyon iyi referanstır, ama burada dikkate alınması gereken bazı noktalar şunlardır:

  • Mümkün olduğunca içeri koyun

    Belge veritabanının neşesi, pek çok Birleştirmeyi ortadan kaldırmasıdır. İlk içgüdünüz mümkün olduğunca tek bir belgeye yerleştirmek olmalıdır. MongoDB belgelerinin yapısı olduğundan ve bu yapı içinde etkin bir şekilde sorgulayabildiğiniz için (bu, ihtiyacınız olan belgenin bir kısmını alabileceğiniz anlamına gelir, bu nedenle belge boyutu sizi çok endişelendirmemelidir) gibi verileri normalleştirmeye hemen gerek yoktur. SQL'de olurdu. Özellikle, ana dokümanı dışında faydalı olmayan tüm veriler aynı dokümanın bir parçası olmalıdır.

  • Birden fazla yerden atıfta bulunulabilen verileri kendi koleksiyonuna ayırın.

    Bu, bir "veri tutarlılığı" sorunu olduğu için bir "depolama alanı" sorunu değildir. Birçok kayıt aynı verilere atıfta bulunacaksa, tek bir kaydı güncellemek ve başka yerlerde referansları tutmak daha verimli ve daha az hataya açıktır.

  • Belge boyutu hakkında önemli noktalar

    MongoDB, tek bir belgeye 4 MB (1,8 ile 16 MB) boyut sınırı uygular. GB veri dünyasında bu kulağa küçük geliyor, ancak aynı zamanda 30 bin tweet veya 250 tipik Yığın Taşması yanıtı veya 20 titreşimsiz fotoğraf. Öte yandan, bu, tipik bir web sayfasında bir kerede sunmak isteyebileceğinden çok daha fazla bilgi. Öncelikle sorgularınızı neyin daha kolay hale getireceğini düşünün. Çoğu durumda belge boyutlarıyla ilgili endişeler erken optimizasyon olacaktır.

  • Karmaşık veri yapıları:

    MongoDB, keyfi derin iç içe veri yapılarını depolayabilir, ancak bunları verimli bir şekilde arayamaz. Verileriniz bir ağaç, orman veya grafik oluşturuyorsa, her düğümü ve kenarlarını ayrı bir belgede etkili bir şekilde saklamanız gerekir. (Bu tür veriler için özel olarak düşünülmesi gereken veri depoları olduğunu unutmayın)

    Ayrıca, bir belgedeki öğelerin bir alt kümesinin döndürülmesinin imkansız olduğuna işaret edilmiştir . Her belgenin birkaç bitini seçmeniz ve seçmeniz gerekiyorsa, bunları ayırmak daha kolay olacaktır.

  • Veri tutarlılığı

    MongoDB verimlilik ve tutarlılık arasında bir denge kurar. Kural tek bir belgede yapılan değişiklikler her zaman atomikken, birden çok belgeye yapılan güncellemelerin hiçbir zaman atomik olduğu varsayılmamalıdır. Sunucudaki bir kaydı "kilitlemenin" de bir yolu yoktur (örneğin "kilit" alanı kullanarak bunu müşterinin mantığına oluşturabilirsiniz). Şemanızı tasarlarken verilerinizi nasıl tutarlı tutacağınızı düşünün. Genellikle, bir belgede ne kadar çok tutarsanız o kadar iyidir.

Açıkladığınız şey için, yorumları katıştırır ve her yoruma bir ObjectID ile bir kimlik alanı veririm. ObjectID içinde gömülü bir zaman damgası vardır, böylece isterseniz adresindeki yerine kullanmak yerine bunu kullanabilirsiniz.


1
OP sorusuna eklemek istiyorum: Yorum modelim kullanıcı adını ve avatarına bağlantı içeriyor. Bir kullanıcının adını / avatarını değiştirebileceğini düşünerek en iyi yaklaşım nedir?
user1102018

5
'Karmaşık veri yapıları' ile ilgili olarak, birleştirme çerçevesini kullanarak bir belgedeki öğelerin bir alt kümesini döndürmek mümkündür ($ unwind deneyin).
Eyal Roth

4
Errr, Bu teknik 2012 başında MongoDB'de ya ihtimalsiz ya da yaygın olarak bilinmiyordu. Bu sorunun popülerliği göz önüne alındığında, kendi güncellenmiş cevabınızı yazmanızı tavsiye ederim. Korkarım MongoDB'deki aktif gelişimden uzaklaştım ve orijinal yazımdaki yorumunuzu ele almak için iyi bir pozisyonda değilim.
John F. Miller

54
16MB = 30 milyon tweet? ths menas tweet başına yaklaşık 0,5 bayt ?!
Paolo

8
Evet, 1000 katına çıkmışım gibi görünüyor ve bazı insanlar bunu önemli buluyor. Gönderiyi düzenleyeceğim. WRT 560bytes tweet, 2011'de bunu kullandığımda twitter hala kısa mesajlara ve Ruby 1.4 dizelerine bağlıydı; başka bir deyişle, hala sadece ASCII karakterleri.
John F. Miller


29

Belirli bir yorumu düzenlemek istersem, içeriğini ve sorusunu nasıl alabilirim?

Sen alt belge ile sorgulayabilir: db.question.find({'comments.content' : 'xxx'}).

Bu, tüm Soru belgesini döndürecektir. Belirtilen yorumu düzenlemek için, istemcideki yorumu bulmanız, düzenlemeyi yapmanız ve bunu tekrar DB'ye kaydetmeniz gerekir.

Genel olarak, belgeniz bir dizi nesne içeriyorsa, bu alt nesnelerin istemci tarafında değiştirilmesi gerektiğini görürsünüz.


4
iki yorum aynı içeriğe sahipse bu çalışmaz. arama sorgusuna yazar da ekleyebileceğimizi iddia edebiliriz, ancak yazar aynı içerikle iki özdeş yorum yaparsa yine de işe yaramaz
Steel Brain

@SteelBrain: Yorum dizinini tutsaydı, nokta gösterimi yardımcı olabilir. bkz. stackoverflow.com/a/33284416/1587329
serv-inc

13
Bu cevabın nasıl 34 oy aldığını anlamıyorum, ikinci çoklu kişi tüm sistemin kıracağı aynı şeyi yorumlar. Bu kesinlikle korkunç bir tasarım ve asla kullanılmamalıdır. @ Kullanıcı yaptığı yol gitmek için bir yoldur
user2073973

21

Biraz geç kaldım ama yine de şema oluşturma yolumu paylaşmak istiyorum.

Klasik OOP'ta yaptığınız gibi, bir kelimeyle tanımlanabilecek her şey için şemalarım var.

ÖRNEĞİN

  • Yorum Yap
  • hesap
  • kullanıcı
  • Blog yazısı
  • ...

Her şema Belge veya Alt Belge olarak kaydedilebilir, bu yüzden bunu her şema için beyan ederim.

Belge:

  • Referans olarak kullanılabilir. (Örneğin, kullanıcı bir yorum yaptı -> yorumda "tarafından yapılan" bir referans var)
  • Uygulamada bir "Kök" dir. (Örneğin, blog yazısı -> blog yazısı hakkında bir sayfa var)

alt belge:

  • Sadece bir kez kullanılabilir / asla referans değildir. (Ör. Yorum blog defterine kaydedilir)
  • Asla uygulamada bir "Kök" değildir. (Yorum sadece blog yazısı sayfasında görünür, ancak sayfa hala blog yazısı ile ilgilidir)

20

Bu soruyu kendi başıma araştırırken bu küçük sunumla karşılaştım. Ne kadar iyi düzenlendiği, hem bilgi hem de sunumu şaşırdı.

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

Özetledi:

Genel bir kural olarak, çok sayıda alt öğeniz varsa veya büyükse, ayrı bir koleksiyon en iyisi olabilir.

Daha küçük ve / veya daha az belge gömme için doğal bir uyum sağlama eğilimindedir.


11
Ne kadar a lot? 3? 10? 100? Nedir large? 1kb? 1MB? 3 alan? 20 alan? Nedir smaller/ fewer?
Traxo

1
Bu iyi bir soru ve benim için özel bir cevabım yok. Aynı sunumda, "Tüm gömülü belgeleri ve dizileri içeren bir belge 16 MB'ı aşamaz" diyen bir slayt içeriyordu, böylece bu sizin kesintiniz olabilir veya sadece özel durumunuz için makul / rahat görünen şeyle devam edebilir. Mevcut projemde, gömülü belgelerin çoğunluğu 1: ​​1 ilişkiler veya gömülü belgelerin gerçekten basit olduğu 1: ​​birçok ilişki içindir.
Chris Bloom

Ayrıca, bir eşik için belirli sayılar sunmasa da, kararınızı yönlendirmenize yardımcı olacak bazı ek işaretler içeren @ john-f-miller'in şu anki en iyi yorumuna bakın.
Chris Bloom

16

Bu oldukça eski olduğunu biliyorum ama OP sadece belirtilen yorum dönmek için nasıl soruya cevap arıyorsanız, $ (sorgu) operatörü şöyle kullanabilirsiniz :

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

4
iki yorum aynı içeriğe sahipse bu çalışmaz. arama sorgusuna yazar da ekleyebileceğimizi iddia edebiliriz, ancak yazar aynı içerikle iki özdeş yorum yaparsa yine de işe yaramaz
Steel Brain

1
@SteelBrain: İyi oynandı efendim, iyi oynadı.
JakeStrang

12

Evet, tıpkı başka bir belge doldurmak belgesindeki daha başvuru kullanabilirsiniz sql i joins.In Mongo db sahip oldukları daha dont biz kullanabileceği çok ilişki document.Instead eşleme birine katılır olarak doldurulur bizim senaryoyu yerine getirmek ..

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

Nüfus, belgedeki belirtilen yolları otomatik olarak diğer koleksiyonlardan belge (ler) ile değiştirme işlemidir. Tek bir belgeyi, birden çok belgeyi, düz nesneyi, birden çok düz nesneyi veya sorgudan döndürülen tüm nesneleri doldurabiliriz. Bazı örneklere bakalım.

Daha fazla bilgi almak için lütfen şu adresi ziyaret edin: http://mongoosejs.com/docs/populate.html


5
Firavun faresi, doldurulmuş her alan için ayrı bir talepte bulunacaktır. Bu, sunucuda gerçekleştirildiklerinden SQL JOINS'ten farklıdır. Bu, uygulama sunucusu ile mongodb sunucusu arasındaki ekstra trafiği içerir. Yine, optimizasyon yaparken bunu düşünebilirsiniz. Yine de, yanıtlayanınız hala doğrudur.
Maksimum

6

Aslında, neden kimsenin UML spesifikasyonları hakkında konuşmadığını merak ediyorum. Temel kural, bir topluluğunuz varsa referanslar kullanmanızdır. Ancak bir kompozisyonsa, bağlantı daha güçlüdür ve gömülü belgeler kullanmalısınız.

Ve bunun neden mantıklı olduğunu çabucak anlayacaksınız. Bir nesne üst öğeden bağımsız olarak mevcutsa, üst öğe olmasa bile ona erişmek istersiniz. Mevcut olmayan bir ebeveyne gömemeyeceğiniz için, kendi veri yapısında canlı hale getirmeniz gerekir. Ve bir üst öğe varsa, üst öğeye nesnenin bir ref'sini ekleyerek bunları birbirine bağlamanız yeterlidir.

İki ilişki arasındaki farkın ne olduğunu gerçekten bilmiyor musunuz? İşte onları açıklayan bir link: Toplama ve Kompozisyon UML'de


Neden -1? Lütfen sebebini açıklığa kavuşturacak bir açıklama yapın
Bonjour123 16


1

Belirli bir yorumu düzenlemek istersem, içeriğini ve sorusunu nasıl alabilirim?

Yorum sayısını ve değiştirmek istediğiniz yorumun dizinini takip ettiyseniz , nokta operatörünü kullanabilirsiniz ( SO örneği ).

Sen f.ex yapabilirsin.

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

(sorunun içindeki yorumları düzenlemenin başka bir yolu olarak)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.