Как выполнить эквивалент SQL Join в MongoDB? - PullRequest
451 голосов
/ 28 февраля 2010

Как выполнить эквивалент SQL-соединения в 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"  }

Есть ли способ получить все комментарии с определенным полем (например, ... find ({pid: 444})) и информацию о пользователе, связанную с каждым комментарием, за один раз?

В данный момент я сначала получаю комментарии, которые соответствуют моим критериям, затем вычисляю все uid в этом наборе результатов, получаю пользовательские объекты и объединяю их с результатами комментария. Похоже, я делаю это неправильно.

Ответы [ 19 ]

6 голосов
/ 27 марта 2018

$ lookup (агрегат)

Выполняет левое внешнее объединение с неотмеченной коллекцией в той же базе данных для фильтрации документов из «объединенной» коллекции для обработки. К каждому входному документу этап поиска $ добавляет новое поле массива, элементами которого являются совпадающие документы из «объединенной» коллекции. Этап поиска $ передает эти измененные документы на следующий этап. Этап $ lookup имеет следующие синтаксисы:

Матч равенства

Чтобы выполнить сравнение на равенство между полем из входных документов и полем из документов «объединенной» коллекции, этап $ 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>
     }
}

Операция будет соответствовать следующему псевдо-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

5 голосов
/ 22 октября 2016

До 3.2.6 , Mongodb не поддерживает запрос соединения, как и MySQL. ниже решение, которое работает для вас.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])
3 голосов
/ 18 октября 2012

Вы можете запускать SQL-запросы, включая объединение в MongoDB, с помощью mongo_fdw из Postgres.

2 голосов
/ 14 апреля 2015

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;


            });

    });
1 голос
/ 13 декабря 2017

Вы можете сделать это с помощью конвейера агрегации, но написать это самостоятельно - боль.

Вы можете использовать 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 голосов
/ 07 сентября 2012

playORM может сделать это для вас, используя S-SQL (Scalable SQL), который просто добавляет разбиение, так что вы можете делать соединения внутри разделов.

0 голосов
/ 24 марта 2015

Мы можем объединить две коллекции, используя подзапрос 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"
        }
    ]
}`

Надеюсь, это поможет.

0 голосов
/ 23 сентября 2015

Я думаю, если вам нужны нормализованные таблицы данных - вам нужно попробовать другие решения для баз данных.

Но я основал это решение для 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'] });

Решение

Нам нужно пройтись по каждому фильму в документе Actor и создать каждый фильм отдельно.

Улов здесь находится в фазе уменьшения. Мы не можем создать массив из фазы сокращения, поэтому мы должны построить массив Actors внутри возвращаемого документа «value».

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

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

Обратите внимание, что actor_list на самом деле является объектом javascript, который содержит массив. Также обратите внимание, что карта испускает ту же структуру.

Запустите следующую команду, чтобы выполнить отображение / уменьшение, вывести ее в коллекцию "pivot" и распечатать результат:

printjson (db.actors.mapReduce (map, lower, "pivot")); db.pivot.find () Foreach (printjson).

Вот пример выходных данных, обратите внимание, что в «Pretty Woman» и «Runaway Bride» есть и «Richard Gere», и «Julia Roberts».

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

0 голосов
/ 04 сентября 2015

Нет, не похоже, что вы делаете это неправильно. Присоединения 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 все еще вещь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...