Як виконати еквівалент SQL Join в MongoDB?


498

Як виконати еквівалент SQL Join в MongoDB?

Наприклад, скажіть, що у вас є дві колекції (користувачі та коментарі), і я хочу перетягнути всі коментарі з pid = 444 разом із інформацією про користувача для кожного.

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

Чи є спосіб витягнути всі коментарі до певного поля (наприклад, ... знайти ({pid: 444})) та інформацію про користувачів, пов'язану з кожним коментарем, за один раз?

На даний момент я спочатку отримую коментарі, які відповідають моїм критеріям, а потім з'ясовує всі uid у цьому наборі результатів, отримує об’єкти користувача та об'єднує їх з результатами коментаря. Здається, я роблю це неправильно.


35
Остання відповідь на це питання, мабуть, є найбільш актуальною, оскільки MongoDB 3.2+ реалізував рішення для з'єднання під назвою $ lookup. Думав, що я натисну його сюди, бо, можливо, не всі прочитають до кінця. stackoverflow.com/a/33511166/2593330
thefourtheye

6
Правильно, пошук $ було введено в MongoDB 3.2. Деталі можна знайти на docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Відповіді:


306

Відповідно до Монго 3.2, відповіді на це питання здебільшого не є правильними. Новий оператор пошуку $, доданий до трубопроводу агрегації, по суті ідентичний лівому зовнішньому з'єднанню:

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

З документів:

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

Звичайно, Mongo не є реляційною базою даних, і розробники обережно рекомендують конкретні випадки використання для пошуку $ $, але принаймні, як на 3.2 виконання об'єднання, тепер можливе використання MongoDB.


@clayton: Як щодо більше двох колекцій?
Dipen Dedania

1
@DipenDedania просто додає додаткові етапи пошуку $ до конвеєрного конвеєра.
Клейтон Гулік

Я не можу приєднатися до будь-якого поля в масиві в лівій колекції з відповідним ідентифікатором у правій collection.can хтось мені допоможе ??
Prateek Singh

1
Я трохи збентежений з цього приводу - чи є якийсь спосіб вказати, що ви хочете лише певні документи у колекції "from", або він автоматично приєднується до всіх у db відразу?
користувач3413723

Цікаво, чи є в останніх даних Spring MongoDB підтримка 3.2?
gtiwari333

142

Ця сторінка на офіційному сайті mongodb стосується саме цього питання:

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

Коли ми відображаємо наш список історій, нам потрібно буде вказати ім’я користувача, який опублікував історію. Якщо ми використовували реляційну базу даних, ми могли б виконати об'єднання користувачів та магазинів та отримати всі наші об’єкти за один запит. Але MongoDB не підтримує приєднання, і, часом, вимагає трохи денормалізації. Тут це означає кешування атрибута 'username'.

Релаксаційні пуристи вже можуть відчувати себе непросто, ніби ми порушуємо якийсь універсальний закон. Але маємо на увазі, що колекції MongoDB не рівнозначні реляційним таблицям; кожен служить унікальній меті дизайну. Нормована таблиця містить атомний, ізольований фрагмент даних. Документ, однак, більш детально представляє об'єкт у цілому. Що стосується сайту соціальних новин, можна стверджувати, що ім’я користувача є суттєвим для опублікованої історії.


51
@dudelgrincen - це зміна парадигми від нормалізації та реляційних баз даних. Мета NoSQL - це дуже швидко читати та записувати з бази даних. З BigData ви матимете багато прикладних серверів та серверів з меншими числами в БД. Ви очікуєте робити мільйони транзакцій в секунду. Вивантажте важку підйомну базу даних та поставте її на рівень програми. Якщо вам потрібен глибокий аналіз, ви запускаєте завдання інтеграції, яке передає ваші дані в базу даних OLAP. Ви все одно не повинні отримувати багато глибоких запитів від своїх OLTP-dbs.
Снігопад

