Como executo o equivalente de junção SQL no MongoDB?

498

Como executo o equivalente de junção SQL no MongoDB?

Por exemplo, digamos que você tenha duas coleções (usuários e comentários) e desejo extrair todos os comentários com pid = 444, juntamente com as informações do usuário de cada uma.

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

Existe uma maneira de extrair todos os comentários com um determinado campo (por exemplo, ... find ({pid: 444})) e as informações do usuário associadas a cada comentário de uma só vez?

No momento, estou recebendo primeiro os comentários que correspondem aos meus critérios e, depois, descobrindo todos os uid nesse conjunto de resultados, obtendo os objetos do usuário e os mesclando com os resultados do comentário. Parece que estou fazendo errado.

O desconhecido
fonte
35
A última resposta para esta pergunta é provavelmente a mais relevante, já que o MongoDB 3.2+ implementou uma solução de junção chamada $ lookup. Pensei que eu iria empurrá-lo aqui, porque talvez nem todos vão ler o fundo. stackoverflow.com/a/33511166/2593330
thefourtheye
6
Correto, a pesquisa de $ foi introduzida no MongoDB 3.2. Detalhes podem ser encontrados em docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Respostas:

306

No Mongo 3.2, as respostas para essa pergunta não estão mais corretas. O novo operador $ lookup adicionado ao pipeline de agregação é essencialmente idêntico a uma junção externa esquerda:

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

Dos documentos:

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

É claro que o Mongo não é um banco de dados relacional, e os desenvolvedores estão tomando cuidado para recomendar casos de uso específicos para $ lookup, mas pelo menos a partir do 3.2, a junção agora é possível com o MongoDB.

Clayton Gulick
fonte
@clayton: Que tal mais de duas coleções?
Dipen Dedania
1
O @DipenDedania apenas adiciona estágios de pesquisa $ adicionais ao pipeline de agregação.
Clayton Gulick
Eu não posso juntar qualquer campo na matriz na coleção da esquerda com seu ID correspondente na coleção da direita. Alguém pode me ajudar?
Prateek Singh
1
Estou um pouco confuso sobre isso - existe alguma maneira de especificar que você deseja apenas determinados documentos na coleção "de" ou ele se junta automaticamente a todos no banco de dados de uma só vez?
user3413723
Imaginando se o Spring Data MongoDB mais recente suporta o 3.2?
gtiwari333
142

Esta página no site oficial do mongodb aborda exatamente esta questão:

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

Quando exibimos nossa lista de histórias, precisamos mostrar o nome do usuário que postou a história. Se estivéssemos usando um banco de dados relacional, poderíamos realizar uma junção em usuários e lojas e obter todos os nossos objetos em uma única consulta. Mas o MongoDB não suporta junções e, às vezes, requer um pouco de desnormalização. Aqui, isso significa armazenar em cache o atributo 'nome de usuário'.

Os puristas relacionais podem já estar se sentindo desconfortáveis, como se estivéssemos violando alguma lei universal. Mas tenhamos em mente que as coleções do MongoDB não são equivalentes às tabelas relacionais; cada um serve a um objetivo de design exclusivo. Uma tabela normalizada fornece uma porção atômica e isolada de dados. Um documento, no entanto, representa mais de perto um objeto como um todo. No caso de um site de notícias sociais, pode-se argumentar que um nome de usuário é intrínseco à história que está sendo postada.

