【问题标题】:MongoDB [NodeJS] find only documents with array elements in rangeMongoDB [NodeJS] 仅查找具有范围内数组元素的文档
【发布时间】:2016-01-20 11:42:56
【问题描述】:

我有历史文件。我想查找具有特殊历史条目的文档。 历史元素:

{
    name:"added",
    date: Isodate
}

查询应获取名称被“添加”且日期低于参考的所有文档。如果日期更高,则应跳过该文档。一个文档可以有多个“添加”事件

这是我的查询,查找所有添加历史元素在 7 天范围内的文档,如果一个元素小于 7 天,则不应显示该文档:

 var expiryDate = new Date();
 expiryDate.setDate(expiryDate.getDate() - 7);

 var query = {
        $and: [
            {
                history: {
                    $elemMatch:{
                        event: "added",
                        date: {
                            $not: {
                                $gte: expiryDate.toISOString()
                            },
                        }
                    }
                }
            },
            {
                history: {
                    $elemMatch: {
                        event: "added",
                        date: {
                            $lt: expiryDate.toISOString()
                        }
                    }
                }
            }
        ]
    }

来电:

self.db.items.find(query).toArray(function (err, items) { 
    if(items)
       console.log(items.length);
});

问题是,只有 $lte 部分有效。如果有一个大于参考的日期,它将被输出。

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    考虑一个替代方案,例如 aggregation framework。运行以下管道将为您提供所需的结果,history 数组根据给定的日期条件进行过滤。

    注意:没有必要将 JavaScript Date 对象转换为 ISOString,因为 Mongo 使用 ISODate 帮助程序包装 Date 类型的对象,但在内部是原生 JS Date 对象存储为一个 64 位整数,表示自 Unix 纪元(1970 年 1 月 1 日)以来的毫秒数,这导致过去和未来大约 2.9 亿年的可表示日期范围。

    var expiryDate = new Date();
    expiryDate.setDate(expiryDate.getDate() - 7);
    
    var pipeline = [
        {
            "$match": {
                "history.event": "added",
                "history.date": { "$lt": expiryDate }
            }
        },
        {
            "$project": {
                "other_fields": 1, 
                "history": {
                    "$setDifference": [
                        {
                            "$map": {
                                "input": "$history",
                                "as": "el",
                                "in": {
                                    "$cond": [
                                        {  
                                            "$and": [
                                                { "$eq": [ "$$el.event", "added" ]}
                                                { "$lt": [ "$$el.date", expiryDate ]}
                                            ]
                                        },
                                        "$$el",
                                        false
                                    ]
                                }
                            }
                        },
                        [false]
                    ]
                }
            }
        }
    ];
    
    db.collection.aggregate(pipeline)
    

    上述管道涉及过滤history 数组,以便您删除不满足上述给定条件的文档。这可以通过 $setDifference$map 运算符实现。

    $map 运算符本质上创建了一个新的数组字段,该字段保存值作为数组每个元素的子表达式中的评估逻辑的结果。 $setDifference 运算符然后返回一个集合,其中包含出现在第一个集合中但不在第二个集合中的元素;即相对于第一组执行第二组的相对补充。在这种情况下,它将通过dateevent 属性返回包含与父文档无关的元素的最终history 数组。

    【讨论】:

    • Tyvm 为此,我现在正在测试它。我会尽快回复 :)
    • 它有效。我在数据库中有 ISO 字符串作为日期。没有 .toISOString() 不幸的是,我不会得到任何结果。
    • 这部分是您的初始查询不起作用的原因。与日期/时间戳进行比较比字符串值更有效,建议将所有日期存储为 UTC 中的 Date 对象,或者可能与本地数据中心相关的一致时区,然后将日期值转换为正确的本地客户端上的时区。由于您有 ISO 字符串作为日期,因此过滤和处理此值是客户端的责任,因为 mongo 将其视为字符串。您可以使用此answer. 转换为日期
    猜你喜欢
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    相关资源
    最近更新 更多