是否可以使用2个或更多类似于SQL查询的集合在Mongo DB中编写联合查询?
我正在使用spring mongo模板和用例是我需要根据某些条件从3-4个集合中获取数据。我们能在一次操作中实现这一目标吗?
例如,我有一个名为“circuitId”的字段,它出现在所有4个集合中。现在,在搜索操作中,我需要从该字段与给定值匹配的所有4个集合中获取所有记录。
答案 0 :(得分:2)
从Mongo 4.4
开始,聚合框架提供了一个新的$unionWith
阶段,执行两个集合的联合(将两个集合的合并管道结果合并为一个结果集)。
因此,为了合并3个集合中的文档:
// > db.collection1.find()
// { "circuitId" : 12, "a" : "1" }
// { "circuitId" : 17, "a" : "2" }
// { "circuitId" : 12, "a" : "5" }
// > db.collection2.find()
// { "circuitId" : 12, "b" : "x" }
// { "circuitId" : 12, "b" : "y" }
// > db.collection3.find()
// { "circuitId" : 12, "c" : "i" }
// { "circuitId" : 32, "c" : "j" }
db.collection1.aggregate([
{ $match: { circuitId: 12 } },
{ $unionWith: { coll: "collection2", pipeline: [{ $match: { circuitId: 12 } }] } },
{ $unionWith: { coll: "collection3", pipeline: [{ $match: { circuitId: 12 } }] } }
])
// { "circuitId" : 12, "a" : "1" }
// { "circuitId" : 12, "a" : "5" }
// { "circuitId" : 12, "b" : "x" }
// { "circuitId" : 12, "b" : "y" }
// { "circuitId" : 12, "c" : "i" }
此:
collection1
过滤文档collection2
中的文档从$unionWith
阶段引入到管道中。 pipeline
参数是一个可选的聚合管道,应用于合并发生之前要合并的集合中的文档。collection3
到$unionWith
相同阶段的文档从管道中引入。答案 1 :(得分:1)
不幸的是,基于文档的MongoDB不像关系数据库引擎那样支持JOINS / Unions。 MongoDB的一个关键设计原则是根据应用程序的数据获取模式阻止使用嵌入式文档的连接。 话虽如此,如果您真的需要使用4个集合,或者您可以根据MongoDB最佳实践重新设计数据库设计,则需要在应用程序端管理逻辑。
了解更多信息:https://docs.mongodb.com/master/core/data-model-design/
答案 2 :(得分:1)
可以在单个查询中使用聚合和查找以“ SQL UNION”方式在MongoDB中进行联合。
类似这样的东西:
db.getCollection("AnyCollectionThatContainsAtLeastOneDocument").aggregate(
[
{ $limit: 1 }, // Reduce the result set to a single document.
{ $project: { _id: 1 } }, // Strip all fields except the Id.
{ $project: { _id: 0 } }, // Strip the id. The document is now empty.
// Lookup all collections to union together.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } },
// Merge the collections together.
{
$project:
{
Union: { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
},
{ $unwind: "$Union" }, // Unwind the union collection into a result set.
{ $replaceRoot: { newRoot: "$Union" } } // Replace the root to cleanup the resulting documents.
]);
以下是其工作原理的说明:
从其中至少有一个文档的数据库的任何集合中实例化一个aggregate
。如果不能保证数据库的任何集合都不为空,则可以通过在数据库中创建某种“虚拟”集合来解决此问题,该“虚拟”集合中仅包含一个空文档,专门用于进行联合查询。
使管道的第一阶段为{ $limit: 1 }
。这将删除集合中除第一个文档外的所有文档。
使用$project
阶段对剩余文档的所有字段进行剥离:
{ $project: { _id: 1 } },
{ $project: { _id: 0 } }
您的汇总现在包含一个空文档。现在该为要合并在一起的每个集合添加查找。您可以使用pipeline
字段进行一些特定的过滤,也可以将localField
和foreignField
保留为空以匹配整个集合。
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
您现在有了一个包含单个文档的聚合,该文档包含3个数组,如下所示:
{
Collection1: [...],
Collection2: [...],
Collection3: [...]
}
然后,您可以使用$project
阶段和$concatArrays
聚合运算符将它们合并为一个数组:
{
"$project" :
{
"Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
}
您现在有了一个包含单个文档的聚合,其中包含一个包含集合联合的数组。剩下要做的是添加一个$unwind
和一个$replaceRoot
阶段,以将数组拆分成单独的文档:
{ $unwind: "$Union" },
{ $replaceRoot: { newRoot: "$Union" } }
Voilà。您知道有一个结果集,其中包含要合并在一起的集合。然后,您可以添加更多阶段以对其进行进一步过滤,排序,应用skip()和limit()。您想要的几乎任何东西。