William Stein
fonte
51
@dudelgrincen é uma mudança de paradigma da normalização e dos bancos de dados relacionais. O objetivo de um NoSQL é ler e escrever do banco de dados muito rapidamente. Com o BigData, você terá dezenas de servidores de aplicativos e front-end com números mais baixos nos bancos de dados. Espera-se que você faça milhões de transações por segundo. Descarregue o trabalho pesado do banco de dados e coloque-o no nível do aplicativo. Se você precisar de uma análise profunda, execute um trabalho de integração que coloque seus dados em um banco de dados OLAP. Você não deveria estar recebendo muitas consultas profundas dos seus dbs OLTP de qualquer maneira.
quer
18
@dudelgrincen Eu também devo dizer que não é para todo projeto ou design. Se você tem algo que funciona em um banco de dados do tipo SQL, por que alterá-lo? Se você não pode massagear seu esquema para trabalhar com o noSQL, não o faça.
Snowburnt
9
Migrações e esquemas em constante evolução também são muito mais fáceis de gerenciar em um sistema NoSQL.
Justin
14
E se o usuário tiver 3.540 postagens no site e ele alterar seu nome de usuário no perfil? Todas as postagens devem ser atualizadas com o novo nome de usuário?
Ivo Pereira
2
@IvoPereira Sim, e é exatamente por isso que devemos evitar modelar dados dessa maneira. Há um artigo que explica o mesmo cenário e suas consequências: Por que você nunca deve Use MongoDB
Omid
138

Podemos mesclar / juntar todos os dados em apenas uma coleção, com uma função fácil em poucas linhas, usando o console do cliente mongodb, e agora podemos realizar a consulta desejada. Abaixo um exemplo completo,

.- Autores:

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

.- Categorias:

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

.- Livros

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

.- Empréstimos de livros

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

.- A mágica:

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

.- Obtenha os novos dados de coleta:

db.booksReloaded.find().pretty()

.- Resposta :)

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

Espero que estas linhas possam ajudá-lo.

Orlando Becerra
fonte
2
Eu estou querendo saber se esse mesmo código pode ser executado usando a doutrina mongodb?
Abbood 30/05
4
O que acontece quando um dos objetos de referência recebe uma atualização? Essa atualização reflete automaticamente no objeto de livro? Ou esse loop precisa ser executado novamente?
balupton
14
Tudo bem, desde que seus dados sejam pequenos. Ele trará o conteúdo de cada livro ao seu cliente e buscará cada categoria, empréstimos e autores, um por um. No momento em que seus livros estão em milhares, isso seria realmente muito lento. Uma técnica melhor provavelmente seria usar o pipeline de agregação e gerar os dados mesclados em uma coleção separada. Deixe-me voltar a isso novamente. Vou acrescentar que uma resposta.
precisa
Você pode adaptar seu algoritmo a esse outro exemplo? stackoverflow.com/q/32718079/287948
Peter Krauss
1
@SandeepGiri como posso fazer o pipeline agregado, pois tenho dados realmente intensivos na coleta separada?
Yassine Abdul-Rahman
38

Você tem que fazer isso da maneira que você descreveu. O MongoDB é um banco de dados não relacional e não suporta junções.

Otto Allmendinger
fonte
4
Parece desempenho incorreto sábio vindo de um fundo de servidor sql, mas talvez não seja tão ruim com um documento db?
Terjetyl
3
de um servidor sql em segundo plano, também gostaria que o MongoDB aceitasse um 'conjunto de resultados' (com campos retornados selecionados) como entrada para uma nova consulta de uma só vez, como consultas aninhadas no SQL
Stijn Sanders
1
@terjetyl Você precisa realmente planejar isso. Quais campos você apresentará no front end, se for um valor limitado em uma exibição individual, você os tomará como documentos incorporados. A chave é não precisar fazer junções. Se você deseja fazer uma análise profunda, faça-o após o fato em outro banco de dados. Execute um trabalho que transforma os dados em um cubo OLAP para otimizar o desempenho.
quer
4
Na versão mongo 3.2, as junções esquerdas são suportadas.
Somnath Muluk
18

