Datediff in Criteria operator in spring-data-mongodb not working - spring

Can I have difference between two dates to greater than 0 in Criteria operator in spring-data-mongodb? I wrote the query below :
Criteria c= Criteria.where("myDate").gte(startDate).
andOperator(Criteria.where("myDate").lte(endDate).andOperator(Criteria.where("studentId").is(studentId).andOperator(Criteria.where("currDate - myDate").gt(0))));
This query not working.
If possible please help me in getting this query work with spring-data-mongodb.
Edit:
The mongodb pipeline query is as follows:
{ "aggregate" : "__collection__" , "pipeline" : [ { "$match" : { "myDate" : { "$gte" : { "$date" : "2000-01-01T07:57:33.231Z"}} , "$and" : [ { "myDate" : { "$lte" : { "$date" : "2015-11-05T07:57:33.231Z"}} , "$and" : [ { "studentId" : "100" , "$and" : [ { "currDate - myDate" : { "$gt" : 0}}]}]}]}} , { "$project" : { "status" : 1}} , { "$group" : { "_id" : { "status" : "$status"} , "activeCount" : { "$sum" : 1}}}]}
Regards
Kris

For it to work, you'd essentially want to convert the current aggregation pipeline to this:
var pipeline = [
{
"$project" : {
"status" : 1,
"studentId" : 1,
"myDate" : 1,
"dateDifference": { "$subtract": [ new Date(), "$myDate" ] }
}
},
{
"$match" : {
"studentId": "100" ,
"myDate": {
"$gte": ISODate("2000-01-01T07:57:33.231Z"),
"$lte": ISODate("2015-11-05T07:57:33.231Z")
},
"dateDifference": { "$gt" : 0 }
}
},
{
"$group": {
"_id": "$status",
"activeCount": { "$sum" : 1 }
}
}
];
db.collection.aggregate(pipeline);
The Spring Data MongoDB equivalent follows:
Criteria dateCriteria = new Criteria().andOperator(Criteria.where("myDate").gte(startDate).lte(endDate),
Criteria.where("dateDifference").gt(0));
Aggregation agg = Aggregation.newAggregation(
project("id", "status", "studentId", "myDate")
.andExpression("currDate - myDate").as("dateDifference"),
//.and(currDate).minus("myDate").as("dateDifference"), <-- or use expressions
match(Criteria.where("studentId").is("100").andOperator(dateCriteria)),
group("status"),
.count().as("activeCount")
);

Related

Filter in projection aggregation in SpringMongoDb

I have this aggregation that I want to transform into a Spring MongoDb aggregation.
A document has several named data in a list. But I want to project only 2 datas for a couple of documents with a given ID as follow.
How to put a filter in the spring aggregation ?
db.getCollection("data").aggregate(
[
{
"$match" : {
"_id" : {
"$in" : [
1,
2
]
}
}
},
{
"$project" : {
"datas" : {
"$filter" : {
"input" : "$datas",
"as" : "item",
"cond" : {
"$or" : [
{
"$eq" : [
"$$item.name",
"data1"
]
},
{
"$eq" : [
"$$item.name",
"data2"
]
}
]
}
}
}
}
}
]
);

Get document by min size of array in Mongodb

I have mongo collection:
{
"_id" : 123,
"index" : "111",
"students" : [
{
"firstname" : "Mark",
"lastname" : "Smith"),
}
],
}
{
"_id" : 456,
"index" : "222",
"students" : [
{
"firstname" : "Mark",
"lastname" : "Smith"),
}
],
}
{
"_id" : 789,
"index" : "333",
"students" : [
{
"firstname" : "Neil",
"lastname" : "Smith"),
},
{
"firstname" : "Sofia",
"lastname" : "Smith"),
}
],
}
I want to get document that has index that is in the set of the given indexes, for example givenSet = ["111","333"] and has min length of students array.
Result should be the first document with _id:123, because its index is in the givenSet and studentsArrayLength = 1, which is smaller than third.
I need to write custom JSON #Query for Spring Mongo Repository. I am new to Mongo and am stuck a bit with this problem.
I wrote something like this:
#Query("{'index':{$in : ?0}, length:{$size:$students}, $sort:{length:-1}, $limit:1}")
Department getByMinStudentsSize(Set<String> indexes);
And got error: error message '$size needs a number'
Should I just use .count() or something like that?
you should use the aggregation framework for this type of query.
filter the result based on your condition.
add a new field and assign the array size to it.
sort based on the new field.
limit the result.
the solution should look something like this:
db.collection.aggregate([
{
"$match": {
index: {
"$in": [
"111",
"333"
]
}
}
},
{
"$addFields": {
"students_size": {
"$size": "$students"
}
}
},
{
"$sort": {
students_size: 1
}
},
{
"$limit": 1
}
])
working example: https://mongoplayground.net/p/ih4KqGg25i6
You are getting the issue because the second param should be enclosed in curly braces. And second param is projection
#Query("{{'index':{$in : ?0}}, {length:{$size:'$students'}}, $sort:{length:1}, $limit:1}")
Department getByMinStudentsSize(Set<String> indexes);
Below is the mongodb query :
db.collection.aggregate(
[
{
"$match" : {
"index" : {
"$in" : [
"111",
"333"
]
}
}
},
{
"$project" : {
"studentsSize" : {
"$size" : "$students"
},
"students" : 1.0
}
},
{
"$sort" : {
"studentsSize" : 1.0
}
},
{
"$limit" : 1.0
}
],
{
"allowDiskUse" : false
}
);