18
@dudelgrincen Я також повинен сказати, що це не для кожного проекту чи дизайну. Якщо у вас є щось, що працює в базі даних типу SQL, навіщо це змінювати? Якщо ви не можете масажувати вашу схему роботи з noSQL, тоді не робіть цього.
Снігопад

9
Міграції та схеми, що постійно розвиваються, також набагато простіше в управлінні в системі NoSQL.
Justin

14
Що робити, якщо користувач має 3,540 публікацій на веб-сайті, і він змінює своє ім’я користувача у профілі? Чи слід оновлювати кожну публікацію новим іменем користувача?
Іво Перейра

2
@IvoPereira Так і саме тому слід уникати моделювання даних таким чином. Є стаття, яка пояснює той самий сценарій та його наслідки: Чому ніколи не слід використовувати MongoDB
Омід

138

Ми можемо об'єднати / об'єднати всі дані всередині однієї колекції з легкою функцією в декілька рядків за допомогою клієнтської консолі mongodb, і тепер ми зможемо виконати потрібний запит. Нижче наведено повний приклад:

.- Автори:

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

.- Категорії:

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

.- Книги

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

.- Кредитування книг

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

.- Магія:

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

.- Отримайте нові дані колекції:

db.booksReloaded.find().pretty()

.- Відповідь :)

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

Я сподіваюся, що ці рядки можуть вам допомогти.


2
мені цікаво, чи можна цей самий код запустити за допомогою доктрини mongodb?
абат

4
Що відбувається, коли один із посилальних об’єктів отримує оновлення? Чи оновлення автоматично відображається в об’єкті книги? Або цю петлю потрібно запустити знову?
balupton

14
Це добре, якщо ваші дані мало. Він збирається донести кожен вміст книги до вашого клієнта, а потім отримати кожну категорію, позику та авторів по одному. Щойно ваших книг налічується тисячами, це піде насправді дуже повільно. Кращою технікою може бути використання конвеєрного конвеєра та виведення об'єднаних даних в окрему колекцію. Дозвольте мені повернутися до цього ще раз. Я додам, що відповідь.
Sandeep Giri

Чи можете ви адаптувати свій алгоритм до цього іншого прикладу? stackoverflow.com/q/32718079/287948
Пітер Краус

1
@SandeepGiri Як я можу зробити сукупний конвеєр, оскільки у мене справді дуже інтенсивні дані в розділеному зборі, потрібно приєднатися ??
Яссін Абдул-Рахман

38

Ви повинні зробити так, як ви описали. MongoDB - це нереляційна база даних і не підтримує приєднання.


4
Здається, неправильна продуктивність приходить з фонового сервера sql, але це може бути не так вже й погано з документом db?
terjetyl

3
і на тлі сервера sql, я би вдячний, щоб MongoDB взяв 'набір результатів' (з вибраними поверненими полями) як вхід для нового запиту за один раз, як і вкладені запити в SQL
Stijn Sanders

1
@terjetyl Ви повинні дійсно планувати це. Які поля ви збираєтеся представляти на передній частині, якщо це обмежена кількість в окремому вікні, то ви приймаєте їх як вбудовані документи. Головне - не потрібно робити приєднання. Якщо ви хочете зробити глибокий аналіз, ви робите це після факту в іншій базі даних. Виконайте завдання, яке перетворює дані в куб OLAP для досягнення оптимальної продуктивності.
Снігопад

4
З версії mongo 3.2 підтримуються ліві з'єднання.
Сомнат Мулук

18

Як зазначають інші, ви намагаєтеся створити реляційну базу даних з жодної реляційної бази даних, яку ви насправді не хочете робити, але все одно, якщо у вас є випадок, що вам доведеться це зробити, це рішення, яке ви можете використовувати. Ми спочатку робимо пошук випередження у колекції A (або у вашому випадку користувачів), а потім отримуємо кожен предмет як об’єкт, потім використовуємо властивість об'єкта (у вашому випадку uid) для пошуку у нашій другій колекції (у вашому випадку коментарі), якщо ми можемо знайти його, тоді у нас є відповідність, і ми можемо надрукувати або зробити щось із цим. Сподіваюся, це допоможе вам і удачі :)

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

