Qual é a sintaxe para fazer uma $ lookup em um campo que é uma matriz de ObjectIds em vez de apenas um único ObjectId?
Exemplo de documento de pedido:
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
]
}
Consulta que não funciona:
db.orders.aggregate([
{
$lookup:
{
from: "products",
localField: "products",
foreignField: "_id",
as: "productObjects"
}
}
])
Resultado desejado
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
],
productObjects: [
{<Car Object>},
{<Bike Object>}
],
}
mongodb
mongodb-query
aggregation-framework
Jason Lin
fonte
fonte
Respostas:
Atualização de 2017
$ lookup agora pode usar diretamente um array como o campo local .
$unwind
não é mais necessário.Resposta antiga
O
$lookup
estágio do pipeline de agregação não funcionará diretamente com uma matriz. O objetivo principal do projeto é fazer uma "junção à esquerda" como um tipo de junção "um para muitos" (ou realmente uma "pesquisa") nos possíveis dados relacionados. Mas o valor deve ser singular e não uma matriz.Portanto, você deve "desnormalizar" o conteúdo antes de executar a
$lookup
operação para que isso funcione. E isso significa usar$unwind
:Depois de
$lookup
corresponder a cada membro do array, o resultado é um array em si, para que você$unwind
volte$group
a$push
usar novos arrays para o resultado final.Observe que qualquer correspondência de "junção à esquerda" que não seja encontrada criará uma matriz vazia para "productObjects" no produto fornecido e, portanto, negará o documento para o elemento "produto" quando o segundo
$unwind
for chamado.Embora uma aplicação direta a um array fosse bom, é assim que isso funciona atualmente, combinando um valor singular com muitos possíveis.
Como
$lookup
é basicamente muito novo, atualmente funciona como seria familiar para aqueles que estão familiarizados com o mangusto como uma "versão de homem pobre" do.populate()
método oferecido lá. A diferença é que$lookup
oferece processamento "do lado do servidor" da "junção" em oposição ao do cliente e que parte da "maturidade" no$lookup
está faltando no que.populate()
oferece (como interpolar a pesquisa diretamente em um array).Na verdade, esse é um problema atribuído para melhoria SERVER-22881 , então, com um pouco de sorte, chegaria à próxima versão ou logo depois.
Como princípio de design, sua estrutura atual não é boa nem ruim, mas apenas sujeita a sobrecargas ao criar qualquer "junção". Como tal, o princípio básico permanente do MongoDB no início se aplica, onde se você "pode" viver com os dados "pré-reunidos" em uma coleção, então é melhor fazer isso.
Outra coisa que pode ser dita
$lookup
como princípio geral é que a intenção da "junção" aqui é trabalhar ao contrário do mostrado aqui. Portanto, em vez de manter os "ids relacionados" dos outros documentos dentro do documento "pai", o princípio geral que funciona melhor é quando os "documentos relacionados" contêm uma referência ao "pai".Portanto,
$lookup
pode-se dizer que "funciona melhor" com um "design de relação" que é o oposto de como algo como o mangusto.populate()
realiza suas junções do lado do cliente. Ao idendificar o "um" dentro de cada "muitos", você apenas puxa os itens relacionados sem precisar$unwind
primeiro do array.fonte
$lookup
validação de documentos como sendo recursos em sua infância e com probabilidade de melhorar. Portanto, a expansão direta em uma matriz seria bem-vinda, assim como uma "consulta" para filtrar os resultados. Ambos estariam muito mais alinhados com o.populate()
processo do mangusto a que muitos estão acostumados. Adicionando o link do problema diretamente no conteúdo da resposta.$lookup
agora funciona diretamente em um array.A partir do MongoDB v3.4 (lançado em 2016), o
$lookup
estágio de pipeline de agregação também pode funcionar diretamente com um array . Não há necessidade de$unwind
mais nada.Isso foi rastreado no SERVER-22881 .
fonte
Você também pode usar o
pipeline
estágio para realizar verificações em uma matriz de subdocumentosAqui está o exemplo usando
python
(desculpe, sou gente cobra).O problema aqui é combinar todos os objetos no
ObjectId
array
(estrangeiro_id
que está nolocal
campo / propproducts
).Você também pode limpar ou projetar os registros estrangeiros com
stage
s adicionais , conforme indicado pelo comentário acima.fonte
use $ unfind você obterá o primeiro objeto em vez de uma matriz de objetos
inquerir:
resultado:
fonte
Agregar com
$lookup
e subsequente$group
é muito complicado, então se (e esse é um meio se) você estiver usando o node & Mongoose ou uma biblioteca de suporte com algumas dicas no esquema, você pode usar um.populate()
para buscar esses documentos:fonte
Eu tenho que discordar, podemos fazer $ lookup funcionar com a matriz de IDs se começarmos com $ match stage.
Torna-se mais complicado se quisermos passar o resultado da pesquisa para um pipeline. Mas, novamente, há uma maneira de fazer isso (já sugerido por @ user12164):
fonte