MongoDB: Gabungkan data dari banyak koleksi menjadi satu..bagaimana?

229

Bagaimana saya (dalam MongoDB) dapat menggabungkan data dari banyak koleksi menjadi satu koleksi?

Bisakah saya menggunakan pengurangan peta dan jika demikian, bagaimana caranya?

Saya akan sangat menghargai beberapa contoh karena saya seorang pemula.

pengguna697697
sumber
18
Apakah Anda hanya ingin menyalin dokumen dari koleksi yang berbeda ke dalam satu koleksi tunggal atau apa rencanamu? Bisakah Anda menentukan "menggabungkan"? Jika Anda hanya ingin menyalin melalui mongo shell db.collection1.find().forEach(function(doc){db.collection2.save(doc)});sudah cukup. Silakan tentukan driver bekas Anda (java, php, ...) jika Anda tidak menggunakan mongo shell.
Proximus
jadi saya punya koleksi (katakan pengguna) daripada koleksi lain mengatakan koleksi buku alamat, daftar koleksi buku, dll. Bagaimana saya bisa berdasarkan pada tombol say user_id menggabungkan koleksi-koleksi ini menjadi hanya satu koleksi tunggal. ?
user697697
Terkait: stackoverflow.com/q/2350495/435605
AlikElzin-kilaka

Jawaban:

147

Meskipun Anda tidak dapat melakukan ini secara real-time, Anda dapat menjalankan pengurangan peta beberapa kali untuk menggabungkan data bersama dengan menggunakan opsi "perkecil" di MongoDB 1.8+ map / kurangi (lihat http://www.mongodb.org/ display / DOCS / MapReduce # MapReduce-Outputoptions ). Anda perlu memiliki beberapa kunci di kedua koleksi yang dapat Anda gunakan sebagai _id.

Misalnya, katakanlah Anda memiliki userskoleksi dan commentskoleksi dan Anda ingin memiliki koleksi baru yang memiliki beberapa info demografis pengguna untuk setiap komentar.

Katakanlah userskoleksi memiliki bidang-bidang berikut:

  • _Indo
  • nama depan
  • nama keluarga
  • negara
  • jenis kelamin
  • usia

Dan kemudian commentskoleksi memiliki bidang-bidang berikut:

  • _Indo
  • identitas pengguna
  • komentar
  • dibuat

Anda akan melakukan peta ini / mengurangi:

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

Pada titik ini, Anda akan memiliki koleksi baru yang disebut users_commentsyang berisi data yang digabungkan dan sekarang Anda dapat menggunakannya. Semua koleksi yang diperkecil ini memiliki _idkunci yang Anda keluarkan dalam fungsi peta Anda dan kemudian semua nilainya adalah sub-objek di dalam valuekunci - nilainya tidak berada di tingkat teratas dari dokumen yang diperkecil ini.

Ini adalah contoh yang agak sederhana. Anda dapat mengulangi ini dengan lebih banyak koleksi sebanyak yang Anda ingin terus membangun koleksi berkurang. Anda juga bisa melakukan ringkasan dan agregasi data dalam proses tersebut. Kemungkinan Anda akan mendefinisikan lebih dari satu fungsi pengurangan karena logika untuk menggabungkan dan mempertahankan bidang yang ada menjadi lebih kompleks.

Anda juga akan mencatat bahwa sekarang ada satu dokumen untuk setiap pengguna dengan semua komentar pengguna itu dalam sebuah array. Jika kami menggabungkan data yang memiliki hubungan satu-ke-satu dan bukan satu-ke-banyak, itu akan datar dan Anda bisa menggunakan fungsi pengurangan seperti ini:

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;
};

Jika Anda ingin meratakan users_commentskoleksi jadi satu dokumen per komentar, jalankan ini juga:

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"});

