need help for mongodb query for between optional parameter - mongodb

I am trying to create mongodb optional query in spring data but getting error. Can anyone please help me to resolve this issue.
Please note this query is for between date range
Below is the query
#Query(value = "{ $and: [
{$or : [ { $where: '?0 == null' } , { createdDate : {$gt : ?0} }]},
{$or : [ { $where: '?1 == null' } , { createdDate : {$lt : ?1} }]}
] }")
Below is the generated query
{ "$and" : [
{ "$or" : [ { "$where" : "{ \"$date\" : \"2016-02-28T18:30:00.000Z\"} == null"} ,
{ "createdDate" : { "$gt" : { "$date" : "2016-02-28T18:30:00.000Z"}}}]} ,
{ "$or" : [ { "$where" : "{ \"$date\" : \"2016-03-11T18:30:00.000Z\"} == null"} ,
{ "createdDate" : { "$lt" : { "$date" : "2016-03-11T18:30:00.000Z"}}}]}
]}
getting below error
org.springframework.data.mongodb.UncategorizedMongoDbException: { "serverUsed" : "localhost:27017" , "waitedMS" : 0 , "ok" : 0.0 , "errmsg" : "Failed to call method" , "code" : 1}; nested exception is com.mongodb.CommandFailureException: { "serverUsed" : "localhost:27017" , "waitedMS" : 0 , "ok" : 0.0 , "errmsg" : "Failed to call method" , "code" : 1}

Your query is not very clear, but from what I understand (based on ?0 and ?1 interpolation and your comments):
#Query(value = "{ $or: [
{$and : [ { toDate: { $exists: false } } , { startDate: { $exists: true } }, { createdDate : {$gt : ?0} }]},
{$and : [ { toDate: { $exists: true } }, { startDate: { $exists: false } } , { createdDate : {$lt : ?1} }]},
{$and : [ { toDate: { $exists: false } }, { startDate: { $exists: false } }
] }")

Related

Having problem with date comparison in #Query [mongo/springboot]

I am trying to create a custom Query annotation in a MongoRepository. Everything is fine except the date comparison. I need to find all items that were created on a specific day, so I give 2 date objects with the good date and the times fixed to 00:00 and 23:59 to gte and lte operators.
This is the request sent according to the debug trace:
2022-08-27 01:52:25.817 DEBUG 440959 --- [or-http-epoll-4] o.s.data.mongodb.core.MongoTemplate : find using query: { "$and" : [{ "$or" : [{ "$where" : "5 == null"}, { "rating" : 5}]}, { "$or" : [{ "$where" : "null == null"}, { "productId" : null}]}, { "$or" : [{ "$where" : "APPROVEDD == null"}, { "moderationStatus" : "APPROVEDD"}]}, { "$or" : [{ "$where" : "true == false"}, { "clientResponses" : { "$exists" : true, "$not" : { "$size" : 0}}}]}, { "$or" : [{ "$where" : "2022-01-29T23:00:00Z == null || 2022-01-30T22:59:59Z == null"}, { "submissionTime" : { "$gte" : { "$date" : "2022-01-29T23:00:00Z"}, "$lte" : { "$date" : "2022-01-30T22:59:59Z"}}}]}]} fields: Document{{}} for class: class com.company.productreviews.cdpresponseportalapi.model.Review in collection: reviews
Dates seem to be in the good format, but mongo gives me an error (only when I use the date filter of my request):
Query failed with error code 139 and error message 'SyntaxError: identifier starts immediately after numeric literal' on server mongodb-databaserevqa-shard-00-02.xxocp.mongodb.net:27017; nested exception is com.mongodb.MongoQueryException: Query failed with error code 139 and error message 'SyntaxError: identifier starts immediately after numeric literal' on server mongodb-databaserevqa-shard-00-02.xxocp.mongodb.net:27017
My repo:
public interface ReviewRepository extends MongoRepository<Review, String> {
#Query("{ $and : [" +
"{ $or : [ { $where: '?0 == null' }, { 'rating': ?0 } ] }," +
"{ $or : [ { $where: '?1 == null' }, { 'productId': ?1 } ] }," +
"{ $or : [ { $where: '?2 == null' }, { 'moderationStatus': ?2 } ] }," +
"{ $or : [ { $where: '?3 == false' }, { 'clientResponses': { $exists: true, $not: { $size: 0 } } } ] }," +
"{ $or : [ { $where: '?4 == null || ?5 == null' }, { 'submissionTime': { $gte: ?4, $lte: ?5 } } ] }," +
"]}")
List<Review> findAll(Integer rating, Integer productId, String moderationStatus, boolean withAnswersOnly, Date submissionDateStartRange, Date submissionDateEndRange);
Review findOneByReviewId(int reviewId);
}
It seems to be a syntax error when your date value are not null. Wrapping your statement in quote should do the trick. So in your last #Query statement do the following:
"{ $or : [ { $where: '\"?4\" == \"null\" || \"?5\" == \"null\"' }, { 'submissionTime': { $gte: ?4, $lte: ?5 } } ] },"

