mongodb - 大集合中的 MongoDB 查询对
问题描述
我最近才开始使用 MongoDB 尝试解决特定于域的问题,但在尝试自我加入大集合时遇到了困难。我有一个包含超过 1000 万个文档的数据库,每个文档都包含一个实体的地址元素(个人、组织、组织的邮箱等)。请注意,每个深度(例如街道)可以多次出现以存储一些不同的信息,例如别名或特定于深度的 id。我没有架构限制,如果有助于解决问题,我可以更改它。
数据如下所示:
{
"some_info": "xyz",
"tags": {
"HOUSE_NUMBER": [
{
"id": "23.45678",
"value": "18",
"attributes": ["NU"]
}
],
"FORENAME": [
{
"id": "34.56789",
"value": "MAX",
"attributes": ["XQ4", "M"]
},
{
"id": "45.67890",
"value": "X65732862",
"attributes": ["XID"]
}
],
"STREET": [
{
"id": "56.789012",
"value": "RICHMOND STREET",
"attributes": []
}
],
"...": []
}
}
我想查询集合中的对,例如“查找居住在同一条街道上的所有具有相同名字的人对”,或“查找所有居住在同一城市中至少有 3 个单词的人和组织对”。我当前对第一个问题的查询如下所示:
db.collection_name.aggregate([
{$unwind: "$tags.STREET"},
{$unwind: "$tags.FORENAME"},
{
$match: {
"tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
}
},
{
$lookup: {
from: "collection_name",
localField: "tags.STREET.id",
foreignField: "tags.STREET.id",
as: "joined"
}
},
{$unwind: "$joined"},
{$unwind: "$joined.tags.FORENAME"},
{
$match: {$expr: {$ne: ["$tags.FORENAME.id", "$joined.tags.FORENAME.id"]}}
},
{
$match: {$expr: {$eq: ["$tags.FORENAME.value", "$joined.tags.FORENAME.value"]}}
}
], {
allowDiskUse: true
})
我在 tags.STREET.id、tags.FORENAME.id、tags.FORENAME.attributes 和 tags.FORENAME.value 上创建了索引。
问题在于执行时间。我只是无法达到可接受的水平,上述查询需要 3.5 分钟才能在我的机器上获得 500 个结果。相比之下,我的 PostgreSQL 数据库(针对问题专门创建的视图和索引)只需要几秒钟。
我怎样才能加快这种查询?MongoDB甚至适合这种问题吗?
解决方案
$lookup是 Mongo 执行的一个非常昂贵的阶段,在这种特定情况下,完全不需要。更不用说你在整个系列上都这样做了。
我会像这样重写这个管道,使用$group而不是$lookup
:
db.collection_name.aggregate([
{
$unwind: "$tags.STREET"
},
{
$unwind: "$tags.FORENAME"
},
{
$match: {
"tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
}
},
{
$group: {
"_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id"},
docs: {$addToSet: "$$ROOT"}
}
},
{
$match: {
"docs.1": {$exists: true}
}
},
//Add whichever other structure changes you need.
],
{
allowDiskUse: true
});
现在这仍然是一个有点昂贵的管道,因为我们必须$unwind
和$group
整个集合,但它会比当前版本快得多。
我不知道您的数据/产品是如何工作的,因此很难为应该如何构建模式/集合架构提供更好的“解决方案”。
话虽如此,我立即看到一个简单的改进是将street
andforename
结构从数组更改为对象。(除非您可以拥有多个街道和姓名,然后关系数据库是否适合您?)这将使当前管道的前 3 个阶段变得多余,并提高性能。
编辑:分组时不可能否定条件,但我们可以通过添加一个额外的$group
阶段来解决它。
db.collection_name.aggregate([
{
$unwind: "$tags.STREET"
},
{
$unwind: "$tags.FORENAME"
},
{
$unwind: "$tags.HOUSE_NUMBER"
},
{
$match: {
"tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
}
},
{
$group:{
"_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id", houseName: "tags.HOUSE_NUMBER.id"},
docs: {$addToSet: "$$ROOT"}
}
},
{
$group: {
"_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id"},
docs: {$addToSet: "$docs"}
}
},
{
$match: {
"docs.1": {$exists: true}
}
},
//Add whichever other structure changes you need.
],
{
allowDiskUse: true
});
推荐阅读
- java - 是否有可能在 Spring Batch 中引发致命的不可回滚异常?
- pandas - 中位数()如何处理偶数个条目?
- python - RuntimeError 过滤权重低于阈值的边缘 - Networkx
- java - 无法在 Java 中从 int 转换为 Integer
- spyder - 通过 VPN 启动 Spyder IDE 时出现错误“LoadLibrary failed with error 126: The module could not be found”
- ios - 无法分配“字符串”类型的值?键入“UILabel?”
- python - MariaDB 游标无法正确使用 Python
- google-cloud-platform - 如果我正在设计一个应该搜索标签/列的应用程序,我将如何在 Bigtable 中设计一个行键?
- google-kubernetes-engine - 从另一个 GKE 私有集群访问私有 GKE 集群中的节点端口服务
- python - Python(熊猫):如何根据列中的值将每一行除以“绝对”行