MongoDB'de SQL Join eşdeğerini nasıl gerçekleştirebilirim?


498

MongoDB'de SQL Join eşdeğerini nasıl gerçekleştirebilirim?

Örneğin, iki koleksiyonunuz (kullanıcılar ve yorumlar) olduğunu ve her birinin kullanıcı bilgileriyle birlikte pid = 444 ile tüm yorumları çekmek istediğinizi varsayalım.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }

users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

Belirli bir alanla ilgili tüm yorumları (örneğin ... find ({pid: 444})) ve her yorumla ilişkili kullanıcı bilgilerini tek seferde almanın bir yolu var mı?

Şu anda, ölçütlerimle eşleşen yorumları alıyorum, daha sonra bu sonuç kümesindeki tüm kullanıcı adlarını anlıyorum, kullanıcı nesnelerini alıyorum ve bunları yorum sonuçlarıyla birleştiriyorum. Yanlış yapıyormuşum gibi görünüyor.


35
MongoDB 3.2+, $ lookup adında bir birleştirme çözümü uyguladığından, bu sorunun son cevabı muhtemelen en alakalı olanıdır. Onu buraya iteceğimi düşündüm çünkü belki herkes dibe okumaz. stackoverflow.com/a/33511166/2593330
thefourtheye

6
Doğru, $ araması MongoDB 3.2'de tanıtıldı. Detaylar docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Yanıtlar:


306

Mongo 3.2'den itibaren bu sorunun cevapları çoğunlukla doğru değildir. Toplama boru hattına eklenen yeni $ lookup operatörü esasen sol dış birleşim ile aynıdır:

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_lookup

Dokümanlardan:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

Tabii ki Mongo ilişkisel bir veritabanı değildir ve geliştiriciler $ lookup için belirli kullanım durumları önermeye özen göstermektedir, ancak en azından 3.2'den itibaren MongoDB ile birleştirme yapmak artık mümkün.


@clayton: İki koleksiyondan daha fazlasına ne dersiniz?
Dipen Dedania

1
@DipenDedania, toplama pipeline'ına ek $ arama aşamaları eklemeniz yeterli.
Clayton Gulick

Sağ koleksiyondaki karşılık gelen kimliği ile sol koleksiyondaki dizideki herhangi bir alana katılamıyorum. Kimse bana yardımcı olabilir mi?
Prateek Singh

1
Bu konuda biraz kafam karıştı - sadece "from" koleksiyonunda belirli belgeleri istediğinizi belirtmek için herhangi bir yolu var mı, ya da otomatik olarak tüm db bir kerede katılıyor mu?
user3413723

En son Spring Data MongoDB'nin 3.2 için desteği olup olmadığını merak mı ediyorsunuz?
gtiwari333

142

Resmi mongodb sitesindeki bu sayfa tam olarak bu soruyu ele alıyor :

https://mongodb-documentation.readthedocs.io/en/latest/ecosystem/tutorial/model-data-for-ruby-on-rails.html

Hikaye listemizi gösterdiğimizde, hikayeyi yayınlayan kullanıcının adını göstermemiz gerekecek. İlişkisel bir veritabanı kullanıyor olsaydık, kullanıcılar ve mağazalarda birleştirme yapabilir ve tüm nesnelerimizi tek bir sorguda alabilirdik. Ancak MongoDB birleşimleri desteklemez ve bu nedenle zaman zaman biraz denormalizasyon gerektirir. Burada bu, 'kullanıcı adı' özniteliğini önbelleğe almak anlamına gelir.

İlişkisel puristler, sanki bazı evrensel yasaları ihlal ediyormuş gibi, huzursuz hissediyor olabilirler. Ancak, MongoDB koleksiyonlarının ilişkisel tablolara eşdeğer olmadığını unutmayın; her biri benzersiz bir tasarım amacına hizmet eder. Normalleştirilmiş bir tablo atomik, izole bir veri yığını sağlar. Ancak bir belge, bir nesneyi bir bütün olarak daha yakından temsil eder. Sosyal bir haber sitesi söz konusu olduğunda, bir kullanıcı adının gönderilen öyküye özgü olduğu söylenebilir.