MongoDB : The argument to $size must be an array, but was of type: missing

It throws an error :org.springframework.dao.InvalidDataAccessApiUsageException: Command execution failed: Error [The argument to $size must be an array, but was of type: missing]
The mongo document from which I have to find the count of userid of for specific survey,question,option ids
{
"_id" : ObjectId("5ea31dce0e4d4b09e4db0cd6"),
"_class" : "com.litmus7.river.commons.common.model.survey.Result",
"survey_id" : "5ea30df40e4d4b4de4d7b6d7",
"survey_question" : [
{
"question_id" : "5ea30df40e4d4b4de4d7b6d2",
"question_options" : [
{
"option_id" : "5ea30df40e4d4b4de4d7b6d1",
"user_id" : [
"111",
"222"
]
},
{
"option_id" : "5ea30df40e4d4b4de4d7b6cf",
"user_id" : [
"333",
"444"
]
}
]
},
{
"question_id" : "5ea30df40e4d4b4de4d7b6d6",
"question_options" : [
{
"option_id" : "5ea30df40e4d4b4de4d7b6d3",
"user_id" : [
"111",
"222"
]
},
{
"option_id" : "5ea30df40e4d4b4de4d7b6d5",
"user_id" : [
"333",
"444"
]
}
]
}
]
}
** The Aggregation function written : **
public int getResultCount() {
Aggregation aggregation = newAggregation(
match(Criteria.where("survey_id").is("5ea30df40e4d4b4de4d7b6d7")),
unwind("survey_question"),
match(Criteria.where("survey_question.question_id").is("5ea30df40e4d4b4de4d7b6d2")),
unwind("survey_question.question_options"),
match(Criteria.where("survey_question.question_options.option_id").is("5ea30df40e4d4b4de4d7b6d1")),
project()
.and("survey_question.question_options.user_id")
.size()
.as("count"));
AggregationResults<Object> results = mongoTemplate.aggregate(aggregation, "result", Object.class);
if (results != null && results.getMappedResults() != null && results.getMappedResults().size() > 0) {
Integer intCount = (Integer) ((Map) results.getMappedResults().get(0)).get("count");
return intCount;
}
return 0;
}
You need to use $ifNull operator:
.project().and(ArrayOperators.arrayOf(ConditionalOperators
.ifNull("survey_question.question_options.user_id").then(Collections.emptyList())).length())
.as("count");

Mongodb : How should I get original Json structure after filter the records based on requirement?

