MongoDB Çoktan çoğa Derneği


143

MongoDB ile nasıl çoktan çoğa ilişki kurarsınız?

Örneğin; Diyelim bir Kullanıcılar tablonuz ve Roller tablonuz var. Kullanıcıların birçok rolü vardır ve rollerin de birçok kullanıcısı vardır. SQL arazisinde bir UserRoles tablosu oluşturacaksınız.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

MongoDB'de aynı ilişki nasıl ele alınır?


Yanıtlar:


96

Sorgu gereksinimlerinize bağlı olarak her şeyi kullanıcı belgesine koyabilirsiniz:

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

Tüm Mühendisleri edinmek için şunu kullanın:

db.things.find( { roles : "Engineer" } );

Rolleri ayrı belgelerde korumak istiyorsanız, belgenin _id değerini roles dizisine ad yerine ekleyebilirsiniz:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

ve rolleri şu şekilde ayarlayın:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}

7
İkincisi daha iyi olurdu çünkü mevcut tüm rollerin bir listesini almam gerekiyor. Tek kötü kısmı, derneğin iki ucunu da kurmam gerekiyor. SQL yolunu yaparken, bir UserRole eklemek Kullanıcının Rolü ve Rolü Kullanıcı hakkında bilgilendirir. Bu şekilde, Rolü Kullanıcı'da ve Kullanıcıyı Rolde ayarlamam gerekir. Sanırım bu iyi.
Josh Close

46
Bir veritabanı sql'ı desteklemediği için, referansların yararlı araçlar olmadığı anlamına gelmez NoSQL! = NoReference şu açıklamaya bakın: mongodb.org/display/DOCS/Schema+Design
Tom Gruner

8
Bu iyi bir fikir gibi görünmüyor. Sadece altı rolünüz varsa, elbette, ama 20000 nesneye bağlanabilecek 20000 nesneniz varsa (çok sayıda ilişkide)? MongoDB belgeleri bile değişebilir, devasa referans dizilerinden kaçınmanız gerektiğini ima ediyor. docs.mongodb.org/manual/tutorial/…
CaptSaltyJack

Açıkçası çok sayıda nesne ile çoktan çoğa ilişkilerde farklı bir çözüm kullanmak istiyorsunuz (dokümanlardaki yayıncı / kitap örneği gibi). Bu durumda iyi çalışır ve yalnızca ayrı kullanıcı rolü belgeleri oluşturursanız işleri karmaşık hale getirir.
loseerikh

1
Bu, çoğu sistem için işe yarar coz rolleri genellikle küçük bir settir ve genellikle bir kullanıcı alır ve daha sonra rollerine bakarız. Peki ya roller büyükse? ya da sizden bana == "Mühendis" rolü olan kullanıcıların bir listesini vermenizi istersem ne olur? Artık tüm kullanıcı koleksiyonunuzu sorgulamak zorundasınız (mühendis rolü olmayan tüm kullanıcıları ziyaret ederek) sadece bu tür milyonlarca kullanıcı arasında bu role sahip olabilecek 2 veya 3 kullanıcı elde etmek için. Ayrı bir masa veya koleksiyon çok daha iyidir.
programcı

32

RDBMS'lerdeki uzun yıllara dayanan tecrübemize göre modellemeye çalışmak yerine, atomik saygıyı göz önünde bulundurarak, okunan kullanım durumlarını optimize ederek MongoDB, Redis ve diğer NoSQL veri depolarını kullanarak belge deposu çözümlerini modellemenin çok daha kolay olduğunu gördüm. yazma kullanım durumları tarafından desteklenmesi gereken yazma işlemleri.

Örneğin, "Roller'deki Kullanıcılar" alanının kullanımı şu şekildedir:

  1. Rol - Oluşturma, Okuma, Güncelleme, Silme, Kullanıcıları Listeleme, Kullanıcı Ekleme, Kullanıcıyı Kaldırma, Tüm Kullanıcıları Temizle, Kullanıcı Dizini veya benzeri "Destek Rolü" (kapsayıcı + kendi meta verileri gibi işlemler).
  2. Kullanıcı - Oluşturma, Okuma, Güncelleme, Silme (bağımsız bir varlık gibi CRUD işlemleri)

Bu, aşağıdaki belge şablonları olarak modellenebilir:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

User varlığındaki Rolle ilgili özellikler gibi yüksek frekanslı kullanımları desteklemek için, User.Roles kasıtlı olarak denormalize edilir, hem Kullanıcı'da hem de yinelenen depolama alanına sahip olan Rollerde saklanır.

Metinde kolayca görünmüyorsa, ancak belge depolarını kullanırken teşvik edilen düşünce türüdür.

Umarım bu, operasyonların okunan tarafı ile arasındaki boşluğu kapatmaya yardımcı olur.

Yazma tarafı için teşvik edilen şey atomik yazımlara göre modellemektir. Örneğin, belge yapıları bir kilit edinmeyi, bir belgeyi, daha sonra başka bir belgeyi ve muhtemelen daha fazla belgeyi güncellemeyi ve sonra kilidi serbest bırakmayı gerektiriyorsa, muhtemelen model başarısız olmuştur. Dağıtılmış kilitler yapabilmemiz, bunları kullanmamız gerektiği anlamına gelmez.

Roldeki Kullanıcı modeli söz konusu olduğunda, atomik yazma kilitlerinden kaçınmamızı genişleten işlemler, bir Kullanıcıyı bir Rolden eklemek veya kaldırmaktır. Her iki durumda da, başarılı bir işlem, hem tek bir Kullanıcı'nın hem de tek bir Rol belgesinin güncellenmesine neden olur. Bir şey başarısız olursa, temizlik yapmak kolaydır. Bu, İş Birimi şablonunun belge havuzlarının kullanıldığı yerlerde oldukça fazla ortaya çıkmasının nedenlerinden biridir.