51
@dudelgrincen normalleşme ve ilişkisel veritabanlarından paradigma değişimi. NoSQL'in amacı veritabanından çok hızlı bir şekilde okumak ve yazmaktır. BigData ile DB'lerde daha az sayıya sahip uygulama ve ön uç sunucularından oluşan gruplara sahip olacaksınız. Saniyede milyonlarca işlem yapmanız bekleniyor. Ağır kaldırmayı veritabanından boşaltın ve uygulama seviyesine koyun. Derin analize ihtiyacınız varsa, verilerinizi bir OLAP veritabanına yerleştiren bir entegrasyon işi çalıştırırsınız. Yine de OLTP dbs'nizden çok derin sorgular almamalısınız.
Snowburnt

18
@dudelgrincen Ayrıca her proje veya tasarım için olmadığını söylemeliyim. SQL tipi veritabanında çalışan bir şeyiniz varsa neden değiştiresiniz? Şemanıza noSQL ile çalışmak için masaj yapamıyorsanız, yapmayın.
Snowburnt

9
Göçler ve sürekli gelişen şemalar da NoSQL sisteminde yönetilmesi çok daha kolaydır.
justin

14
Kullanıcı web sitesinde 3.540 gönderi içeriyorsa ve profilde kullanıcı adını değiştirirse ne olur? Her gönderi yeni kullanıcı adıyla güncellenmeli mi?
Ivo Pereira

2
@IvoPereira Evet ve bu yüzden verileri bu şekilde modellemekten kaçınmalısınız. Aynı senaryoyu ve sonuçlarını açıklayan bir makale var: Neden MongoDB'yi Asla Kullanmamalısınız
Omid

138

Mongodb istemci konsolunu kullanarak tek bir koleksiyondaki tüm verileri birkaç satırda kolay bir işlevle birleştirebilir / birleştirebiliriz ve şimdi istenen sorguyu gerçekleştirebiliyoruz. Tam bir örnek altında,

.- Yazarlar:

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

.- Kategoriler:

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

.- Kitabın

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

. - Kitap ödünç verme

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

.- Sihir:

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

.- Yeni koleksiyon verilerini alın:

db.booksReloaded.find().pretty()

.- Tepki :)

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

Umarım bu çizgiler size yardımcı olabilir.


2
Aynı kod doktrin mongodb kullanarak çalıştırılabilir olup olmadığını merak ediyorum?
abbood

4
Referans nesnelerinden biri bir güncelleme aldığında ne olur? Bu güncelleme kitap nesnesine otomatik olarak yansıyor mu? Yoksa bu döngünün tekrar çalışması gerekiyor mu?
balupton

14
Verileriniz küçük olduğu sürece bu iyidir. Her bir kitap içeriğini müşterinize getirecek ve ardından her bir kategoriyi, ödünç verme ve yazarları tek tek getirecektir. Kitaplarınız binlerce olduğunda, bu gerçekten çok yavaş gidiyor. Daha iyi bir teknik muhtemelen toplama boru hattını kullanmak ve birleştirilen verileri ayrı bir koleksiyona çıkarmak olacaktır. Tekrar geri döneyim. Buna bir cevap ekleyeceğim.
Sandeep Giri

Algoritmanızı bu diğer örneğe uyarlayabilir misiniz? stackoverflow.com/q/32718079/287948
Peter Krauss

1
@SandeepGiri nasıl ayrı toplama gerçekten gerçekten yoğun veri var çünkü toplam boru hattı yapabilirim katılmak gerekir ??
Yassine Abdul-Rahman

38

Bunu tarif ettiğiniz şekilde yapmalısınız. MongoDB ilişkisel olmayan bir veritabanıdır ve birleştirmeleri desteklemez.


4
Bir sql sunucusu arka plan geliyor akıllıca performans yanlış görünüyor, ama belki bir belge db ile o kadar da kötü değil?
terjetyl

3
bir sql sunucusu arka plandan, ben de bir sonuç kümesi '(seçilen alanlar ile) bir seferde yeni bir sorgu için giriş olarak, bir çok sonuç için SQL
Stijn Sanders