I am new to mongodb.
I have a Json document in collection like :
{
"_id" : ObjectId("55abf32f358e3aca807f0e6a"),
"usercbid" : 1995492.0000000000000000,
"defaultnotifytype" : {
"status" : true,
"alert" : true,
"action" : true
},
"calendar" : {
"alert" : 2468.0000000000000000,
"action" : 13579.0000000000000000,
"status" : 123456.0000000000000000
},
"assignment" : [
{
"orgid" : {
"service" : "AVPN",
"adminemail" : "pl9129#att.com",
"notifytype" : {
"status" : true,
"alert" : true
},
"keytype" : "MCN",
"KeyValue" : "SK1383"
}
},
{
"orgid" : {
"KeyValue" : "DD3342",
"service" : "<all>",
"keytype" : "MCN"
}
},
{
"orgid" : {
"notifytype" : {
"optout" : true
},
"keytype" : "MCN",
"keyvalue" : "<all>",
"service" : "MVPN"
}
},
{
"order" : {
"date" : "2015-03-15",
"adminemail" : "abc.com",
"notifytype" : {
"alert" : true
},
"id" : 123456.0000000000000000
}
},
{
"order" : {
"id" : 135246.0000000000000000,
"date" : "2015-03-17",
"adminemail" : "abc.com"
}
}
]
}
I would like to filter above json document with following condition:
var result = db.subscription.aggregate(
[ { $unwind: "$assignment" }
, {$match : {$or:
[
{
"assignment.order.id" : 123456
},
{
"assignment.orgid.keytype" : { $in: ["MCN"]}
,"assignment.orgid.KeyValue" : { $in: ["<all>","SK1383"]}
,"assignment.orgid.service" : { $in: ["<all>","AVPN"]}
}
]
}
}
,{$group: {_id: "$_id", assignment: {$push: "$assignment"}}}
// ,{$project : { usercbid : $usercbid, defaultnotifytype : 1, calendar : 1, assignment: 1} }
]
)
printjson(result);
Result of above query is :
{
"result" : [
{
"_id" : ObjectId("55abf32f358e3aca807f0e6a"),
"assignment" : [
{
"orgid" : {
"service" : "AVPN",
"adminemail" : "pl9129#att.com",
"notifytype" : {
"status" : true,
"alert" : true
},
"keytype" : "MCN",
"KeyValue" : "SK1383"
}
},
{
"order" : {
"date" : "2015-03-15",
"adminemail" : "pl9129#att.com",
"notifytype" : {
"alert" : true
},
"id" : 123456
}
}
]
}
],
"ok" : 1
}
But my final result lost the following original content:
"usercbid" : 1995492.0000000000000000,
"defaultnotifytype" : {
"status" : true,
"alert" : true,
"action" : true
},
"calendar" : {
"alert" : 2468.0000000000000000,
"action" : 13579.0000000000000000,
"status" : 123456.0000000000000000
},
How should I append above original content with filtered records?
Thanks,
$Fisrt is the operator which helps you getting the required output.
When you do a $Group, the result of the $Group pipeline operator contains only those fields which are specified inside the $Group pipeline operator.
So, from your query we can notice that you are grouping based on "_Id" and you are selecting only "assignment" key field, so the OUTPUT of this group pipeline operator will contain only those 2 fileds ( "_ID" and "assignment" ).
To make sure that the other left out feilds ( usercbid, defaultnotifytype , calendar ) to be part of the $Group pipeline output, we need to mention that explicitly in the Group pipeline using $First as below :
{ $group: { _id: "$_id", assignment: {$push: "$assignment"},
usercbid : { $first : "usercbid"} ,
defaultnotifytype : { $first : "defaultnotifytype" } ,
calendar : { $first : "calendar"}
}
}
$First Returns the value that results from applying an expression to the first document in a group of documents that share the same group by key.
Please check the below query, it will help you in fetching the required output :
var result = db.subscription.aggregate(
[ { $unwind: "$assignment" }
, { $match : {$or:
[
{
"assignment.order.id" : 123456
},
{
"assignment.orgid.keytype" : { $in: ["MCN"]}
,"assignment.orgid.KeyValue" : { $in: ["<all>","SK1383"]}
,"assignment.orgid.service" : { $in: ["<all>","AVPN"]}
}
]
}
}
,{ $group: { _id: "$_id", assignment: {$push: "$assignment"},
usercbid : { $first : "usercbid"} ,
defaultnotifytype : { $first : "defaultnotifytype" } ,
calendar : { $first : "calendar"}
}
}
]
).pretty();

How to find distinct (and greatest) values from collection using mongoDB?