Teknik ini seharusnya tidak dilakukan dengan cepat. Ini cocok untuk pekerjaan cron atau sesuatu seperti itu yang memperbarui data yang digabungkan secara berkala. Anda mungkin ingin menjalankan ensureIndexkoleksi baru untuk memastikan bahwa kueri yang Anda lakukan terhadapnya berjalan dengan cepat (perlu diingat bahwa data Anda masih di dalam valuekunci, jadi jika Anda mengindeks comments_with_demographicspada createdwaktu komentar , itu akan menjadidb.comments_with_demographics.ensureIndex({"value.created": 1});

rmarscher
sumber
1
Saya mungkin tidak akan pernah melakukan itu dalam perangkat lunak produksi, tetapi itu masih merupakan teknik keren yang jahat.
Dave Griffith
3
Terima kasih, Dave. Saya menggunakan teknik ini untuk menghasilkan tabel ekspor dan pelaporan untuk situs lalu lintas tinggi dalam produksi selama 3 bulan terakhir tanpa masalah. Inilah artikel lain yang menggambarkan penggunaan teknik yang serupa
rmarscher
1
Terima kasih @rmarscher detail ekstra Anda benar-benar membantu saya untuk lebih memahami segalanya.
benstr
5
Saya harus memperbarui jawaban ini dengan contoh menggunakan pipa agregasi dan operasi pencarian $ baru. Menyebutkannya di sini sampai saya dapat menyusun artikel yang tepat. docs.mongodb.org/manual/reference/operator/aggregation/lookup
rmarscher
1
FYI bagi mereka yang ingin cepat grok melakukan hal ini, inilah yang ada di users_commentskoleksi setelah blok pertama kode gist.github.com/nolanamy/83d7fb6a9bf92482a1c4311ad9c78835
Nolan Amy
127

MongoDB 3.2 sekarang memungkinkan seseorang untuk menggabungkan data dari banyak koleksi menjadi satu melalui tahap agregasi $ lookup . Sebagai contoh praktis, katakanlah Anda memiliki data tentang buku yang dipecah menjadi dua koleksi yang berbeda.

Pengumpulan pertama, disebut books, memiliki data berikut:

{
    "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"
}

Dan koleksi kedua, disebut books_selling_data, memiliki data berikut:

{
    "_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
}

Untuk menggabungkan kedua koleksi hanyalah masalah menggunakan $ lookup dengan cara berikut:

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

Setelah agregasi ini, bookskoleksi akan terlihat seperti berikut:

{
    "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
        }
    ]
}

Penting untuk mencatat beberapa hal:

  1. Koleksi "dari", dalam hal ini books_selling_data, tidak dapat dibelokkan.
  2. Kolom "as" akan berupa array, seperti contoh di atas.
  3. Pilihan "localField" dan "foreignField" pada tahap $ lookup akan diperlakukan sebagai null untuk tujuan yang cocok jika mereka tidak ada di koleksi masing-masing ( $ $ lookup memiliki contoh sempurna tentang itu).

Jadi, sebagai kesimpulan, jika Anda ingin mengkonsolidasikan kedua koleksi, memiliki, dalam hal ini, bidang datar salinan_sold dengan total salinan terjual, Anda harus bekerja sedikit lebih, mungkin menggunakan koleksi perantara yang akan, kemudian, menjadi $ keluar ke koleksi akhir.

Bruno Krebs
sumber
hai di sana, bisakah Anda memberi tahu apa yang akan menjadi cara yang dioptimalkan untuk mengelola data seperti ini: Pengguna, file.files dan file.chunks adalah tiga koleksi, saya ingin pengguna tertentu dengan semua file terkait dalam tanggapan apakah mungkin.? {"name": "batMan", "email ':" [email protected] "," files ": [{file1}, {file2}, {file3}, .... seterusnya]}
mfaisalhyder
Contoh dokumentasi resmi untuk solusi di atas dapat ditemukan di sini: docs.mongodb.com/manual/reference/operator/aggregation/lookup
Jakub Czaplicki
4
Sebenarnya jawaban saya sudah memiliki tiga tautan ke dokumentasi resmi. Tetapi terima kasih atas kontribusi Anda. @JakubCzaplicki
Bruno Krebs
2
Saya mungkin mengalami kerusakan otak total (kemungkinan besar) tetapi dalam $lookupseharusnya tidak semua "localField" dan "foreignField" sama dengan "isbn"? bukan "_id" dan "isbn"?
Dev01
13

Jika tidak ada sisipan massal ke mongodb, kami lilitkan semua objek di dalam small_collectiondan masukkan satu per satu ke big_collection:

db.small_collection.find().forEach(function(obj){ 
   db.big_collection.insert(obj)
});
Hieu Le
sumber
db.colleciton.insert ([{}, {}, {}]) Sisipkan menerima array.
augurone
2
ini berfungsi baik untuk koleksi kecil, tapi jangan lupa untuk memigrasi indeks :)
Sebastien Lorber
12