1
@terjetyl Gerçekten planlamalısınız. Ön tarafta hangi alanları sunacaksınız, eğer bireysel bir görünümde sınırlı bir miktar varsa, bunları gömülü belgeler olarak alırsınız. Anahtar birleştirme yapmak zorunda değilsiniz. Derin analiz yapmak istiyorsanız, bunu başka bir veritabanında yaptıktan sonra yaparsınız. En iyi performans için verileri OLAP küpüne dönüştüren bir iş çalıştırın.
Snowburnt

4
Mongo 3.2 sürümünden sol birleşimler desteklenir.
Somnath Muluk

18

Diğerlerinin de belirttiği gibi, gerçekten yapmak istemediğiniz hiçbir ilişkisel veritabanından ilişkisel bir veritabanı oluşturmaya çalışıyorsunuz, ancak yine de, bunu yapmanız gereken bir durum varsa, burada kullanabileceğiniz bir çözüm var. İlk olarak A koleksiyonunda (veya sizin durum kullanıcılarınızda) bir foreach bulması yaparız ve sonra her öğeyi bir nesne olarak alırız, sonra ikinci koleksiyonumuza (sizin durum yorumlarınızda) bakmak için object özelliğini (sizin durumunuzda) kullanırız. bulabilir, o zaman bir eşleşiriz ve onunla bir şeyler yazdırabilir veya yapabiliriz. Umarım bu size yardımcı olur ve iyi şanslar :)

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});

Bu, şu anda döngüde olduğumuz öğeyi bulamaz mı?
Skarlinski

18

$ Lookup , $ project ve $ match öğelerinin doğru kombinasyonuyla , birden fazla parametrede birden fazla tabloya katılabilirsiniz. Bunun nedeni birden çok kez zincirlenebilmeleridir.

Aşağıdaki yapmak istediğimizi varsayalım ( referans )

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID =R.ID AND S.MID =R.MID WHERE R.TIM >0 AND 
S.MOB IS NOT NULL

1. Adım: Tüm tabloları bağlayın

İstediğiniz kadar tablo arayabilirsiniz.

$ lookup - sorgudaki her tablo için bir tane

$ unwind - veriler doğru şekilde denormalize edildiğinden, başka dizilere sarılır

Python kodu ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

2. Adım: Tüm şartları tanımlayın

$ project : burada tüm koşullu ifadeleri ve ayrıca seçmek istediğiniz tüm değişkenleri tanımlayın.

Python Kodu ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

3. Adım: Tüm koşullara katılın

$ match - VEYA veya AND vb. kullanarak tüm koşullara katılın. Bunların katları olabilir.

$ project : tüm koşulların tanımını kaldır

Python Kodu ..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},

                        # join all conditionals

                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},

                        # undefine conditionals

                        {"$project": {
                          "midEq": 0
                        }}

                        ])

Hemen hemen her türlü tablo, koşul ve birleşim bu şekilde yapılabilir.


17

İşte bir "birleştirme" örneği * Aktörler ve Filmler koleksiyonları:

https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt

.mapReduce()Yöntemi kullanır

* join - belge tabanlı veritabanlarına katılmak için bir alternatif


19
-1, Bu iki koleksiyondaki verilere katılmıyor. Etraftaki verileri döndüren tek bir koleksiyondan (aktörler) veri kullanıyor. Öyleyse anahtar olan şeyler artık değer ve değerler artık anahtar ... bir JOIN'den çok farklı.
Evan Teran

12
Tam olarak yapmanız gereken bu, MongoDB ilişkisel değil, belge odaklı. MapReduce büyük performans verileriyle (küme vb ... kullanabilirsiniz) oynamak için izin verir, ancak basit durumlarda bile, çok yararlı!
Thomas Decaux

14

3.2 sürümünde sunulan aramayı kullanarak Mongo'daki iki koleksiyona katılabilirsiniz. Sizin durumunuzda sorgu

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

veya kullanıcılarla ilgili olarak da katılabilirsiniz, o zaman aşağıda belirtildiği gibi küçük bir değişiklik olacaktır.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

SQL'de sol ve sağ birleştirmeyle aynı şekilde çalışacaktır.


11

Ne yapmaya çalıştığınıza bağlıdır.

Şu anda normalleştirilmiş bir veritabanı olarak ayarladınız, ki bu iyi ve bunu yaptığınız yöntem uygun.

Bununla birlikte, bunu yapmanın başka yolları da vardır.