I have following collection -
[{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.20",
"runtimeMilliSeconds" : 1422007201815
}
{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.20",
"runtimeMilliSeconds" : 1422008101736
}
{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.21",
"runtimeMilliSeconds" : 1422009002239
}
{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.21",
"runtimeMilliSeconds" : 1422009901379
}
{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.22",
"runtimeMilliSeconds" : 1422010801685
}
{
"customerId" : "54a32e9f1e14fa5476d654db",
"hostId" : "192.168.20.22",
"runtimeMilliSeconds" : 1422010801585
}]
I also have list of hostIds as : [ "192.168.20.20" , "192.168.20.21" , "192.168.20.22"]
I want to match hostId list with collection and find latest (greatest) runtimeMilliSeconds only to get following output -
[{"hostId":"192.168.20.20", "runtime": 1422007201815},
{"hostId":"192.168.20.21", "runtime": 1422009002239},
{"hostId":"192.168.20.22", "runtime": 1422010801685}]
I have tried out following with mongo aggregation -
{ "$match" : { "hostId" : { "$in" : [ "192.168.20.20" , "192.168.20.21" , "192.168.20.22"]} ,
"customerId" : "54a32e9f1e14fa5476d654db"}},
{ "$sort" : { "runtimeMilliSeconds" : -1}},
{ "$group" : { "_id" : { "hostId" : "$hostId" ,
"runtime" : "$runtimeMilliSeconds"}}},
{ "$project" : { "hostId" : "$_id.hostId" ,
"runtimeMilliSeconds" : "$_id.runtime" , "_id" : 0}}
But it gives me all values in collection.
How do I get above mentioned output using mongo??
Use $first operator
db.test.aggregate(
[
{ "$match" : { "hostId" : { "$in" : [ "192.168.20.20" , "192.168.20.21" , "192.168.20.22"]} , "customerId" : "54a32e9f1e14fa5476d654db"}},
{ "$sort" : { "runtimeMilliSeconds" : -1}},
{ "$group" : { "_id" : { "hostId" : "$hostId" } , "runtime" : { $first : "$runtimeMilliSeconds" }}},
{ "$project" : { "hostId" : "$_id.hostId" , "runtimeMilliSeconds" : "$runtime" , "_id" : 0}}
]
)
output will be:
{
"result" : [
{
"hostId" : "192.168.20.20",
"runtimeMilliSeconds" : 1422008101736
},
{
"hostId" : "192.168.20.21",
"runtimeMilliSeconds" : 1422009901379
},
{
"hostId" : "192.168.20.22",
"runtimeMilliSeconds" : 1422010801685
}
],
"ok" : 1
}
The most efficient way to do that would be to use the $max operator (no $sort stage needed):
[
{"$match" : {
"hostId" : { "$in" : [ "192.168.20.20" , "192.168.20.21" , "192.168.20.22"]},
"customerId" : "54a32e9f1e14fa5476d654db"
}},
{ "$group" : {
"_id" : "$hostId",
"runtime" : {"$max" : "$runtimeMilliSeconds"}
}},
{"$project" : {
"hostId" : "$_id" ,
"runtime" : 1,
"_id" : 0
}}
]
Hi I think you are close to your answer but following some changes will meet your output
{
"$match": {
"hostId": {
"$in": [
"192.168.20.20",
"192.168.20.21",
"192.168.20.22"
]
},
"customerId": "54a32e9f1e14fa5476d654db"
}
},
{
"$group": {
"_id": {
"hostId": "$hostId",
"runtime": "$runtimeMilliSeconds"
}
}
},
{
"$sort": {
"_id.runtime": -1
}
}{
"$group": {
"_id": "$_id.hostId",
"runtime": {
"$first": "$_id.runtime"
}
}
}

Count statement in mongodb

I have the following mysql statement but I'd like to use it with spring mongodb driver for java. How to convert it? Have looked at aggregation but have no clue how to.
SELECT SUM(CASE WHEN CreatedTime BETWEEN ('7:00:00' AND '7:14:59') THEN 1 ELSE 0) as firstCount,
SUM(CASE WHEN CreatedTime BETWEEN ('7:15:00' AND '7:29:59') THEN 1 ELSE 0) as secondCount,
FROM MyTable
Where username='Jim'
Mongo document:
{ _id: ObjectId("5asd3ea3402984ca53"), username: "Jim", comment: "hi", CreatedTime: ISODate("2014-10-15T16:39:26.870Z") }
UPDATE on translating it to java using spring data:
When calling getTemplate().executeCommand(match); I get this:
{ "serverUsed" : "xxxxxxx" , "ok" : 0.0 , "errmsg" : "no such cmd: $match" , "bad cmd" : {
"$match" : { "username" : "Jim"} ,
"$group" : {
"firstCount" : {
"$sum" : {
"$cond" : {
"if" : {
"$and" : [ [ { "$gte" : { "$CreatedTime" : { "$date" : "2014-09-20T16:02:10.924Z"}}} , 1 , 0] ,
[ { "$lte" : { "$CreatedTime" : { "$date": "2014-10-20T15:48:19.744Z"}}} , 1 , 0]]} ,
"then" : { "$ifTrue" : 1} ,
"else" : { "$else" : 0}
}
}
}
}}}
Code that I use to get the JSON is here (its quite long).Query looks about the same that was suggested by #Wizard
What could be the trouble with $match? I read somewhere in Stackoverflow that old versions of mongodb do not support $match but I have Aug-2014 release so that can't be the case.
Like this:
db.MyTable.aggregate([{
$match : {
username : 'Jim'
}
}, {
$group : {
_id : 0,
firstCount : {
$sum : {
$cond : {
"if" : {
$and : [{
$gte : [ "$CreateTime", '7:00:00' ]
}, {
$lte : [ "$CreateTime", '7:14:59' ]
}]
},
"then" : 1,
"else" : 0
}
}
},
secondCount : {
$sum : {
$cond : {
"if" : {
$and : [{
$gte : [ "$CreateTime", '7:15:00' ]
}, {
$lte : [ "$CreateTime", '7:29:59' ]
}]
},
"then" : 1,
"else" : 0
}
}
},
}
}]);