MongoDB: Birden fazla koleksiyondaki verileri tek bir şekilde birleştirin .. nasıl?


229

(MongoDB'de) birden çok koleksiyondaki verileri tek bir koleksiyonda nasıl birleştirebilirim?

Harita küçültmeyi kullanabilir miyim, öyleyse nasıl?

Bir acemi olduğum için bazı örnekleri çok takdir ediyorum.


18
Farklı koleksiyonlardaki dokümanları tek bir koleksiyona mı kopyalamak istiyorsunuz yoksa planınız nedir? "Birleştir" i belirtebilir misiniz? Eğer sadece mongo kabuğu ile kopyalamak istiyorsanız a db.collection1.find().forEach(function(doc){db.collection2.save(doc)});yeterlidir. Mongo kabuğunu kullanmıyorsanız lütfen kullanılmış sürücünüzü (java, php, ...) belirtin.
proximus

öylesine i bir koleksiyon var (kullanıcılar demek) daha fazla koleksiyon var adres defteri koleksiyonu, kitap koleksiyonları listesi, vb diyor. nasıl kullanıcı_kodu anahtarına dayalı bu koleksiyonları sadece tek bir koleksiyon birleştirir. ?
user697697

Yanıtlar:


147

Bunu gerçek zamanlı olarak yapamasanız da, MongoDB 1.8+ harita / azaltmadaki "azaltma" seçeneğini kullanarak verileri birleştirmek için harita küçültmeyi birden çok kez çalıştırabilirsiniz (bkz. Http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). Her iki koleksiyonda da _id olarak kullanabileceğiniz bir anahtarınızın olması gerekir.

Örneğin, usersbir commentskoleksiyonunuz ve koleksiyonunuz olduğunu ve her yorum için bazı kullanıcı demografik bilgileri içeren yeni bir koleksiyonunuz olduğunu varsayalım.

Diyelim ki userskoleksiyonda aşağıdaki alanlar var:

  • _İD
  • İsim
  • Soyadı
  • ülke
  • Cinsiyet
  • yaş

Ve sonra commentskoleksiyon aşağıdaki alanlara sahiptir:

  • _İD
  • Kullanıcı kimliği
  • yorum Yap
  • oluşturulan

Bu haritayı yaparsınız / azaltırsınız:

var mapUsers, mapComments, reduce;
db.users_comments.remove();

// setup sample data - wouldn't actually use this in production
db.users.remove();
db.comments.remove();
db.users.save({firstName:"Rich",lastName:"S",gender:"M",country:"CA",age:"18"});
db.users.save({firstName:"Rob",lastName:"M",gender:"M",country:"US",age:"25"});
db.users.save({firstName:"Sarah",lastName:"T",gender:"F",country:"US",age:"13"});
var users = db.users.find();
db.comments.save({userId: users[0]._id, "comment": "Hey, what's up?", created: new ISODate()});
db.comments.save({userId: users[1]._id, "comment": "Not much", created: new ISODate()});
db.comments.save({userId: users[0]._id, "comment": "Cool", created: new ISODate()});
// end sample data setup

mapUsers = function() {
    var values = {
        country: this.country,
        gender: this.gender,
        age: this.age
    };
    emit(this._id, values);
};
mapComments = function() {
    var values = {
        commentId: this._id,
        comment: this.comment,
        created: this.created
    };
    emit(this.userId, values);
};
reduce = function(k, values) {
    var result = {}, commentFields = {
        "commentId": '', 
        "comment": '',
        "created": ''
    };
    values.forEach(function(value) {
        var field;
        if ("comment" in value) {
            if (!("comments" in result)) {
                result.comments = [];
            }
            result.comments.push(value);
        } else if ("comments" in value) {
            if (!("comments" in result)) {
                result.comments = [];
            }
            result.comments.push.apply(result.comments, value.comments);
        }
        for (field in value) {
            if (value.hasOwnProperty(field) && !(field in commentFields)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.users.mapReduce(mapUsers, reduce, {"out": {"reduce": "users_comments"}});
db.comments.mapReduce(mapComments, reduce, {"out": {"reduce": "users_comments"}});
db.users_comments.find().pretty(); // see the resulting collection

Bu noktada, users_commentsbirleştirilmiş verileri içeren yeni bir koleksiyonunuz olacak ve şimdi bunu kullanabilirsiniz. Bu azaltılmış koleksiyonların hepsinde _id, harita işlevlerinizde yayınladığınız anahtar vardır ve daha sonra tüm değerler valueanahtarın içinde bir alt nesnedir - değerler bu azaltılmış belgelerin en üst düzeyinde değildir.

Bu biraz basit bir örnek. İndirgenmiş koleksiyonu oluşturmaya devam etmek için daha fazla koleksiyonla bunu tekrarlayabilirsiniz. Süreçteki verilerin özetleri ve toplamalarını da yapabilirsiniz. Büyük olasılıkla, mevcut alanları birleştirme ve koruma mantığı daha karmaşık hale geldiğinden, birden fazla azaltma işlevi tanımlayabilirsiniz.

Ayrıca, her kullanıcının bir dizideki tüm yorumlarını içeren bir belge olduğunu da göreceksiniz. Bire çok değil, bire bir ilişkisi olan verileri birleştiriyor olsaydık, düz olurdu ve basitçe şöyle bir azaltma işlevi kullanabilirsiniz:

reduce = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};

users_commentsKoleksiyonu, yorum başına bir belge olacak şekilde düzleştirmek istiyorsanız , ayrıca şunu da çalıştırın:

var map, reduce;
map = function() {
    var debug = function(value) {
        var field;
        for (field in value) {
            print(field + ": " + value[field]);
        }
    };
    debug(this);
    var that = this;
    if ("comments" in this.value) {
        this.value.comments.forEach(function(value) {
            emit(value.commentId, {
                userId: that._id,
                country: that.value.country,
                age: that.value.age,
                comment: value.comment,
                created: value.created,
            });
        });
    }
};
reduce = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.users_comments.mapReduce(map, reduce, {"out": "comments_with_demographics"});

Bu teknik kesinlikle anında gerçekleştirilmemelidir. Birleştirilen verileri periyodik olarak güncelleyen bir cron işi veya bunun gibi bir şey için uygundur. Muhtemelen ensureIndexona karşı gerçekleştirdiğiniz sorguların hızlı bir şekilde çalıştığından emin olmak için yeni koleksiyonda yayınlamak isteyeceksiniz (verilerinizin hala bir valueanahtarın içinde olduğunu unutmayın , bu nedenle comments_with_demographicsyorum createdzamanını dizine ekleyecekseniz ,db.comments_with_demographics.ensureIndex({"value.created": 1});


1
Muhtemelen bunu üretim yazılımında asla yapmam, ama yine de kötü bir teknik.
Dave Griffith

3
Teşekkürler Dave. Bu tekniği son 3 aydır üretimde yüksek trafikli bir site için ihracat ve raporlama tabloları oluşturmak amacıyla sorunsuz bir şekilde kullandım. İşte tekniğin benzer bir kullanımını anlatan başka bir makale: tebros.com/2011/07/…
rmarscher

1
Teşekkürler @rmarscher Ekstra ayrıntılarınız gerçekten her şeyi daha iyi anlamama yardımcı oldu.
benstr

5
Bu yanıtı toplama boru hattı ve yeni $ arama işlemi kullanarak bir örnekle güncelleştirmeliyim. Burada düzgün bir yazı yazana kadar bahsetmiştim. docs.mongodb.org/manual/reference/operator/aggregation/lookup
rmarscher

1
Hızlı bir şekilde bu işi yapmak isteyenler için FYI, işte users_commentsilk kod bloğundan sonra koleksiyonda neler var gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
Nolan Amy

128

MongoDB 3.2 artık birden çok koleksiyondaki verileri $ lookup aggregation aşamasında bir araya getiriyor . Pratik bir örnek olarak, iki farklı koleksiyona ayrılmış kitaplar hakkında verileriniz olduğunu varsayalım.

booksAşağıdaki verilerle adlandırılan ilk koleksiyon :

{
    "isbn": "978-3-16-148410-0",
    "title": "Some cool book",
    "author": "John Doe"
}
{
    "isbn": "978-3-16-148999-9",
    "title": "Another awesome book",
    "author": "Jane Roe"
}

Ve ikinci koleksiyon denir books_selling_data, aşağıdaki verilere sahiptir:

{
    "_id": ObjectId("56e31bcf76cdf52e541d9d26"),
    "isbn": "978-3-16-148410-0",
    "copies_sold": 12500
}
{
    "_id": ObjectId("56e31ce076cdf52e541d9d28"),
    "isbn": "978-3-16-148999-9",
    "copies_sold": 720050
}
{
    "_id": ObjectId("56e31ce076cdf52e541d9d29"),
    "isbn": "978-3-16-148999-9",
    "copies_sold": 1000
}

Her iki koleksiyonu birleştirmek sadece $ lookup komutunu şu şekilde kullanmakla ilgilidir:

db.books.aggregate([{
    $lookup: {
            from: "books_selling_data",
            localField: "isbn",
            foreignField: "isbn",
            as: "copies_sold"
        }
}])

Bu toplama işleminden sonra bookskoleksiyon aşağıdaki gibi görünecektir:

{
    "isbn": "978-3-16-148410-0",
    "title": "Some cool book",
    "author": "John Doe",
    "copies_sold": [
        {
            "_id": ObjectId("56e31bcf76cdf52e541d9d26"),
            "isbn": "978-3-16-148410-0",
            "copies_sold": 12500
        }
    ]
}
{
    "isbn": "978-3-16-148999-9",
    "title": "Another awesome book",
    "author": "Jane Roe",
    "copies_sold": [
        {
            "_id": ObjectId("56e31ce076cdf52e541d9d28"),
            "isbn": "978-3-16-148999-9",
            "copies_sold": 720050
        },
        {
            "_id": ObjectId("56e31ce076cdf52e541d9d28"),
            "isbn": "978-3-16-148999-9",
            "copies_sold": 1000
        }
    ]
}

Birkaç şeye dikkat etmek önemlidir:

  1. " books_selling_dataGönderen " koleksiyonu, bu durumda , parçalanamaz.
  2. "Örnek" alanı yukarıdaki örnekte olduğu gibi bir dizi olacaktır.
  3. $ Search aşamasında hem "localField" hem de "foreignField" seçenekleri, ilgili koleksiyonlarında yoksa eşleştirme amaçları için null olarak değerlendirilir ( $ lookup docs bununla ilgili mükemmel bir örneğe sahiptir).

Sonuç olarak, her iki koleksiyonu da birleştirmek istiyorsanız, bu durumda, satılan toplam kopya ile düz bir copy_s_sold alanı elde etmek istiyorsanız, muhtemelen biraz daha fazla çalışmanız gerekecek, muhtemelen, olmak dışarı $ nihai koleksiyonuna.


merhaba orada, lütfen böyle verileri yönetmek için optimize edilmiş yol ne olacağını söyleyebilirim: Kullanıcı, file.files ve file.chunks üç koleksiyon, bir yanıtta tüm ilgili dosya ile belirli bir kullanıcı istiyorum mümkün mü? {"name": "batMan", "email ':" bt@gmail.com "," files ": [{dosya1}, {dosya2}, {dosya3}, .... vb.]}
mfaisalhyder

Yukarıdaki çözüm için resmi dokümantasyon örnekleri burada bulunabilir: docs.mongodb.com/manual/reference/operator/aggregation/lookup
Jakub Czaplicki

4
Aslında cevabımın resmi belgelere zaten üç bağlantısı vardı. Ama yine de katkınız için teşekkürler. JakubCzaplicki
Bruno Krebs

2
Toplam beyin arızası (büyük olasılıkla) olabilir ama $lookuptüm "localField" ve "foreignField" eşit "isbn" olmamalı? "_id" ve "isbn" değil mi?
Dev01

13

Mongodb'a toplu ekleme yoksa, içindeki tüm nesneleri döngüye sokar small_collectionve bunları tek tek ekleriz big_collection:

db.small_collection.find().forEach(function(obj){ 
   db.big_collection.insert(obj)
});

db.colleciton.insert ([{}, {}, {}]) Ekle, dizileri kabul eder.
augurone

2
Bu, küçük koleksiyonlar için iyi çalışır, ancak dizinleri
taşımayı

12

$ Lookup ile çok temel bir örnek.

db.getCollection('users').aggregate([
    {
        $lookup: {
            from: "userinfo",
            localField: "userId",
            foreignField: "userId",
            as: "userInfoData"
        }
    },
    {
        $lookup: {
            from: "userrole",
            localField: "userId",
            foreignField: "userId",
            as: "userRoleData"
        }
    },
    { $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }},
    { $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}
])

İşte kullanılır

 { $unwind: { path: "$userInfoData", preserveNullAndEmptyArrays: true }}, 
 { $unwind: { path: "$userRoleData", preserveNullAndEmptyArrays: true }}

Onun yerine

{ $unwind:"$userRoleData"} 
{ $unwind:"$userRoleData"}

Çünkü {$ çile çözme: "$ userRoleData"} eşleşen kaydı arama $ ile bulursa bu boş veya 0 sonucunu döndürecektir.


11

MongoDB'de sendikaları 'SQL UNION' tarzında yapmak, tek bir sorguda aramalarla birlikte toplamaları kullanarak mümkündür. İşte ben MongoDB 4.0 ile çalışan test bir örnek:

// Create employees data for testing the union.
db.getCollection('employees').insert({ name: "John", type: "employee", department: "sales" });
db.getCollection('employees').insert({ name: "Martha", type: "employee", department: "accounting" });
db.getCollection('employees').insert({ name: "Amy", type: "employee", department: "warehouse" });
db.getCollection('employees').insert({ name: "Mike", type: "employee", department: "warehouse"  });

// Create freelancers data for testing the union.
db.getCollection('freelancers').insert({ name: "Stephany", type: "freelancer", department: "accounting" });
db.getCollection('freelancers').insert({ name: "Martin", type: "freelancer", department: "sales" });
db.getCollection('freelancers').insert({ name: "Doug", type: "freelancer", department: "warehouse"  });
db.getCollection('freelancers').insert({ name: "Brenda", type: "freelancer", department: "sales"  });

// Here we do a union of the employees and freelancers using a single aggregation query.
db.getCollection('freelancers').aggregate( // 1. Use any collection containing at least one document.
  [
    { $limit: 1 }, // 2. Keep only one document of the collection.
    { $project: { _id: '$$REMOVE' } }, // 3. Remove everything from the document.

    // 4. Lookup collections to union together.
    { $lookup: { from: 'employees', pipeline: [{ $match: { department: 'sales' } }], as: 'employees' } },
    { $lookup: { from: 'freelancers', pipeline: [{ $match: { department: 'sales' } }], as: 'freelancers' } },

    // 5. Union the collections together with a projection.
    { $project: { union: { $concatArrays: ["$employees", "$freelancers"] } } },

    // 6. Unwind and replace root so you end up with a result set.
    { $unwind: '$union' },
    { $replaceRoot: { newRoot: '$union' } }
  ]);

İşte nasıl çalıştığının açıklaması:

  1. Bir örneğini aggregatedurumlar o herhangi içinde en az bir belge vardır veritabanınızda toplanması. Veritabanınızın herhangi bir koleksiyonunun boş olmayacağını garanti edemezseniz, veritabanınızda, özellikle birleşim sorguları yapmak için orada olacak tek bir boş belge içeren bir tür 'kukla' koleksiyon oluşturarak bu sorunu çözebilirsiniz.

  2. Boru hattınızın ilk aşamasını yapın { $limit: 1 }. Bu, birincisi dışındaki koleksiyonun tüm belgelerini çıkaracaktır.

  3. Bir $projectsahne alanı kullanarak kalan belgenin tüm alanlarını soyun :

    { $project: { _id: '$$REMOVE' } }
  4. Toplamınız artık tek, boş bir belge içeriyor. Bir araya getirmek istediğiniz her koleksiyon için arama ekleme zamanı. Sen kullanabilir pipelinebazı özel filtreleme yapmak alanını veya terk localFieldve foreignFieldboş olarak tüm koleksiyonu eşleşecek.

    { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
    { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
    { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
  5. Artık şu şekilde 3 dizi içeren tek bir belge içeren bir topluluğunuz var:

    {
        Collection1: [...],
        Collection2: [...],
        Collection3: [...]
    }

    Daha sonra $project, $concatArraystoplama işleciyle birlikte bir sahne alanı kullanarak bunları tek bir dizide birleştirebilirsiniz:

    {
      "$project" :
      {
        "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
      }
    }
  6. Artık tek bir belge içeren ve içinde koleksiyon birliğinizi içeren bir dizinin bulunduğu bir topluluğunuz var. Yapılması gereken , dizinizi ayrı belgelere bölmek için bir $unwindve bir $replaceRootaşama eklemektir :

    { $unwind: "$Union" },
    { $replaceRoot: { newRoot: "$Union" } }
  7. Voilà. Artık bir araya getirmek istediğiniz koleksiyonları içeren bir sonuç kümeniz var. Daha sonra daha fazla filtre uygulamak, sıralamak, skip () ve limit () uygulamak için daha fazla aşama ekleyebilirsiniz. Hemen hemen istediğiniz her şeyi.


"$ Projection en az bir çıktı alanı gerektiriyor" iletisi ile sorgu başarısız oluyor.
abhishek_ganta

@abhishek Bunun nedeni, tüm alanları tek bir projeksiyon aşamasında tek bir belgeden çıkarmaya çalışmanızdır. MongoDB bunu yapmanıza izin vermez. Bu sorunu çözmek için, birincisinin _id dışında her şeyi ve ikincisinin kalan _id'i şeritlediği 2 ardışık projeksiyon yapmanız gerekir.
sboisse

@abhishek $ proje aşamalarını '$$ REMOVE' değişkenini kullanan tek bir aşamada değiştirerek sorguyu daha da basitleştirdim. Ayrıca, çalıştığını görmek için doğrudan sorgu test cihazınıza kopyalayıp yapıştırabileceğiniz somut bir örnek ekledim.
sboisse

@sboisse, bu çözüm daha küçük koleksiyonlar için çalışır, ancak bunu büyük koleksiyonlarda (100.000+ dokümanlar) yapmak istersem, "collectionToUnion1'deki toplam doküman boyutu maksimum doküman boyutunu aşıyor". Dokümanlarda, büyük ara belgeler oluşturmaktan kaçınmak için $ aramasından hemen sonra bir $ gevşetmeyi önerir. Bu yöntemi kullanarak bu çözümü değiştirmekte başarılı olamadım. Bu sorunla karşılaştı ve bu yöntemi kullanmak zorunda kaldınız mı? Başvurduğum belgelere bağlantı: [link] ( docs.mongodb.com/manual/core/aggregation-pipeline-optimization/… )
lucky7samson

@ lucky7samson ne yazık ki uğraşmak zorunda olduğum veri miktarı o kadar büyük değildi. Yani bahsettiğiniz sorunla yüzleşmek zorunda kalmadım. Benim durumumda, kayıtları geri kalanı ile birleştirmeden önce arama için filtreleme uygulayabilirim, böylece birliğe veri miktarı oldukça küçüktü.
sboisse

9

toplamada birden çok koleksiyon için birden fazla $ araması kullanma

sorgu:

db.getCollection('servicelocations').aggregate([
  {
    $match: {
      serviceLocationId: {
        $in: ["36728"]
      }
    }
  },
  {
    $lookup: {
      from: "orders",
      localField: "serviceLocationId",
      foreignField: "serviceLocationId",
      as: "orders"
    }
  },
  {
    $lookup: {
      from: "timewindowtypes",
      localField: "timeWindow.timeWindowTypeId",
      foreignField: "timeWindowTypeId",
      as: "timeWindow"
    }
  },
  {
    $lookup: {
      from: "servicetimetypes",
      localField: "serviceTimeTypeId",
      foreignField: "serviceTimeTypeId",
      as: "serviceTime"
    }
  },
  {
    $unwind: "$orders"
  },
  {
    $unwind: "$serviceTime"
  },
  {
    $limit: 14
  }
])

sonuç:

{
    "_id" : ObjectId("59c3ac4bb7799c90ebb3279b"),
    "serviceLocationId" : "36728",
    "regionId" : 1.0,
    "zoneId" : "DXBZONE1",
    "description" : "AL HALLAB REST EMIRATES MALL",
    "locationPriority" : 1.0,
    "accountTypeId" : 1.0,
    "locationType" : "SERVICELOCATION",
    "location" : {
        "makani" : "",
        "lat" : 25.119035,
        "lng" : 55.198694
    },
    "deliveryDays" : "MTWRFSU",
    "timeWindow" : [ 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32cde"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "06:00",
                "closeTime" : "08:00"
            },
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32cdf"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "09:00",
                "closeTime" : "10:00"
            },
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b0a3b7799c90ebb32ce0"),
            "timeWindowTypeId" : "1",
            "Description" : "MORNING",
            "timeWindow" : {
                "openTime" : "10:30",
                "closeTime" : "11:30"
            },
            "accountId" : 1.0
        }
    ],
    "address1" : "",
    "address2" : "",
    "phone" : "",
    "city" : "",
    "county" : "",
    "state" : "",
    "country" : "",
    "zipcode" : "",
    "imageUrl" : "",
    "contact" : {
        "name" : "",
        "email" : ""
    },
    "status" : "ACTIVE",
    "createdBy" : "",
    "updatedBy" : "",
    "updateDate" : "",
    "accountId" : 1.0,
    "serviceTimeTypeId" : "1",
    "orders" : [ 
        {
            "_id" : ObjectId("59c3b291f251c77f15790f92"),
            "orderId" : "AQ18O1704264",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ18O1704264",
            "orderDate" : "18-Sep-17",
            "description" : "AQ18O1704264",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 296.0,
            "size2" : 3573.355,
            "size3" : 240.811,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "BNWB020",
                    "size1" : 15.0,
                    "size2" : 78.6,
                    "size3" : 6.0
                }, 
                {
                    "ItemId" : "BNWB021",
                    "size1" : 20.0,
                    "size2" : 252.0,
                    "size3" : 11.538
                }, 
                {
                    "ItemId" : "BNWB023",
                    "size1" : 15.0,
                    "size2" : 285.0,
                    "size3" : 16.071
                }, 
                {
                    "ItemId" : "CPMW112",
                    "size1" : 3.0,
                    "size2" : 25.38,
                    "size3" : 1.731
                }, 
                {
                    "ItemId" : "MMGW001",
                    "size1" : 25.0,
                    "size2" : 464.375,
                    "size3" : 46.875
                }, 
                {
                    "ItemId" : "MMNB218",
                    "size1" : 50.0,
                    "size2" : 920.0,
                    "size3" : 60.0
                }, 
                {
                    "ItemId" : "MMNB219",
                    "size1" : 50.0,
                    "size2" : 630.0,
                    "size3" : 40.0
                }, 
                {
                    "ItemId" : "MMNB220",
                    "size1" : 50.0,
                    "size2" : 416.0,
                    "size3" : 28.846
                }, 
                {
                    "ItemId" : "MMNB270",
                    "size1" : 50.0,
                    "size2" : 262.0,
                    "size3" : 20.0
                }, 
                {
                    "ItemId" : "MMNB302",
                    "size1" : 15.0,
                    "size2" : 195.0,
                    "size3" : 6.0
                }, 
                {
                    "ItemId" : "MMNB373",
                    "size1" : 3.0,
                    "size2" : 45.0,
                    "size3" : 3.75
                }
            ],
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b291f251c77f15790f9d"),
            "orderId" : "AQ137O1701240",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ137O1701240",
            "orderDate" : "18-Sep-17",
            "description" : "AQ137O1701240",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 28.0,
            "size2" : 520.11,
            "size3" : 52.5,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "MMGW001",
                    "size1" : 25.0,
                    "size2" : 464.38,
                    "size3" : 46.875
                }, 
                {
                    "ItemId" : "MMGW001-F1",
                    "size1" : 3.0,
                    "size2" : 55.73,
                    "size3" : 5.625
                }
            ],
            "accountId" : 1.0
        }, 
        {
            "_id" : ObjectId("59c3b291f251c77f15790fd8"),
            "orderId" : "AQ110O1705036",
            "serviceLocationId" : "36728",
            "orderNo" : "AQ110O1705036",
            "orderDate" : "18-Sep-17",
            "description" : "AQ110O1705036",
            "serviceType" : "Delivery",
            "orderSource" : "Import",
            "takenBy" : "KARIM",
            "plannedDeliveryDate" : ISODate("2017-08-26T00:00:00.000Z"),
            "plannedDeliveryTime" : "",
            "actualDeliveryDate" : "",
            "actualDeliveryTime" : "",
            "deliveredBy" : "",
            "size1" : 60.0,
            "size2" : 1046.0,
            "size3" : 68.0,
            "jobPriority" : 1.0,
            "cancelReason" : "",
            "cancelDate" : "",
            "cancelBy" : "",
            "reasonCode" : "",
            "reasonText" : "",
            "status" : "",
            "lineItems" : [ 
                {
                    "ItemId" : "MMNB218",
                    "size1" : 50.0,
                    "size2" : 920.0,
                    "size3" : 60.0
                }, 
                {
                    "ItemId" : "MMNB219",
                    "size1" : 10.0,
                    "size2" : 126.0,
                    "size3" : 8.0
                }
            ],
            "accountId" : 1.0
        }
    ],
    "serviceTime" : {
        "_id" : ObjectId("59c3b07cb7799c90ebb32cdc"),
        "serviceTimeTypeId" : "1",
        "serviceTimeType" : "nohelper",
        "description" : "",
        "fixedTime" : 30.0,
        "variableTime" : 0.0,
        "accountId" : 1.0
    }
}

1

Mongorestore, veritabanında bulunan her şeyin üstüne ekleme özelliğine sahiptir, bu nedenle bu davranış iki koleksiyonu birleştirmek için kullanılabilir:

  1. mongodump koleksiyonu1
  2. collection2.rename (collection1)
  3. mongorestore

Henüz denemedim, ancak harita / azaltma yaklaşımından daha hızlı performans gösterebilir.


1

İlk olarak Mongo 4.4, yeni $unionWithbirleştirme aşamasını $groupyeni $accumulatoroperatörüne bağlayarak bu birleştirme bir toplama hattı içinde gerçekleştirebiliriz :

// > db.users.find()
//   [{ user: 1, name: "x" }, { user: 2, name: "y" }]
// > db.books.find()
//   [{ user: 1, book: "a" }, { user: 1, book: "b" }, { user: 2, book: "c" }]
// > db.movies.find()
//   [{ user: 1, movie: "g" }, { user: 2, movie: "h" }, { user: 2, movie: "i" }]
db.users.aggregate([
  { $unionWith: "books"  },
  { $unionWith: "movies" },
  { $group: {
    _id: "$user",
    user: {
      $accumulator: {
        accumulateArgs: ["$name", "$book", "$movie"],
        init: function() { return { books: [], movies: [] } },
        accumulate: function(user, name, book, movie) {
          if (name) user.name = name;
          if (book) user.books.push(book);
          if (movie) user.movies.push(movie);
          return user;
        },
        merge: function(userV1, userV2) {
          if (userV2.name) userV1.name = userV2.name;
          userV1.books.concat(userV2.books);
          userV1.movies.concat(userV2.movies);
          return userV1;
        },
        lang: "js"
      }
    }
  }}
])
// { _id: 1, user: { books: ["a", "b"], movies: ["g"], name: "x" } }
// { _id: 2, user: { books: ["c"], movies: ["h", "i"], name: "y" } }
  • $unionWithbelirli bir koleksiyondaki kayıtları zaten toplama hattında bulunan belgeler içinde birleştirir. İki birlik aşamasından sonra, boru hattında tüm kullanıcı, kitap ve film kayıtlarına sahibiz.

  • Daha sonra , operatörü kullanarak, gruplandıkça belgelerin özel olarak birikmesine izin veren öğeleri kullanarak $groupkaydeder $userve biriktiririz $accumulator:

    • biriktirmek istediğimiz alanlar tanımlanır accumulateArgs.
    • init öğeleri gruplandırdığımızda biriktirilecek durumu tanımlar.
    • accumulateişlevi, bir kayıtla özel bir eylem birikmiş bir devlet inşa etmek için gruplandırılmak yapmak mümkündür. Örneğin, gruplanan öğenin booktanımlanmış alanı varsa, durumun bir bookskısmını güncelleriz .
    • mergeiki iç durumu birleştirmek için kullanılır. Yalnızca parçalanmış kümelerde çalışan toplamalar için veya işlem bellek sınırlarını aştığında kullanılır.

için benzer çıktı almak mümkündür: 4.2.6 sürümü
Nixit Patel

0

Evet şunları yapabilirsiniz: Bugün yazdığım bu yardımcı program işlevini al:

function shangMergeCol() {
  tcol= db.getCollection(arguments[0]);
  for (var i=1; i<arguments.length; i++){
    scol= db.getCollection(arguments[i]);
    scol.find().forEach(
        function (d) {
            tcol.insert(d);
        }
    )
  }
}

Bu işleve istediğiniz sayıda koleksiyon aktarabilirsiniz, ilki hedef olan olacak. Geri kalan tüm koleksiyonlar, hedef koleksiyona aktarılacak kaynaklardır.


-1

Kod pasajı. Nezaket-Bu dahil olmak üzere yığın taşması üzerine birden fazla mesaj.

 db.cust.drop();
 db.zip.drop();
 db.cust.insert({cust_id:1, zip_id: 101});
 db.cust.insert({cust_id:2, zip_id: 101});
 db.cust.insert({cust_id:3, zip_id: 101});
 db.cust.insert({cust_id:4, zip_id: 102});
 db.cust.insert({cust_id:5, zip_id: 102});

 db.zip.insert({zip_id:101, zip_cd:'AAA'});
 db.zip.insert({zip_id:102, zip_cd:'BBB'});
 db.zip.insert({zip_id:103, zip_cd:'CCC'});

mapCust = function() {
    var values = {
        cust_id: this.cust_id
    };
    emit(this.zip_id, values);
};

mapZip = function() {
    var values = {
    zip_cd: this.zip_cd
    };
    emit(this.zip_id, values);
};

reduceCustZip =  function(k, values) {
    var result = {};
    values.forEach(function(value) {
    var field;
        if ("cust_id" in value) {
            if (!("cust_ids" in result)) {
                result.cust_ids = [];
            }
            result.cust_ids.push(value);
        } else {
    for (field in value) {
        if (value.hasOwnProperty(field) ) {
                result[field] = value[field];
        }
         };  
       }
      });
       return result;
};


db.cust_zip.drop();
db.cust.mapReduce(mapCust, reduceCustZip, {"out": {"reduce": "cust_zip"}});
db.zip.mapReduce(mapZip, reduceCustZip, {"out": {"reduce": "cust_zip"}});
db.cust_zip.find();


mapCZ = function() {
    var that = this;
    if ("cust_ids" in this.value) {
        this.value.cust_ids.forEach(function(value) {
            emit(value.cust_id, {
                zip_id: that._id,
                zip_cd: that.value.zip_cd
            });
        });
    }
};

reduceCZ = function(k, values) {
    var result = {};
    values.forEach(function(value) {
        var field;
        for (field in value) {
            if (value.hasOwnProperty(field)) {
                result[field] = value[field];
            }
        }
    });
    return result;
};
db.cust_zip_joined.drop();
db.cust_zip.mapReduce(mapCZ, reduceCZ, {"out": "cust_zip_joined"}); 
db.cust_zip_joined.find().pretty();


var flattenMRCollection=function(dbName,collectionName) {
    var collection=db.getSiblingDB(dbName)[collectionName];

    var i=0;
    var bulk=collection.initializeUnorderedBulkOp();
    collection.find({ value: { $exists: true } }).addOption(16).forEach(function(result) {
        print((++i));
        //collection.update({_id: result._id},result.value);

        bulk.find({_id: result._id}).replaceOne(result.value);

        if(i%1000==0)
        {
            print("Executing bulk...");
            bulk.execute();
            bulk=collection.initializeUnorderedBulkOp();
        }
    });
    bulk.execute();
};


flattenMRCollection("mydb","cust_zip_joined");
db.cust_zip_joined.find().pretty();

-2

Bunu uygulama katmanınızda yapmanız gerekir. Bir ORM kullanıyorsanız, diğer koleksiyonlarda bulunan referansları çekmek için ek açıklamalar (veya benzer bir şey) kullanabilir. Ben sadece Morphia ile çalıştı ve @Referenceek açıklama sorgulandığında referans varlık alır, bu yüzden kod kendim yapmaktan kaçınmak mümkün.

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.