Ви не знайдете предмет, про який ми зараз розбираємося?
Skarlinski

18

Завдяки правильному поєднанню $ lookup , $ project та $ match , ви можете приєднатися до неодноразових таблиць за кількома параметрами. Це тому, що їх можна прикувати кілька разів.

Припустимо, ми хочемо зробити наступне ( посилання )

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: Зв’яжіть усі таблиці

ви можете $ шукати стільки таблиць, скільки вам потрібно.

$ lookup - по одному для кожної таблиці запиту

$ unind - оскільки дані денормалізовані правильно, інакше загорнуті в масиви

Код Python ..

db.LeftTable.aggregate([
                        # connect all tables

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

                        ])

Крок 2: Визначте всі умови

$ project : визначте всі умовні оператори тут, а також усі змінні, які ви хочете вибрати.

Код Python ..

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: Приєднайтесь до всіх умов

$ match - приєднуйтесь до всіх умов, використовуючи OR або AND і т.д. Їх може бути кілька.

$ проект : визначити всі умови

Код Python ..

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

                        ])

Таким чином можна зробити будь-яку комбінацію таблиць, умовних умов та з'єднань.


17

Ось приклад " колекції " * Колекції акторів та фільмів :

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

Він використовує .mapReduce()метод

* приєднатися - альтернатива приєднатися до баз даних, орієнтованих на документи


19
-1, це НЕ об'єднання даних із двох колекцій. Він використовує дані з однієї колекції (акторів), що обертаються навколо. Так що речі, які були ключами, тепер є значеннями, а значення тепер - це ключі ... дуже різні, ніж ПРИЄДНАЙТЕСЬ.
Еван Теран

12
Це саме те, що вам потрібно зробити, MongoDB не є реляційним, а орієнтованим на документи. MapReduce дозволяє грати з даними з великою продуктивністю (можна використовувати кластер тощо), але навіть для простих випадків це дуже корисно!
Томас Деко

14

Ви можете приєднатись до двох колекцій у Монго, використовуючи пошук, який пропонується у версії 3.2. У вашому випадку запит був би

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

або ви також можете приєднатись до користувачів, тоді зміни, як зазначено нижче, будуть незначні.

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

Він буде працювати так само, як лівий і правий приєднання до SQL.


11

Це залежить від того, що ви намагаєтеся зробити.

Наразі у вас це створено як нормалізована база даних, що добре, і спосіб, яким ви це робите, є відповідним.

Однак є й інші способи зробити це.

У вас може бути колекція публікацій, яка містить коментарі до кожної публікації із посиланнями на користувачів, до яких ви можете ітеративно запитувати. Ви можете зберігати ім'я користувача з коментарями, ви можете зберігати їх в одному документі.

Річ у NoSQL - це те, що він розроблений для гнучких схем та дуже швидкого читання та запису. У типовій фермі Big Data база даних є найбільшим вузьким місцем, у вас менше двигунів баз даних, ніж у серверів додатків і передніх серверів ... вони дорожчі, але потужніші, а також місце на жорсткому диску порівняно дешево. Нормалізація виходить із концепції намагання заощадити місце, але це пов'язано з витратами на те, щоб ваші бази даних виконували складні з'єднання та перевіряли цілісність відносин, виконували каскадні операції. Все це врятує розробникам деякі головні болі, якщо вони правильно розробили базу даних.