Her gönderi için yinelenen olarak sorgulamak üzere kullanabileceğiniz kullanıcılara referanslar içeren gömülü yorumlar içeren bir gönderi koleksiyonunuz olabilir. Kullanıcının adını yorumlarla saklayabilir, hepsini tek bir belgede saklayabilirsiniz.

NoSQL ile ilgili şey, esnek şemalar ve çok hızlı okuma ve yazma için tasarlanmış olmasıdır. Tipik bir Büyük Veri çiftliğinde veritabanı en büyük darboğazdır, uygulama ve ön uç sunuculardan daha az veritabanı motoruna sahipsiniz ... daha pahalı ama daha güçlüdür, ayrıca sabit disk alanı nispeten ucuzdur. Normalleştirme, yerden tasarruf etmeye çalışma kavramından gelir, ancak veritabanlarınızın karmaşık Birleşmeler yapmasını ve ilişkilerin bütünlüğünü doğrulamayı, basamaklı işlemleri gerçekleştirmeyi bir maliyetle birlikte gelir. Tüm bunlar, veritabanını doğru bir şekilde tasarladıkları takdirde geliştiricilere bazı baş ağrılarından kurtarır.

NoSQL ile, artıklık ve depolama alanının maliyetlerinden dolayı sorun olmadığını kabul ederseniz (hem verileri güncellemek için gereken işlemci süresinde hem de ekstra veri depolamak için sabit disk maliyetlerinde), normalleştirmeyle ilgili bir sorun olmaz (gömülü diziler için yüz binlerce öğe bir performans sorunu olabilir, ancak çoğu zaman bu bir sorun değildir). Ayrıca, her veritabanı kümesi için birkaç uygulama ve ön uç sunucunuz olacaktır. Birleştirmeleri ağır kaldırmalarını sağlayın ve veritabanı sunucularının okuma ve yazma işlemlerine sadık kalmasını sağlayın.

TL; DR: Yaptığınız iyi ve bunu yapmanın başka yolları da var. Bazı harika örnekler için mongodb belgelerinin veri modeli modellerine göz atın. http://docs.mongodb.org/manual/data-modeling/


8
“Normalleşme, yerden tasarruf etmeye çalışmak kavramından gelir” Bunu sorgularım. IMHO normalizasyonu artıklıktan kaçınma kavramından gelir. Bir kullanıcının adını bir blog yazısı ile birlikte sakladığınızı varsayalım. Ya evlenirse? Normalize edilmemiş bir modelde, tüm yayınlarda dolaşmanız ve adı değiştirmeniz gerekir. Normalleştirilmiş bir modelde genellikle BİR kaydı değiştirirsiniz.
DanielKhan

@DanielKhan artıklığı önlemek ve yerden tasarruf etmek benzer kavramlardır, ancak yeniden analizde katılıyorum, fazlalık bu tasarımın temel nedenidir. Yeniden yazacağım. Not için teşekkürler.
Ocak'ta Snowburnt

11

DBRef adı verilen birçok sürücünün desteklediği bir özellik var.

DBRef, belgeler arasında referanslar oluşturmak için daha resmi bir teknik özelliktir. DBRefs (genellikle), bir nesne adının yanı sıra bir koleksiyon adı içerir. Çoğu geliştirici DBRef'leri yalnızca koleksiyon bir belgeden diğerine değişebiliyorsa kullanır. Referans verilen koleksiyonunuz her zaman aynı olacaksa, yukarıda özetlenen manuel referanslar daha verimlidir.

MongoDB Belgelerinden Alınan: Veri Modelleri> Veri Modeli Referansı> Veritabanı Referansları


11

$ lookup (toplama)

İşlem için “birleştirilmiş” koleksiyondaki belgelere filtre uygulamak için aynı veritabanındaki sertleştirilmemiş bir koleksiyona sol dış birleştirme gerçekleştirir. Her girdi belgesine, $ lookup stage, öğeleri “join” koleksiyonundaki eşleşen belgeler olan yeni bir dizi alanı ekler. $ Arama aşaması bu yeniden şekillendirilmiş belgeleri bir sonraki aşamaya geçirir. $ Lookup aşamasında aşağıdaki sözdizimleri bulunur:

Eşitlik Maçı