Como outros já apontaram, você está tentando criar um banco de dados relacional a partir de nenhum banco de dados relacional que você realmente não queira fazer, mas de qualquer maneira, se você tiver um caso de que precisa fazer isso aqui, é uma solução que você pode usar. Primeiro, fazemos uma busca foreach na coleção A (ou no seu caso, usuários) e, em seguida, obtemos cada item como um objeto, depois usamos a propriedade object (no seu caso, uid) para pesquisar em nossa segunda coleção (nos comentários do seu caso), se podemos encontrá-lo, então temos uma correspondência e podemos imprimir ou fazer algo com ela. Espero que isso ajude você e boa sorte :)

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
    }
});
grepit
fonte
Esse item não encontra o item em que estamos fazendo o loop atualmente?
21417 Skarlinski
18

Com a combinação certa de $ lookup , $ project e $ match , você pode associar várias tabelas em vários parâmetros. Isso ocorre porque eles podem ser encadeados várias vezes.

Suponha que desejemos fazer o seguinte ( referência )

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

Etapa 1: vincular todas as tabelas

você pode procurar quantas tabelas quiser.

$ lookup - um para cada tabela na consulta

$ unwind - porque os dados são desnormalizados corretamente, e outros são agrupados em matrizes

Código Python ..

db.LeftTable.aggregate([
                        # connect all tables

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

                        ])

Etapa 2: definir todos os condicionais

$ project : defina aqui todas as instruções condicionais, além de todas as variáveis ​​que você deseja selecionar.

Código 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
                        }}
                        ])

Etapa 3: junte todos os condicionais

$ match - junte-se a todas as condições usando OR ou AND etc. Pode haver vários deles.

$ project : undefine todos os condicionais

Código 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
                        }}

                        ])

Praticamente qualquer combinação de tabelas, condicionais e junções pode ser feita dessa maneira.

sbharti
fonte
17

Aqui está um exemplo de uma coleção "participar" * de atores e filmes :

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

Faz uso do .mapReduce()método

* join - uma alternativa para ingressar em bancos de dados orientados a documentos

antitóxico
fonte
19
-1, NÃO está juntando dados de duas coleções. Ele está usando dados de uma única coleção (atores) que giram em torno dos dados. Então, as coisas que eram chaves agora são valores e agora são chaves ... muito diferentes de um JOIN.
Evan Teran
12
É exatamente isso que você precisa fazer, o MongoDB não é relacional, mas orientado a documentos. O MapReduce permite jogar com dados com grande desempenho (você pode usar cluster, etc ...), mas mesmo em casos simples, é muito útil!
Thomas Decaux
14

Você pode ingressar em duas coleções no Mongo usando a pesquisa oferecida na versão 3.2. No seu caso, a consulta seria

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

ou você também pode participar com relação aos usuários, haverá uma pequena alteração, conforme indicado abaixo.

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

Funcionará da mesma maneira que a junção esquerda e direita no SQL.

jarry jafery
fonte
11

Depende do que você está tentando fazer.

Atualmente, você o configurou como um banco de dados normalizado, o que é bom, e a maneira como você o faz é apropriada.

No entanto, existem outras maneiras de fazê-lo.

Você pode ter uma coleção de postagens que incorporou comentários para cada postagem com referências aos usuários que você pode consultar iterativamente para obter. Você pode armazenar o nome do usuário com os comentários, todos eles em um documento.

O problema do NoSQL é que ele foi projetado para esquemas flexíveis e leitura e gravação muito rápidas. Em um farm típico de Big Data, o banco de dados é o maior gargalo, você tem menos mecanismos de banco de dados do que servidores de aplicativos e front-end ... eles são mais caros, mas mais poderosos, e também o espaço no disco rígido é muito barato. A normalização vem do conceito de tentar economizar espaço, mas tem um custo em fazer com que seus bancos de dados executem Junções complicadas e verifique a integridade dos relacionamentos, executando operações em cascata. Tudo isso poupa aos desenvolvedores algumas dores de cabeça se eles projetarem o banco de dados corretamente.