Якщо ви погоджуєтесь з NoSQL, що надмірність та простір для зберігання не є проблемами через їх вартість (як у час процесора, необхідний для оновлення, так і витрати на жорсткий диск для зберігання додаткових даних), денормалізація не є проблемою (для вбудованих масивів, які стають сотні тисяч предметів, це може бути проблемою продуктивності, але в більшості випадків це не проблема). Крім того, у вас буде кілька серверів додатків і фронтальних серверів для кожного кластеру баз даних. Дозвольте їм зробити важкий підйом з'єднань і нехай сервери баз даних дотримуються читання та запису.

TL; DR: Те, що ви робите, це добре, і є інші способи зробити це. Перегляньте моделі моделей даних документації mongodb, щоб отримати кілька чудових прикладів. http://docs.mongodb.org/manual/data-modeling/


8
"Нормалізація виходить із концепції намагання заощадити простір". Нормалізація ІМХО виходить із концепції уникнення надмірності. Скажімо, ви зберігаєте ім’я користувача разом із поштовим блогом. Що робити, якщо вона вийде заміж? У ненормованій моделі вам доведеться пробиратися через усі пости та змінювати назву. У нормалізованій моделі зазвичай ви змінюєте ОДИН запис.
DanielKhan

@DanielKhan, що запобігає надмірності та заощаджує простір, є подібними поняттями, але при повторному аналізі я згоден, надмірність є першопричиною цієї конструкції. Я перемовляю. Дякую за замітку.
Снігопалення

11

Існує специфікація, що багато драйверів підтримують, що називається DBRef.

DBRef - це більш формальна специфікація для створення посилань між документами. DBRefs (як правило) включає ім'я колекції, а також ідентифікатор об'єкта. Більшість розробників використовують DBRefs, лише якщо колекція може змінюватися від одного документа до іншого. Якщо ваша колекція посилань завжди буде однаковою, ручні посилання, описані вище, є більш ефективними.

Взяте з документації MongoDB: Моделі даних> Посилання на модель даних > Посилання на базу даних


11

$ пошук (агрегація)

Здійснює ліве зовнішнє приєднання до незакритої колекції в тій самій базі даних, щоб фільтрувати документи з колекції "приєднався" для обробки. До кожного вхідного документа етап пошуку $ додає нове поле масиву, елементами якого є відповідні документи з колекції "приєднався". Етап пошуку $ переходить ці перероблені документи на наступний етап. На етапі пошуку $ є такі синтаксиси:

Матч рівності

Щоб виконати збіг рівності між полем із вхідних документів із полем з документів колекції "приєднався", етап пошуку $ має такий синтаксис:

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

Операція відповідатиме наступному оператору pseudo-SQL:

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

URL Монго


суб-запит абсолютно інший, ніж приєднання, якщо ваша ліва сторона столу величезна, підзапит означає, що кожен рядок повинен робити сам запит. це стане дуже повільним. приєднання дуже швидко в sql.
yww325

8

До 3.2.6 , Mongodb не підтримує запит приєднання, як mysql. нижче рішення, яке працює для вас.

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

4

Ви можете запускати SQL запити, включаючи приєднатися до MongoDB за допомогою mongo_fdw з Postgres.


3

MongoDB не дозволяє приєднуватися, але ви можете використовувати плагіни для цього. Перевірте плагін mongo-join. Це найкраще, і я його вже використав. Ви можете встановити його за допомогою npm безпосередньо так npm install mongo-join. Ви можете ознайомитися з повною документацією з прикладами .

(++) дійсно корисний інструмент, коли нам потрібно приєднатися до (N) колекцій

(-) ми можемо застосувати умови лише на верхньому рівні запиту

Приклад

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

Ви можете зробити це за допомогою конвеєра агрегації, але боляче написати це самостійно.

Ви можете використовувати mongo-join-queryдля створення конвеєрного конвеєра автоматично зі свого запиту.

Ось як виглядатиме ваш запит:

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

