MongoDB以Unix时间戳为键的历史数据如何查询指定日期间记录
商品历史时间区间数据查询方案
现有存储结构下的临时查询方案
你当前的存储逻辑是将Unix时间戳作为字段名存储,MongoDB没有直接支持按字段名范围查询的原生语法,可通过聚合操作实现需求:
- 先将指定的日期A、日期B转换为和你存储精度一致的Unix时间戳(示例中是毫秒级)
- 执行如下聚合查询语句:
// 替换为你自己的起止时间戳 const startTs = 1634553370000; const endTs = 1634558360000; db.你的集合名称.aggregate([ // 可选:指定要查询的商品ID,减少扫描范围 { $match: { collectibleId: "bc50774d-b923-4a5c-8e5d-f25608e1e80e" } }, // 将文档所有键值对转换为数组格式 { $objectToArray: "$$ROOT" }, // 过滤出时间范围内的字段,排除固定元字段 { $project: { collectibleId: 1, targetHistory: { $filter: { input: "$v", as: "field", cond: { $and: [ { $gte: [{ $toLong: "$$field.k" }, startTs] }, { $lte: [{ $toLong: "$$field.k" }, endTs] }, { $nin: ["$$field.k", ["_id", "collectibleId"]] } ] } } } } }, // 可选:将过滤后的结果转回对象格式 { $replaceRoot: { newRoot: { $arrayToObject: { $concatArrays: [ [{ k: "collectibleId", v: "$collectibleId" }], "$targetHistory" ] } } } } ])
注意:该方案需要遍历单文档内的所有字段做格式转换,若单文档内时间戳字段过万,查询性能会明显下降,仅适合临时查询使用。
长期存储优化方案
将时间戳作为字段名属于MongoDB的设计反模式,会大幅提升查询、统计的复杂度,建议调整为嵌套数组的存储结构,优化后结构示例如下:
{ "_id" : ObjectId("616d4e1c1e7edf6e9bcdd668"), "collectibleId" : "bc50774d-b923-4a5c-8e5d-f25608e1e80e", "history" : [ { "timestamp": 1634553373797, "storePrice" : "39.99" }, { "timestamp": 1634554743600, "storePrice" : "39.99" }, { "timestamp": 1634558354739, "storePrice" : "39.99" } ] }
调整结构后,时间区间查询的复杂度和性能都会大幅优化,还可以给history.timestamp加索引进一步提效,对应查询语句如下:
const startTs = 1634553370000; const endTs = 1634558360000; db.你的集合名称.aggregate([ { $match: { collectibleId: "bc50774d-b923-4a5c-8e5d-f25608e1e80e" } }, { $project: { collectibleId: 1, history: { $filter: { input: "$history", as: "record", cond: { $and: [ { $gte: ["$$record.timestamp", startTs] }, { $lte: ["$$record.timestamp", endTs] } ] } } } } } ])
内容的提问来源于stack exchange,提问作者alienbuild
相关产品推荐
相关产品推荐