Com o NoSQL, se você aceitar que a redundância e o espaço de armazenamento não são problemas devido ao seu custo (tanto no tempo do processador necessário para fazer atualizações quanto nos custos do disco rígido para armazenar dados extras), a desnormalização não é um problema (para matrizes incorporadas que se tornam centenas de milhares de itens, pode ser um problema de desempenho, mas na maioria das vezes isso não é um problema). Além disso, você terá vários servidores de aplicativos e front-end para cada cluster de banco de dados. Faça com que eles façam o trabalho pesado das junções e deixem os servidores de banco de dados ficarem com leitura e gravação.

TL; DR: O que você está fazendo está bem e há outras maneiras de fazê-lo. Confira os padrões de modelo de dados da documentação do mongodb para alguns ótimos exemplos. http://docs.mongodb.org/manual/data-modeling/

Snowburnt
fonte
8
"A normalização vem do conceito de tentar economizar espaço", questiono isso. A normalização do IMHO vem do conceito de evitar redundância. Digamos que você armazene o nome de um usuário junto com uma postagem no blog. E se ela se casar? Em um modelo não normalizado, você terá que percorrer todas as postagens e alterar o nome. Em um modelo normalizado, você geralmente altera UM registro.
DanielKhan
O @DanielKhan, que evita a redundância e economiza espaço, é um conceito semelhante, mas, ao analisar novamente, concordo que a redundância é a causa raiz desse design. Eu vou reformular. Obrigado pela observação.
Snowburnt
11

Há uma especificação que muitos drivers suportam, chamada DBRef.

DBRef é uma especificação mais formal para criar referências entre documentos. Os DBRefs (geralmente) incluem um nome de coleção e um ID de objeto. A maioria dos desenvolvedores usa DBRefs apenas se a coleção puder mudar de um documento para o próximo. Se sua coleção referenciada sempre for a mesma, as referências manuais descritas acima são mais eficientes.

Retirado da documentação do MongoDB: Modelos de Dados> Referência de Modelo de Dados> Referências de Banco de Dados

Pickels
fonte
11

$ pesquisa (agregação)

Executa uma junção externa esquerda em uma coleção não compartilhada no mesmo banco de dados para filtrar documentos da coleção "unida" para processamento. Para cada documento de entrada, o estágio de pesquisa $ adiciona um novo campo de matriz cujos elementos são os documentos correspondentes da coleção "unida". O estágio $ lookup passa esses documentos reformulados para o próximo estágio. O estágio $ lookup possui as seguintes sintaxes:

Igualdade de Correspondência

Para executar uma correspondência de igualdade entre um campo dos documentos de entrada e um campo dos documentos da coleção "unida", o estágio $ lookup possui a seguinte sintaxe:

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

A operação corresponderia à seguinte instrução 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 Mongo

GoutamS
fonte
subconsulta é totalmente diferente de associação, se sua tabela do lado esquerdo for enorme, subconsulta significa que cada linha precisa fazer uma consulta. ficará muito lento. join é muito rápido em sql.
yww325 30/04
8

Antes da versão 3.2.6 , o Mongodb não suporta consulta de junção como o mysql. abaixo solução que funciona para você.

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

Você pode executar consultas SQL, incluindo junção no MongoDB com mongo_fdw do Postgres.

metdos
fonte
3

O MongoDB não permite junções, mas você pode usar plugins para lidar com isso. Verifique o plugin mongo-join. É o melhor e eu já o usei. Você pode instalá-lo usando o npm diretamente assim npm install mongo-join. Você pode conferir a documentação completa com exemplos .

(++) ferramenta realmente útil quando precisamos ingressar em (N) coleções

(-) podemos aplicar condições apenas no nível superior da consulta

Exemplo

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;


            });

    });
Amine_Dev
fonte
2

Você pode fazê-lo usando o pipeline de agregação, mas é difícil escrever você mesmo.

Você pode usar mongo-join-querypara criar o pipeline de agregação automaticamente a partir da sua consulta.

É assim que sua consulta seria:

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