У вашому результаті буде об’єкт користувача в uidполі, і ви можете зв'язати стільки рівнів, скільки завгодно. Ви можете заповнити посилання на користувача, який посилається на Команду, яка посилається на щось інше тощо.

Відмова : Я писав, mongo-join-queryщоб вирішити цю точну проблему.


0

playORM може зробити це для вас, використовуючи S-SQL (масштабований SQL), який просто додає розділ таким чином, що ви можете робити приєднання до розділів.


-2

Ні, це не здається, ви робите неправильно. MongoDB приєднується до "клієнта". Приблизно так, як ви сказали:

На даний момент я спочатку отримую коментарі, які відповідають моїм критеріям, а потім з'ясовує всі uid у цьому наборі результатів, отримує об’єкти користувача та об'єднує їх з результатами коментаря. Здається, я роблю це неправильно.

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.

Це не "справжнє" приєднання, але насправді набагато корисніше, ніж приєднання до SQL, оскільки вам не доведеться мати справу з дублюючими рядками для "багато" односторонніх приєднань, замість того, щоб прикрасити початково вибраний набір.

На цій сторінці є багато дурниць і FUD. Виявляється, через 5 років MongoDB - це все-таки річ.


"вам не доведеться мати справу з дублюючими рядками для" багатьох "однобічних з'єднань" - не маю на увазі, що ви маєте на увазі під цим ". Ви можете уточнити?
Марк Амерді

1
@MarkAmery, звичайно. У SQL відносини nn повернуть повторювані рядки. Наприклад, друзі. Якщо Боб дружить з Мері та Джейн, ви отримаєте 2 ряди для Боба: Боб, Мері та Боб, Джейн. 2 Бобс - брехня, є лише один Боб. З приєднанням до клієнта ви можете почати з Боб і прикрасити, як вам подобається: Боб, "Мері і Джейн". SQL давайте вам це робити з підзапитами, але це робиться робота на db-сервері, яку можна зробити на клієнті.
Майкл Коул

-3

Я думаю, якщо вам потрібні нормовані таблиці даних - вам потрібно спробувати деякі інші рішення бази даних.

Але я вважаю, що це роздум для MOngo на Git. До речі, в коді вставок - він має ім'я фільму, але ідентифікатор фільму Noi .

Проблема

У вас є колекція акторів з масивом зроблених ними фільмів.

Ви хочете створити колекцію фільмів із набором акторів у кожному.

Деякі вибіркові дані

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

Рішення

Нам потрібно переглядати кожен фільм у документі "Актор" і випромінювати кожен фільм окремо.

Улов тут знаходиться у фазі зменшення. Ми не можемо випромінювати масив із фази зменшення, тому ми повинні створити масив Actors всередині документа, що повертається "значення".

Код
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;
}

Зверніть увагу, як насправді актор_list - це об’єкт javascript, який містить масив. Також зауважте, що карта випромінює ту саму структуру.

Виконайте наступне, щоб виконати карту / зменшити, вивести її у колекцію «зведення» та надрукувати результат:

printjson (db.actors.mapReduce (карта, зменшення, "поворот")); db.pivot.find (). forEach (printjson);

Ось вибірка зразка, зауважте, що "Гарненька жінка" та "Побіжна наречена" мають і "Річарда Гіра", і "Джулію Робертс".

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


Зауважте, що більша частина змісту цієї відповіді (тобто біт, зрозумілий англійською мовою) скопійована з кухонної книги MongoDB за посиланням GitHub, яке надав відповідь.
Марк Амері

-4

Ми можемо об'єднати дві колекції, використовуючи підзапроси mongoDB. Ось приклад, Commentss--

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

Підзапит MongoDB для JOIN--

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

Отримайте результат від щойно створеної колекції--

db.newCommentUsers.find().pretty()

Результат--

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

Сподіваюся, що це допоможе.


7
Чому ви в основному копіювали цю майже однакову однорічну відповідь? stackoverflow.com/a/22739813/4186945
Хакель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.