spring data mongodb: sum aggregation by domain name

I have collection which is contain id, domain . In the collection the same domain saved multiple times. I want to aggregate and get result like
google.com 4
times.com 5
My code
public List<DomainDTO> domainAggregation() {
Aggregation pipeline = newAggregation(
group(fields("id","domain")),
group("domain").count().as("count"),
sort(Sort.Direction.DESC, previousOperation(), "domain")
);
AggregationResults groupResults = mongoTemplate.aggregate(
pipeline, Domains.class, DomainDTO.class);
List<DomainDTO> domainReport = groupResults.getMappedResults();
return domainReport;
}
DomainDTO consist
private String domain;
private Integer count;
Domain entity consist
private String id;
private String searchId;
private String domain;
private Date searchDate;
private String searchName;
private Integer count;
Result json is
{
"domain": null,
"count": 2
},
{
"domain": null,
"count": 1
},
{
"domain": null,
"count": 2
},
{
"domain": null,
"count": 48
},
Domain name is not passed, and not sorted. Could not find bug. Any suggestions?
Your current query outputs something like
{ "$group" : {
"_id" : { "id" : "$id" , "domain" : "$domain"}
} } ,
{ "$group" : { "_id" : "$_id.domain" , "count" : { "$sum" : 1}}} ,
{ "$sort" : { "_id" : -1 , "_id.domain" : -1}}
I believe you're intending something like
{ "$group" : { "_id" : "$domain" , "count" : { "$sum" : 1}}} ,
{ "$sort" : { "_id" : -1}}
Aggregation Java Code:
Aggregation pipeline = newAggregation(
group("domain").count().as("count"),
sort(Sort.Direction.DESC, previousOperation())
);
You will need a $project stage to map _id back to domain in your DomainDTO class.
Aggregation pipeline = newAggregation(
group("domain").count().as("count"),
sort(Sort.Direction.DESC, previousOperation()),
project(bind("domain", "_id")).andExclude("_id").andInclude("count")
);
Mongo Shell
{ "$group" : { "_id" : "$domain" , "count" : { "$sum" : 1}}} ,
{ "$sort" : { "_id" : -1}},
{ "$project" : { "domain" : "$_id" , "_id" : 0 , "count" : 1}

Count Documents Matching Multiple Array Criteria

Schema is:
{
"_id" : ObjectId("594b7e86f59ccd05bb8a90b5"),
"_class" : "com.notification.model.entity.Notification",
"notificationReferenceId" : "7917a5365ba246d1bb3664092c59032a",
"notificationReceivedAt" : ISODate("2017-06-22T08:23:34.382+0000"),
"sendTo" : [
{
"userReferenceId" : "check",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "UNREAD"
}
]
}
]
}
{
"_id" : ObjectId("594b8045f59ccd076dd86063"),
"_class" : "com.notification.model.entity.Notification",
"notificationReferenceId" : "6990329330294cbc950ef2b38f6d1a4f",
"notificationReceivedAt" : ISODate("2017-06-22T08:31:01.299+0000"),
"sendTo" : [
{
"userReferenceId" : "check",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "UNREAD"
}
]
}
]
}
{
"_id" : ObjectId("594b813ef59ccd076dd86064"),
"_class" : "com.notification.model.entity.Notification",
"notificationReferenceId" : "3c910cf5fcec42d6bfb78a9baa393efa",
"notificationReceivedAt" : ISODate("2017-06-22T08:35:10.474+0000"),
"sendTo" : [
{
"userReferenceId" : "check",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "UNREAD"
}
]
},
{
"userReferenceId" : "hello",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "READ"
}
]
}
]
}
I want to count a user notifications based on statusList which is a List. I used mongoOperations to make a query:
Query query = new Query();
query.addCriteria(Criteria.where("sendTo.userReferenceId").is(userReferenceId)
.andOperator(Criteria.where("sendTo.mediumAndDestination.status").in(statusList)));
long count = mongoOperations.count(query, Notification.class);
I realise I'm doing it wrong because I am getting count as 1 when I query for user with reference ID hello and statusList with single element as UNREAD.
How do I perform an aggregated query on array element?
The query needs $elemMatch in order to actually match "within" the array element that matches both criteria:
Query query = new Query(Criteria.where("sendTo")
.elemMatch(
Criteria.where("userReferenceId").is("hello")
.and("mediumAndDestination.status").is("UNREAD")
));
Which essentially serializes to:
{
"sendTo": {
"$elemMatch": {
"userReferenceId": "hello",
"mediumAndDestination.status": "UNREAD"
}
}
}
Note that in your question there is no such document, the only matching thing with "hello" actually has the "status" of "READ". If I supply those criteria instead:
{
"sendTo": {
"$elemMatch": {
"userReferenceId": "hello",
"mediumAndDestination.status": "READ"
}
}
}
Then I get the last document:
{
"_id" : ObjectId("594b813ef59ccd076dd86064"),
"_class" : "com.notification.model.entity.Notification",
"notificationReferenceId" : "3c910cf5fcec42d6bfb78a9baa393efa",
"notificationReceivedAt" : ISODate("2017-06-22T08:35:10.474Z"),
"sendTo" : [
{
"userReferenceId" : "check",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "UNREAD"
}
]
},
{
"userReferenceId" : "hello",
"mediumAndDestination" : [
{
"medium" : "API",
"status" : "READ"
}
]
}
]
}
But with "UNREAD" the count is actually 0 for this sample.