Kilitlerin atomik yazma kaçınmasını gerçekten uzatan işlem bir Rolü temizler ve bu da birçok Kullanıcı güncelleştirmesinin Role.name öğesini User.roles dizisinden kaldırmasına neden olur. O zaman bu açık operasyon genellikle önerilmez, ancak gerekirse operasyonlar sipariş edilerek uygulanabilir:

  1. Kullanıcı adları listesini Role.users adresinden alın.
  2. Kullanıcı adlarını 1. adımdan itibaren yineleyin, rol adını User.roles öğesinden kaldırın.
  3. Rol kullanıcılarını temizleyin.

Adım 2'de meydana gelmesi muhtemel olan bir sorun durumunda, geri yükleme veya devam etmek için adım 1'deki aynı kullanıcı adı kümesinin kullanılabilmesi nedeniyle geri alma kolaydır.


15

Bu soru üzerine tökezledim ve eski bir soru olmasına rağmen, verilen cevaplarda belirtilmeyen birkaç olasılık eklemenin yararlı olacağını düşündüm. Ayrıca, son birkaç yılda işler biraz ilerledi, bu nedenle SQL ve NoSQL'in birbirine yaklaştıklarını vurgulamakta fayda var.

Yorumculardan biri, “veriler ilişkisel ise ilişkisel kullanın” konusunda akıllıca uyarıcı tavır getirdi. Bununla birlikte, bu yorum sadece şemaların her zaman uygulamadan önce geldiği ilişkisel dünyada mantıklıdır.

İLİŞKİ DÜNYA: Yapı verileri> Onu almak için uygulama yaz
NOSQL WORLD: Tasarım uygulaması> Buna göre yapı verileri

Veriler ilişkisel olsa bile, NoSQL hala bir seçenektir. Örneğin, bire çok ilişkiler hiç sorun değil ve MongoDB belgelerinde geniş yer kaplıyor

2010 SORUNUNA 2015 ÇÖZÜMÜ

Bu soru gönderildiği için, noSQL'i SQL'e yaklaştırmaya yönelik ciddi girişimler olmuştur. California Üniversitesi'nde (San Diego) Yannis Papakonstantinou liderliğindeki ekip , yakında burada açıklanan gibi kalıcı sorunların çözümü olabilecek bir SQL ++ uygulaması olan FORWARD üzerinde çalışıyor .

Daha pratik bir seviyede, Couchbase 4.0'ın piyasaya sürülmesi, ilk kez NoSQL'de yerel JOIN'ler yapabileceğiniz anlamına geliyordu. Kendi N1QL'lerini kullanıyorlar. Bu bir örnektir JOINonların arasından öğreticiler :

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QL, toplama, filtreleme vb.Dahil olmak üzere tüm SQL işlemlerine olmasa da çoğuna izin verir.

YENİ OLMAYAN HİBRİT ÇÖZÜM

MongoDB hala tek seçenekse, uygulamanın veri yapısı üzerinde önceliğe sahip olması gerektiğine geri dönmek istiyorum. Yanıtların hiçbiri, sorgulanan verilerin çoğunun belgeye / nesneye gömüldüğü ve az sayıda vaka için referansların tutulduğu hibrit yerleştirmeden bahsetmez.

Örnek: Bilgi (rol adı dışında) bekleyebilir mi? Kullanıcının henüz ihtiyaç duymadığı bir şey istemeyerek uygulamanın önyüklemesi daha hızlı olabilir mi?

Bu, kullanıcının oturum açması ve ait olduğu tüm roller için tüm seçenekleri görmesi gerektiğinde olabilir. Ancak, kullanıcı bir "Mühendis" dir ve bu rol için seçenekler nadiren kullanılır. Bu, uygulamanın yalnızca bir mühendisin üzerine tıklamak istemesi durumunda seçeneklerini göstermesi gerektiği anlamına gelir.

Bu, uygulamaya başlangıçta (1) kullanıcının ait olduğu rolleri ve (2) belirli bir role bağlı bir olay hakkında nereden bilgi alınacağını bildiren bir belge ile gerçekleştirilebilir.

   {_id: ObjectID(),
    roles: [[“Engineer”, ObjectId()”],
            [“Administrator”, ObjectId()”]]
   }

Daha da iyisi, roller koleksiyonundaki role.name alanını dizine eklediğinizde, ObjectID () öğesini de yerleştirmeniz gerekmeyebilir.

Başka bir örnek: TÜM roller hakkında bilgi TÜM zamanlar isteniyor mu?

Kullanıcının kontrol panelinde oturum açması ve zamanın% 90'ının “Mühendis” rolüne bağlı görevleri gerçekleştirmesi de olabilir. Karma yerleştirme tam olarak bu rol için yapılabilir ve yalnızca geri kalanı için referansları tutabilir.

{_id: ObjectID(),
  roles: [{name: Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, ObjectId()”]
         ]
}

Şematik olmak sadece NoSQL'in bir özelliği değildir, bu durumda bir avantaj olabilir. Bir kullanıcı nesnesinin "Roller" özelliğinde farklı türdeki nesneleri yuvalamak mükemmel bir şekilde geçerlidir.


5

çalışan ve şirket varlık nesnesi olduğunda aşağıdaki şemayı kullanmayı deneyin:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}
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.