Giriş belgelerindeki bir alan ile “birleşik” koleksiyonun belgelerindeki bir alan arasında bir eşitlik eşleşmesi gerçekleştirmek için $ lookup stage aşağıdaki sözdizimine sahiptir:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

İşlem aşağıdaki sözde SQL ifadesine karşılık gelir:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT <documents as determined from the pipeline>
                               FROM <collection to join>
                               WHERE <pipeline> );

Moğol URL'si


alt sorgu birleştirmeden tamamen farklıdır, sol taraftaki tablonuz çok büyükse, alt sorgu her satırın bir sorgu yapması gerektiği anlamına gelir. çok yavaş olacak. katılmak sql çok hızlı.
yww325

8

Önce 3.2.6 , mongodb mysql gibi sorgu katılmak desteklemez. sizin için çalışan çözüm aşağıda.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])


3

MongoDB birleştirmelere izin vermez, ancak bunu gerçekleştirmek için eklentileri kullanabilirsiniz. Mongo-birleştirme eklentisini kontrol edin. En iyisi ve ben zaten kullandım. Doğrudan npm kullanarak bunu kurabilirsiniz npm install mongo-join. Örneklerle belgelerin tamamına göz atabilirsiniz .

(++) (N) koleksiyonlarına katılmamız gerektiğinde gerçekten yararlı bir araç

(-) koşulları, sorgunun en üst düzeyine uygulayabiliriz

Misal

var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server;
db.open(function (err, Database) {
    Database.collection('Appoint', function (err, Appoints) {

        /* we can put conditions just on the top level */
        Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date },
            full_date :{ $lte: end_date }}, function (err, cursor) {
            var join = new Join(Database).on({
                field: '_id_Doctor', // <- field in Appoints document
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            }).on({
                field: '_id_Patient', // <- field in Appoints doc
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            })
            join.toArray(cursor, function (err, joinedDocs) {

                /* do what ever you want here */
                /* you can fetch the table and apply your own conditions */
                .....
                .....
                .....


                resp.status(200);
                resp.json({
                    "status": 200,
                    "message": "success",
                    "Appoints_Range": joinedDocs,


                });
                return resp;


            });

    });

2

Toplama boru hattını kullanarak yapabilirsiniz, ancak kendiniz yazmak bir acıdır.

mongo-join-querySorgunuzdan otomatik olarak toplama boru hattı oluşturmak için kullanabilirsiniz .

Sorgunuz şöyle görünür:

const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");

joinQuery(
    mongoose.models.Comment,
    {
        find: { pid:444 },
        populate: ["uid"]
    },
    (err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);

Sonucunuz uidalanda kullanıcı nesnesine sahip olur ve istediğiniz kadar çok düzeyde bağlantı kurabilirsiniz. Başka bir şeye referans veren bir Ekibe referans yapan kullanıcıya referansı doldurabilirsiniz.

Feragatname : mongo-join-queryBu sorunun üstesinden gelmek için yazdım .


0

playORM, bölümler içinde birleştirmeler yapabilmeniz için bölümleme ekleyen S-SQL (Ölçeklenebilir SQL) kullanarak bunu sizin için yapabilir.


-2

Hayır, yanlış yapıyormuş gibi görünmüyorsun. MongoDB birleşimleri "istemci tarafı" dır. Söylediğin gibi:

Şu anda, ölçütlerimle eşleşen yorumları alıyorum, daha sonra bu sonuç kümesindeki tüm kullanıcı adlarını anlıyorum, kullanıcı nesnelerini alıyorum ve bunları yorum sonuçlarıyla birleştiriyorum. Yanlış yapıyormuşum gibi görünüyor.

1) Select from the collection you're interested in.
2) From that collection pull out ID's you need
3) Select from other collections
4) Decorate your original results.

Bu bir "gerçek" birleştirme değildir, ancak aslında bir SQL birleşiminden çok daha yararlıdır, çünkü "çok" taraflı birleşimler için yinelenen satırlarla uğraşmak zorunda değilsiniz, bunun yerine orijinal olarak seçilen seti dekore etmeniz gerekir.

Bu sayfada saçmalık ve FUD var. 5 yıl sonra ortaya çıkıyor MongoDB hala bir şey.


