【问题标题】:Spring MongoDB query documents if days difference is x days如果天数差为 x 天,则 Spring MongoDB 查询文档
【发布时间】:2019-05-21 03:02:42
【问题描述】:

我有一个包含两个日期字段的集合,我正在尝试查询相差 15 天的所有记录:

{
   "_id" : "someid",
   "factoryNumber" : 123,
   "factoryName" : "some factory name",
   "visitType" : "audit",
   "personelId" : "somePersonel",
   "lastVisit": ISODate("2018-10-30T00:00:00.000+0000"),
   "acceptedDate" : ISODate("2018-11-16T00:00:00.000+0000")
}

现在在某些情况下acceptedDate 将不存在,因此我需要根据当前日期对其进行评估。不完全确定如何在 spring 中编写此类查询以获得期望的结果。

    Criteria.where("acceptedDate"). 
(is 15 days past last visit or current date if last visit not present)

【问题讨论】:

    标签: java mongodb spring-boot criteria spring-data-mongodb


    【解决方案1】:

    从 3.6 开始,您必须使用新运算符 $expr,它允许在匹配查询或常规查询中使用聚合表达式。

    您可以创建 json 查询并直接传递它,因为 Spring 不支持 $expr,但在常规查询中。

    15 天 = 15 * 24 * 60 * 60 * 1000 = 1296000000 毫秒

    Query query = new BasicQuery("{'$expr':{'$gte':[{'$subtract':[{'$ifNull':['$acceptedDate',{'$date':" + System.currentTimeMillis() + "}]},'$lastVisit']},1296000000]}}");
    List<Document> results = mongoTemplate.find(query, Document.class);
    

    3.4版本

    如果你喜欢使用 spring mongo 方法,你必须使用投影来添加保存比较的新字段,然后是匹配操作和额外的投影来删除比较字段。不幸的是,$addFields 仍然不受支持,因此您必须使用 AggregationOperation 手动创建新阶段。

    AggregationOperation addFields = new AggregationOperation() {
        @Override
        public Document toDocument(AggregationOperationContext aggregationOperationContext) {
            Document document = new Document("comp", Document.parse("{'$gte':[{'$subtract':[{'$ifNull':['$acceptedDate', {'$date':" + System.currentTimeMillis() + "}]},'$lastVisit']},1296000000]}}"));      
            return new Document("$addFields", document);
        }
    };
    
    Aggregation aggregation = Aggregation.newAggregation(
            addFields,
            Aggregation.match(Criteria.where("comp").is(true))
            Aggregation.project().andExclude("comp");
    );
    
    List<Document> results = mongoTemplate.aggregate(aggregation, collection name, Document.class).getMappedResults();
    

    3.2版本

    AggregationOperation redact = new AggregationOperation() {
        @Override
        public DBObject toDBObject(AggregationOperationContext aggregationOperationContext) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("if",  BasicDBObject.parse("{'$gte':[{'$subtract':[{'$ifNull':['$acceptedDate', {'$date':" + System.currentTimeMillis() + "}]},'$lastVisit']},1296000000]}}"));
        map.put("then", "$$KEEP");
        map.put("else", "$$PRUNE");
        return new BasicDBObject("$redact", new BasicDBObject("$cond", map));
    };
    
    Aggregation aggregation = Aggregation.newAggregation(redact);
    
    List<FactoryAcceptance> results = mongoTemplate.aggregate(aggregation, FactoryAcceptance.class, FactoryAcceptance.class).getMappedResults();
    

    【讨论】:

    • 目前全公司使用的版本是 3.2。我想我不能使用 expr
    • 当我覆盖 AggregationOperation 时,它会强制我使用 DBObject?与我的收藏实体名称相反?
    • 更新了 3.2 版本和 spring mongo 1.x jar 的答案。
    • 感谢您的快速提问,如果我的实体名为 FactoryAcceptance,那么我需要将 List 映射到我的 List 吗?它会包含记录中的所有字段吗?
    • 由于某种原因,我在第一个 map.put - BasicDBObject.parse 上遇到了 json 解析错误,不过对我来说看起来不错
    【解决方案2】:

    您需要使用聚合管道来获取文档

    • $ifNull - 如果接受日期为空,则设置当前日期
    • $addFields - 将字段fromfromDaystoDays 添加到现有文档到
    • 过滤$redact
    • $redact - 匹配字段并过滤
    • $project - 排除在$addFields 阶段添加的字段

    mongo 查询

    db.t1.aggregate([
        {$addFields : {
            from : {$ifNull : ["$acceptedDate", new Date()]}
        }},
        {$addFields: {
            fromDays : {$sum : [{$multiply : [365, {$year : "$from"}]}, {$dayOfYear : "$from"}]},
            toDays : {$sum : [{$multiply : [365, {$year : "$lastVisit"}]}, {$dayOfYear : "$lastVisit"}]}
        }},
        { $redact: {
            $cond: {
               if: {$lte : [{$subtract : ["$fromDays", "$toDays"]}, 15]},
               then: "$$DESCEND",
               else: "$$PRUNE"
             }
           }
        },
        {$project : {from:0, fromDays:0, toDays:0}}
    ])
    

    样本收集

    > db.t1.find().pretty()
    {
            "_id" : "someid",
            "factoryNumber" : 123,
            "factoryName" : "some factory name",
            "visitType" : "audit",
            "personelId" : "somePersonel",
            "lastVisit" : ISODate("2018-10-30T00:00:00Z"),
            "acceptedDate" : ISODate("2018-11-16T00:00:00Z")
    }
    {
            "_id" : "someotherid",
            "factoryNumber" : 123,
            "factoryName" : "some factory name",
            "visitType" : "audit",
            "personelId" : "somePersonel",
            "lastVisit" : ISODate("2018-10-30T00:00:00Z")
    }
    

    最少 150 天的结果

    > db.t1.aggregate([ {$addFields : { from : {$ifNull : ["$acceptedDate", new Date()]} }}, {$addFields: { fromDays : {$sum : [{$multiply : [365, {$year : "$from"}]}, {$dayOfYear : "$from"}]}, toDays : {$sum : [{$multiply : [365, {$year : "$lastVisit"}]}, {$dayOfYear : "$lastVisit"}]} }}, { $redact: {         $cond: {            if: {$lte : [{$subtract : ["$fromDays", "$toDays"]}, 150]},            then: "$$DESCEND",            else: "$$PRUNE"          }        } }, {$project : {from:0, fromDays:0, toDays:0}} ]).pretty()
    {
            "_id" : "someid",
            "factoryNumber" : 123,
            "factoryName" : "some factory name",
            "visitType" : "audit",
            "personelId" : "somePersonel",
            "lastVisit" : ISODate("2018-10-30T00:00:00Z"),
            "acceptedDate" : ISODate("2018-11-16T00:00:00Z")
    }
    {
            "_id" : "someotherid",
            "factoryNumber" : 123,
            "factoryName" : "some factory name",
            "visitType" : "audit",
            "personelId" : "somePersonel",
            "lastVisit" : ISODate("2018-10-30T00:00:00Z")
    }
    >
    

    将mongo聚合查询翻译成spring mongodb查询

    【讨论】:

    • 很好,春天部分是我最关心的,因为它会产生这个。 +1 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多