Spring MongoDB 天数相差x天查询文档

Spring MongoDB query documents if days difference is x days

我有一个包含两个日期字段的集合,我正在尝试查询相差 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)

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

  • $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 查询

从 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();