I have a collection that has two date fields and I am trying to query all records that have a difference of 15 days:
{
\"_id\" : \"someid\",
\"factoryNumbe
Starting 3.6 you have to use new operator $expr
which allows use of aggregation expressions inside match queries or in regular queries.
You can create the json query and pass it directly as $expr is not supported in spring yet in regular query.
15 days = 15 * 24 * 60 * 60 * 1000 = 1296000000 millis
Query query = new BasicQuery("{'$expr':{'$gte':[{'$subtract':[{'$ifNull':['$acceptedDate',{'$date':" + System.currentTimeMillis() + "}]},'$lastVisit']},1296000000]}}");
List<Document> results = mongoTemplate.find(query, Document.class);
3.4 version
If you like to use spring mongo methods you have to use projection to add new field which holds comparison and followed by match operation and extra projection to drop the comparison field. Unfortunately $addFields is still not supported so you have to use the AggregationOperation
to create a new stage manually.
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 version
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();
You need to use aggregate pipeline to get the documents
$ifNull
- to set current date if accepted date is null$addFields
- to add fields from
, fromDays
and toDays
to existing document to $redact
$redact
- match within fields and filter$project
- to exclude the fields added in $addFields
stagemongo query
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}}
])
sample collection
> 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")
}
result with min 150 days
> 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")
}
>
translate the mongo aggregate query into spring mongodb query