在 MongoDB 数组中查找索引
Posted
技术标签:
【中文标题】在 MongoDB 数组中查找索引【英文标题】:Looking up index in a MongoDB array 【发布时间】:2021-06-02 19:50:58 【问题描述】:我们的数据提供者以一种奇怪的格式提供数据。数组date
和value
是对应的并且保证具有相同的长度。无论出于何种原因,他们甚至决定在 date
中混合 int 和 string 值。
[
"_id": "A000005933",
"date": [905270400000, 918748800000, 937843200000, 965923200000, 983289600000, 984931200000, 1152806400000, "1171987200000", "1225382400000", "1229616000000", "1286208000000", "1455552000000"],
"value": ["0.25", "0.15", "0", "0.25", "0.15", "0", "0.25", "0.5", "0.3", "0.1", "0.1", "-0.1"],
"version": 1614837436798
,
"_id": "A000005934",
"date": [915120000000, 923587200000, 941731200000, 949593600000, 953222400000, 956851200000, 962121600000, 967737600000, 970761600000, 989510400000, 999187200000, 1000742400000, 1005235200000, 1039104000000, 1046966400000, 1054828800000, 1133798400000, 1141747200000, 1150300800000, 1155052800000, 1160496000000, 1165939200000, 1173801600000, 1181664000000, 1215532800000, 1224000000000, 1226419200000, 1228838400000, 1232467200000, 1236700800000, 1239120000000, 1242144000000, 1302624000000, 1310486400000, 1320768000000, 1323792000000, 1341936000000, 1367942400000, 1384272000000, 1402416000000, 1410278400000, 1458057600000],
"value": ["3", "2.5", "3", "3.25", "3.5", "3.78", "4.25", "4.5", "4.78", "4.5", "4.25", "3.75", "3.25", "2.78", "2.5", "2", "2.25", "2.5", "2.75", "3", "3.25", "3.5", "3.75", "4", "4.25", "3.75", "3.25", "2.5", "2", "1.5", "1.25", "1", "1.25", "1.5", "1.25", "1", "0.75", "0.5", "0.25", "0.15", "0.05", "0"],
"version": 1614837436548
,
......
]
我们的典型用例是根据_id
和date
查找value
,所以我不得不这样做。
def get_value_from_mongo(id_: str, date: datetime.date) -> float:
result = db.indicators.find_one("_id": _id, "value": 1, "date": 1)
date_list = list(map(str, result["date"]))
price_list = list(map(str, result["value"]))
dt = date.strftime("%s000")
price = float(price_list[date_list.index(dt)])
return price
这是非常低效的,因为每次我想检索单个值时都会扫描整个数组。也许我可以进行二分搜索,但不能保证 date
会被排序,我不想依赖这种行为。
我可以使用任何 MongoDB 运算符来加快查询速度吗?
【问题讨论】:
您是否考虑过以不同的方式提取数据,以便以更有效的方式对其进行索引和查询? @Joe 有点像,但是做 ETL/ELT 听起来需要做很多工作,我不确定我能不能做对。我的意思是,有很多数据,我需要定期合并和监控。对我来说听起来很复杂:/ 俗话说,你买东西在前端或后端,如果你选择在这里支付后端,我想就是这样。您可以考虑将此查询转换为聚合框架作业,以便您的 mongo 集群可以执行它,而不是您的业务应用程序。 我不明白应该返回什么。与 (date, id) 匹配的价格?或者你是在假设数组永远不会包含欺骗? @grodzidate
数组永远不会包含重复项,date
中的每个元素对应于value
中相同索引处的元素。更具体地说,在上面的示例中,id A000005933
在日期 923587200000
的价格(即价值)是 0.15
。
【参考方案1】:
-
第一种可能性是专注于查找:在日期数组上创建索引
这是为了降低写入速度。
在下面的执行计划中,您可以看到使用了索引(如果它带来了那的改进,您应该进行基准测试)
> db.indicators.explain().find(dates: '1.1')
"queryPlanner" :
"plannerVersion" : 1,
"namespace" : "dummy.indicators",
"indexFilterSet" : false,
"parsedQuery" :
"dates" :
"$eq" : "1.1"
,
"queryHash" : "4204704C",
"planCacheKey" : "1DBFE945",
"winningPlan" :
"stage" : "FETCH",
"inputStage" :
"stage" : "IXSCAN",// <------
"keyPattern" :
"dates" : 1
,
"indexName" : "dates_1",
"isMultiKey" : true,
"multiKeyPaths" :
"dates" : [
"dates"
]
,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" :
"dates" : [
"[\"1.1\", \"1.1\"]"
-
第二种可能性是专注于检索尽可能少的数据
提示瓶颈不是日期查找而是数据传输
因此这不会改进查找(假设您在数据库端而不是应用程序代码端“迭代”数组)。
你可以使用
positional operator 使用 mongo >= 4.4 将投影作为 find 中的第二个参数db.indicators.remove()
db.indicators.insert([_id: '0', dates: [1, '1.1', 2], prices: [1,2,3]])
fetch = date =>
print(date)
res = db.indicators.find(
dates:
$elemMatch:
$in: [Number(date), String(date)]
,
'prices.$': 1 // <<--------
).toArray()
printjson(res)
fetch(2) // [ "_id" : "0", "prices" : [ 3 ] ]
fetch('1.1') // [ "_id" : "0", "prices" : [ 2 ] ]
显然你可以组合 1 和 2,但我会尝试只使用 2 以避免创建索引
【讨论】:
以上是关于在 MongoDB 数组中查找索引的主要内容,如果未能解决你的问题,请参考以下文章