Contoh sangat mendasar dengan $ lookup.

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

Di sini digunakan

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

Dari pada

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

Karena {$ melepas: "$ userRoleData"} ini akan kembali kosong atau hasil 0 jika tidak ada catatan yang cocok dengan $ lookup.

Anish Agarwal
sumber
11

Melakukan serikat di MongoDB dengan cara 'SQL UNION' dimungkinkan menggunakan agregasi bersama dengan pencarian, dalam satu permintaan. Berikut adalah contoh yang telah saya uji yang berfungsi dengan MongoDB 4.0:

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

Berikut ini penjelasan cara kerjanya:

  1. Instantiate aggregatedari setiap koleksi database Anda yang memiliki setidaknya satu dokumen itu. Jika Anda tidak dapat menjamin koleksi apa pun dari basis data Anda tidak akan kosong, Anda dapat mengatasi masalah ini dengan membuat di basis data Anda semacam koleksi 'dummy' yang berisi satu dokumen kosong di dalamnya yang akan ada di sana khusus untuk melakukan kueri gabungan.

  2. Jadikan tahap pertama dari pipa Anda menjadi { $limit: 1 }. Ini akan menghapus semua dokumen koleksi kecuali yang pertama.

  3. Keluarkan semua bidang dokumen yang tersisa dengan menggunakan $projectpanggung:

    { $project: { _id: '$$REMOVE' } }
  4. Agregat Anda sekarang berisi satu dokumen kosong. Saatnya menambahkan pencarian untuk setiap koleksi yang ingin Anda satukan bersama. Anda dapat menggunakan pipelinebidang ini untuk melakukan pemfilteran tertentu, atau meninggalkan localFielddan foreignFieldsebagai nol untuk mencocokkan seluruh koleksi.

    { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
    { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
    { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
  5. Anda sekarang memiliki agregat yang berisi satu dokumen yang berisi 3 array seperti ini:

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

    Anda kemudian dapat menggabungkan mereka menjadi satu array menggunakan $project panggung bersama dengan $concatArraysoperator agregasi:

    {
      "$project" :
      {
        "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
      }
    }
  6. Anda sekarang memiliki agregat yang berisi satu dokumen, yang di dalamnya terdapat array yang berisi gabungan koleksi Anda. Yang masih harus dilakukan adalah menambahkan$unwind dan $replaceRoottahap untuk membagi array Anda menjadi dokumen terpisah:

    { $unwind: "$Union" },
    { $replaceRoot: { newRoot: "$Union" } }
  7. Voa. Anda sekarang memiliki kumpulan hasil yang berisi koleksi yang ingin Anda satukan bersama. Anda kemudian dapat menambahkan lebih banyak tahapan untuk memfilternya lebih lanjut, mengurutkannya, menerapkan lewati () dan batas (). Apa pun yang Anda inginkan.

Sboisse
sumber
Kueri gagal dengan pesan "$ proyeksi memerlukan setidaknya satu bidang keluaran".
abhishek_ganta
@abhishek Jika Anda mengetahui bahwa itu karena Anda mencoba menghapus semua bidang dari dokumen tunggal dalam satu tahap proyeksi. MongoDB tidak akan membiarkan Anda melakukan ini. Untuk mengatasinya, Anda perlu melakukan 2 proyeksi berturut-turut di mana yang pertama menghapus semuanya kecuali _id, dan yang kedua menghapus _id yang tersisa.
sboisse
@abhishek Saya telah menyederhanakan kueri dengan mengganti $ stage proyek dalam satu yang menggunakan variabel '$$ HAPUS'. Saya juga menambahkan contoh konkret yang bisa Anda salin dan tempel langsung di penguji kueri Anda untuk melihat itu berfungsi.
sboisse
@sboisse, solusi ini berfungsi untuk koleksi yang lebih kecil, namun, jika saya ingin melakukan ini pada koleksi besar, (100.000+ dokumen), saya mengalami "Ukuran total dokumen dalam collectionToUnion1 melebihi ukuran dokumen maksimum". Dalam dokumen tersebut, disarankan untuk meletakkan $ relax langsung setelah pencarian $ untuk menghindari pembuatan dokumen perantara yang besar. Saya belum berhasil memodifikasi solusi ini menggunakan metode itu. Sudahkah Anda mengalami masalah ini dan harus menggunakan metode itu? Tautan ke dokumen yang saya ref to: [link] ( docs.mongodb.com/manual/core/aggregation-pipeline-optimization/… )
lucky7samson
@ lucky7samson sayangnya jumlah data yang harus saya tangani tidak sebesar itu. Jadi saya tidak harus menghadapi masalah yang Anda maksud. Dalam kasus saya, saya bisa menerapkan pemfilteran pada koleksi untuk mencari sebelum menggabungkan catatan dengan yang lain, sehingga jumlah data untuk penyatuan cukup kecil.
Sboisse
9

gunakan beberapa $ lookup untuk beberapa koleksi secara agregasi

pertanyaan:

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

hasil:

{
    "_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
    }
}
KARTHIKEYAN.A
sumber
1

Mongorestore memiliki fitur penambahan ini di atas apa pun yang sudah ada dalam database, sehingga perilaku ini dapat digunakan untuk menggabungkan dua koleksi:

  1. koleksi mongodump1
  2. collection2.rename (collection1)
  3. mongorestore

Belum mencobanya, tetapi mungkin kinerjanya lebih cepat daripada pendekatan peta / pengurangan.

shauli
sumber
1

Mulai Mongo 4.4, kami dapat mencapai gabungan ini dalam pipa agregasi dengan $unionWithmenggabungkan tahap agregasi baru dengan operator $groupbaru $accumulator:

// > 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" } }
  • $unionWithmenggabungkan catatan dari koleksi yang diberikan dalam dokumen yang sudah ada dalam pipa agregasi. Setelah 2 tahap penyatuan, kami memiliki semua catatan pengguna, buku, dan film dalam pipa.

  • Kami kemudian $groupmerekam $userdan mengakumulasikan item menggunakan $accumulatoroperator yang memungkinkan akumulasi dokumen kustom ketika dikelompokkan:

    • bidang yang kami minati terakumulasi didefinisikan dengan accumulateArgs.
    • init mendefinisikan negara yang akan diakumulasikan saat kita mengelompokkan elemen.
    • yang accumulatefungsi memungkinkan melakukan tindakan kustom dengan catatan yang dikelompokkan dalam rangka membangun akumulasi negara. Misalnya, jika item yang dikelompokkan memiliki bookbidang yang ditentukan, maka kami memperbaruibooks bagian negara.
    • mergedigunakan untuk menggabungkan dua kondisi internal. Ini hanya digunakan untuk agregasi yang berjalan pada kelompok yang terbengkalai atau ketika operasi melebihi batas memori.
Xavier Guihot
sumber
apakah mungkin untuk mengambil output serupa untuk: versi 4.2.6
Nixit Patel
0

Ya, Anda dapat: Ambil fungsi utilitas yang saya tulis hari ini:

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

Anda dapat melewati ke fungsi ini sejumlah koleksi, yang pertama akan menjadi target. Semua koleksi sisanya adalah sumber yang akan ditransfer ke target.

Shangab
sumber
-1

Cuplikan kode. Courtesy-Banyak posting di stack overflow termasuk yang ini.

 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();
Vipul Mehta
sumber
-2

Anda harus melakukannya di lapisan aplikasi Anda. Jika Anda menggunakan ORM, itu bisa menggunakan anotasi (atau yang serupa) untuk menarik referensi yang ada di koleksi lain. Saya hanya pernah bekerja dengan Morfia , dan @Referencepenjelasannya mengambil entitas yang direferensikan ketika ditanya, jadi saya dapat menghindari melakukannya sendiri dalam kode.

lobster1234
sumber