Seu resultado teria o objeto de usuário no uidcampo e você poderá vincular quantos níveis desejar. Você pode preencher a referência ao usuário, que faz referência a uma equipe, que faz referência a outra coisa, etc.

Disclaimer : Eu escrevi mongo-join-querypara resolver este problema exato.

Marcelo Lazaroni
fonte
0

O playORM pode fazer isso por você usando S-SQL (Scalable Scalable), que apenas adiciona particionamento, de forma que você pode fazer junções dentro de partições.

Dean Hiller
fonte
-2

Não, não parece que você está fazendo errado. As junções do MongoDB são do "lado do cliente". Praticamente como você disse:

No momento, estou recebendo primeiro os comentários que correspondem aos meus critérios e, depois, descobrindo todos os uid nesse conjunto de resultados, obtendo os objetos do usuário e os mesclando com os resultados do comentário. Parece que estou fazendo errado.

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.

Não é uma junção "real", mas na verdade é muito mais útil do que uma junção SQL, porque você não precisa lidar com linhas duplicadas para junções de "muitos" lados, em vez de decorar o conjunto originalmente selecionado.

Há muita bobagem e FUD nesta página. Acontece que cinco anos depois o MongoDB ainda é uma coisa.

Michael Cole
fonte
'você não precisa lidar com linhas duplicadas para "muitas" junções de lados " - não faz ideia do que você quer dizer com isso. Você pode esclarecer?
Mark Amery
1
@MarkAmery, com certeza. No SQL, um relacionamento nn retornará linhas duplicadas. Por exemplo, amigos. Se Bob é amigo de Mary e Jane, você terá duas linhas para Bob: Bob, Mary e Bob, Jane. 2 Bobs é uma mentira, só há um Bob. Com as junções do lado do cliente, você pode começar com Bob e decorar como quiser: Bob, "Mary and Jane". O SQL permite que você faça isso com subconsultas, mas isso está funcionando no servidor db que pode ser feito no cliente.
Michael Cole
-3

Eu acho que, se você precisar de tabelas de dados normalizadas - precisará tentar outras soluções de banco de dados.

Mas eu encontrei essa solução para o MOngo no Git. Aliás, insere código - ele tem o nome do filme, mas o ID do filme noi .

Problema

Você tem uma coleção de atores com vários filmes que eles fizeram.

Você deseja gerar uma coleção de filmes com uma matriz de atores em cada um.

Alguns dados de amostra

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

Solução

Precisamos percorrer cada filme no documento do ator e emitir cada filme individualmente.

O problema aqui está na fase de redução. Não podemos emitir uma matriz da fase de redução; portanto, devemos criar uma matriz de atores dentro do documento "valor" retornado.

O código
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;
}

Observe como actor_list é realmente um objeto javascript que contém uma matriz. Observe também que o mapa emite a mesma estrutura.

Execute o seguinte para executar o mapa / reduzir, produza-o na coleção "pivô" e imprima o resultado:

printjson (db.actors.mapReduce (mapear, reduzir, "dinamizar")); db.pivot.find (). forEach (printjson);

Aqui está o exemplo de saída, observe que "Pretty Woman" e "Runaway Bride" têm "Richard Gere" e "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" ] } }

Max Sherbakov
fonte
Observe que a maior parte do conteúdo desta resposta (ou seja, a parte em inglês compreensível) é copiada do livro de receitas do MongoDB no link do GitHub fornecido pelo respondente.
Mark Amery
-4

Podemos mesclar duas coleções usando a subconsulta mongoDB. Aqui está um exemplo, 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"  }])

Subconsulta do MongoDB para JOIN--

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

Obter resultado de Collection-- recém-gerado

db.newCommentUsers.find().pretty()

Resultado--

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

Espero que isso ajude.

Krishna
fonte
7
Por que você basicamente copiou essa resposta quase idêntica de um ano? stackoverflow.com/a/22739813/4186945
hackel 5/05