Having trouble with a slow MongoDB aggregation query

I have the following aggregation query in MongoDB
return mongoose.model('Submission')
.aggregate([
{ $match: { client: { $in: clientIds }, admin: this._admin._id } },
{ $sort: { client: 1, submitted: -1 } },
{ $group: {
_id: '$client',
lastSubmitted: { $first: '$submitted' },
timezone: { $first: '$timezone' },
} },
])
.exec();
which is performing really badly on a collection with about 2000 documents. It usually takes 5 seconds to complete and I've seen as bad as 15 seconds. I have the following index on the submissions collection:
{
client : 1,
admin : 1,
assessment : 1,
submitted : -1,
}
I'm stuck as to why it's taking so long. Any suggestions?
EDIT
I've run the query
db.submissions.aggregate([
{$match: {
client: {$in: ['54a4cdfdd0666c243035dc98','55cc985291a0ffab6849de34']},
admin: '542b4af8880fc300007eb411'
}},
{$sort: {client:1, submitted: -1}},
{$group: {
_id: '$client',
lastSubmitted: {$first: '$submitted'},
timezone: {$first: '$timezone'}
}}
], {explain: true})
in the shell with explain and got
{
"stages" : [
{
"$cursor" : {
"query" : {
"client" : {
"$in" : [
"54a4cdfdd0666c243035dc98",
"55cc985291a0ffab6849de34"
]
},
"admin" : "542b4af8880fc300007eb411"
},
"fields" : {
"client" : 1,
"submitted" : 1,
"timezone" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "webdemo.submissions",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"admin" : {
"$eq" : "542b4af8880fc300007eb411"
}
},
{
"client" : {
"$in" : [
"54a4cdfdd0666c243035dc98",
"55cc985291a0ffab6849de34"
]
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"client" : 1,
"admin" : 1,
"assessment" : 1,
"submitted" : -1
},
"indexName" : "client_1_admin_1_assessment_1_submitted_-1",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"client" : [
"[\"54a4cdfdd0666c243035dc98\", \"54a4cdfdd0666c243035dc98\"]",
"[\"55cc985291a0ffab6849de34\", \"55cc985291a0ffab6849de34\"]"
],
"admin" : [
"[\"542b4af8880fc300007eb411\", \"542b4af8880fc300007eb411\"]"
],
"assessment" : [
"[MinKey, MaxKey]"
],
"submitted" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ]
}
}
},
{
"$sort" : {
"sortKey" : {
"client" : 1,
"submitted" : -1
}
}
},
{
"$group" : {
"_id" : "$client",
"lastSubmitted" : {
"$first" : "$submitted"
},
"timezone" : {
"$first" : "$timezone"
}
}
}
],
"ok" : 1
}
EDIT 2
The output I get from db.submissions.getIndices() is
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "webdemo.submissions"
},
{
"v" : 1,
"key" : {
"client" : 1,
"admin" : 1,
"assessment" : 1,
"submitted" : -1
},
"name" : "client_1_admin_1_assessment_1_submitted_-1",
"ns" : "webdemo.submissions",
"background" : true
}
]

Resources