'"çok" taraflı birleştirmeler için yinelenen satırlarla uğraşmak zorunda değilsiniz - bununla ne demek istediğini bilmiyorum. Açıklayabilir misin?
Mark Amery

1
@MarkAmery, tabi. SQL'de bir nn ilişkisi yinelenen satırlar döndürür. Örneğin arkadaşlar. Bob Mary ve Jane ile arkadaşsa, Bob için 2 sıra alırsın: Bob, Mary ve Bob, Jane. 2 Bobs bir yalan, sadece bir Bob var. İstemci tarafı birleştirme ile Bob ile başlayabilir ve nasıl istediğinizi dekore edebilirsiniz: Bob, "Mary and Jane". SQL bunu alt sorgular ile yapalım, ancak bu istemcide yapılabilecek db sunucusunda çalışıyor.
Michael Cole

-3

Bence, normalleştirilmiş veri tablolarına ihtiyacınız varsa - Diğer bazı veritabanı çözümlerini denemeniz gerekir.

Ama üzerinde Mongo için o sollution kursak ettik Git uçlar kodunda, arada - bu filmin adı vardır ama noi Filmin kimliğini .

Sorun

Yaptıkları bir dizi Filmler içeren bir Aktörler koleksiyonunuz var.

Her birinde bir dizi Aktör bulunan bir Filmler koleksiyonu oluşturmak istiyorsunuz.

Bazı örnek veriler

 db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
 db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

Çözüm

Aktör belgesindeki her filmde döngü yapmamız ve her Filmi ayrı ayrı yayınlamamız gerekir.

Buradaki yakalama azaltma aşamasında. İndirgeme aşamasından bir dizi gönderemeyiz, bu nedenle döndürülen "değer" belgesinin içinde bir Actors dizisi oluşturmalıyız.

Kod
map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}

reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

Actor_list'in aslında bir dizi içeren bir javascript nesnesi olduğuna dikkat edin. Ayrıca haritanın aynı yapıyı yaydığına dikkat edin.

Haritayı yürütmek / azaltmak, "pivot" koleksiyonuna çıkarmak ve sonucu yazdırmak için aşağıdakileri çalıştırın:

printjson (db.actors.mapReduce (harita, azaltma, "pivot")); db.pivot.find () forEach (printjson.);

İşte örnek çıktı, "Pretty Woman" ve "Kaçak Gelin" hem "Richard Gere" hem de "Julia Roberts" var unutmayın.

{ "_id" : { "movie" : "Chicago" }, "value" : { "actors" : [ "Richard Gere" ] } }
{ "_id" : { "movie" : "Erin Brockovich" }, "value" : { "actors" : [ "Julia Roberts" ] } }
{ "_id" : { "movie" : "Pretty Woman" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }
{ "_id" : { "movie" : "Runaway Bride" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }


Bu cevabın içeriğinin çoğunun (yani anlaşılır İngilizce olan bit), verilen cevaplayıcının GitHub bağlantısındaki MongoDB yemek kitabından kopyalandığını unutmayın.
Mark Amery

-4

İki koleksiyonu mongoDB alt sorgusunu kullanarak birleştirebiliriz. İşte örnek, Yorumlar--

`db.commentss.insert([
  { uid:12345, pid:444, comment:"blah" },
  { uid:12345, pid:888, comment:"asdf" },
  { uid:99999, pid:444, comment:"qwer" }])`

Userss--

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])

JOIN-- için MongoDB alt sorgusu

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`

Yeni oluşturulan Koleksiyondan sonuç alın.

db.newCommentUsers.find().pretty()

Sonuç--

`{
    "_id" : ObjectId("5511236e29709afa03f226ef"),
    "uid" : 12345,
    "pid" : 444,
    "comment" : "blah",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f0"),
    "uid" : 12345,
    "pid" : 888,
    "comment" : "asdf",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f1"),
    "uid" : 99999,
    "pid" : 444,
    "comment" : "qwer",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f3"),
            "uid" : 99999,
            "name" : "mia"
        }
    ]
}`

Umarım bu yardımcı olacaktır.


7
Neredeyse aynı, bir yaşındaki bu cevabı neden kopyaladınız? stackoverflow.com/a/22739813/4186945
hackel
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.