MongoDB date range query throws syntax errors when used in aggregation - java

I am using Java Driver for MongoDB to query database. I am getting syntax errors when trying to execute range query on a date field in aggregation. It works fine if I use it as part of find query only. Aggregation is the problem here. I used the following Java query code :
new BasicDBObject("requestDate", BasicDBObjectBuilder.start("$gte", fromDate).add("$lte", toDate).get());
requestDate is the field I want to query. I tried debugging the code and ran the generated query using command line and I still get syntax errors. Not sure whats wrong here.
Mongo Query generated by the code:
{ "requestDate" : { "$gte" : { "$date" : "2015-03-01T05:00:00.000Z"} , "$lte" : { "$date" : "2015-03-09T04:00:00.000Z"}}}
EDIT. Adding relevant Code:
BasicDBObject match = null;
if (organizationId != null) {
match = new BasicDBObject("$match", new BasicDBObject("organizationId", organizationId));
}
if (optionalParams != null) {
Date fromDate = (Date) optionalParams.get("fromDate");
Date toDate = (Date) optionalParams.get("toDate");
if (match == null) {
match = new BasicDBObject("requestDate", new BasicDBObject("$gte", fromDate).append("$lte", toDate));
} else {
match.append("requestDate", new BasicDBObject("$gte", fromDate).append("$lte", toDate));
}
}
DBObject project = new BasicDBObject("$project", MongoDBUtil.getProjectDateFields());
DBObject groupFields = new BasicDBObject("_id", MongoDBUtil.getGroupDateFields()).append("total", new BasicDBObject("$sum", 1));
DBObject group = new BasicDBObject("$group", groupFields);
DBObject sort = new BasicDBObject("$sort", new BasicDBObject("_id", 1));
List<DBObject> pipeline;
if (match != null) {
pipeline = Arrays.asList(match, project, group, sort);
} else {
pipeline = Arrays.asList(project, group, sort);
}

In brief, you broke your $match pipeline stage construction because like all pipeline directives that "key" is "mandatory. Your conditional building should not be checking is the existing state is null but rather "appending" to what you need to define at the top as a "empty" $match:
// BasicDBObject match = null; // wrong!
BasicDBObject match = new BasicDBObject("$match", new BasicDBObject()); // correct!
if (organizationId != null) {
match.append("organizationId", organizationId);
}
if (optionalParams != null) {
Date fromDate = (Date) optionalParams.get("fromDate");
Date toDate = (Date) optionalParams.get("toDate");
match.append("requestDate", new BasicDBObject("$gte", fromDate)
.append("$lte", toDate));
}
So then, without a value for organizationId you get a pipeline generated like so:
{ "$match": {
"requestDate" : {
"$gte" : { "$date" : "2015-03-01T05:00:00.000Z" },
"$lte" : { "$date" : "2015-03-09T04:00:00.000Z" }
}
}
Without that $match key this is not a valid pipeline stage and it will error on submission to the aggregation pipeline.
There is nothing wrong with the generated syntax and all, full explanation is in the manual under Extended JSON. The MongoShell uses "strict" mode as mentioned there so it requires it's own wrappers.

Related

Spring Data MongoDB Aggregation Match with Date and average

I'm using mongoDB java driver to query transactions between date range and average of value with aggregate framework. This is my mongo query (it works):
db.campaignsForChatUsers.aggregate([{$match:{'created' :
{$gte : ISODate('2017-09-30T11:35:35.155Z'), $lt : ISODate('2019-09-30T11:35:35.155Z')}}},
{$group: {_id : "_id", aveTime: { $avg : "$details.nostradamusOfferCalculatingTime"}}}])
The java code I'm using to make query:
public Double getAverageTimeWithPeriod(){
MongoCollection<Document> dbCollection = mongoTemplate.getCollection(ChatUserCampaign.COLLECTION_NAME);
Date fromDate = LocalDate.parse("2019-10-01").toDate();
Date toDate = LocalDate.parse("2020-03-13").toDate();
BasicDBObject match = new BasicDBObject("$match",
new BasicDBObject("created",
new BasicDBObject("$gte", fromDate/*getDate("01/10/2019")*/).append("$lt", toDate/*getDate("20/10/2019")*/)));
BasicDBObject group = new BasicDBObject("$group",
new BasicDBObject ("_id",
new BsonField("averageTime",
new BsonDocument("$avg,",
new BsonString("$details.nostradamusOfferCalculatingTime")))));
List<Bson> aggregators = null;
assert false;
aggregators.add(match);
aggregators.add(group);
AggregateIterable<Document> output = dbCollection.aggregate(aggregators);
//AggregationOutput output1 = dbCollection.aggregate(Arrays.asList(match,group));
Document result = output.first();
return result.getDouble("averageTime");
}
But this jaja query is returning empty results (in line with: aggregators.add(match); - I get NullPointerException).
Mongo don't understand 01/10/2019 format rather it understand ISO format. Hence, you must be using something like ISODate("2015-06-17T10:03:46Z") in place of fromDate and toDate. Date or LocalDate don't work.

Aggregation query error 15982: "exception: field path references must be prefixed with a '$'"

Need help in creation aggregation query. At this time i have next code:
MongoClient client = new MongoClient();
DB base = client.getDB("school");
DBCollection collection = base.getCollection("students");
DBObject match = new BasicDBObject("$match", new BasicDBObject("scores.type", "homework"));
DBObject unwind = new BasicDBObject("$unwind", "scores");
DBObject sortFields = new BasicDBObject("_id", 1);
sortFields.put("scores.score", -1);
DBObject sort = new BasicDBObject("$sort", sortFields);
List<DBObject> pipeline = Arrays.asList(match, unwind, match, sort);
AggregationOutput output = collection.aggregate(pipeline);
for (DBObject result : output.results()) {
System.out.println(result);
}
But after run console said:
> Command failed with error 15982: 'exception: field path references
> must be prefixed with a '$' ('scores'' on server 127.0.0.1:27017. The full response is { "errmsg" : "exception: field path references must be prefixed with a '$' ('scores'", "code" : 15982, "ok" : 0.0 }
For this I used mongo aggreagtion query as below :
db.students.aggregate([
{ $match: { "scores.type": "homework" }},
{'$unwind' : '$scores' },
{ $match : {"scores.type" : "homework"}},
{$sort : {_id:1, "scores.score":1}}
])
What I'm doing wrong?
Thx yogesh!
The answer is
DBObject unwind = new BasicDBObject("$unwind", "$scores");

MongoDB Java Driver: Aggregation using $match with multiple condition

I've been doing some search but haven't been able to find the answer. From docs, In mongo, if
$match: { type: "airfare"}
translated as:
DBObject match = new BasicDBObject("$match", new BasicDBObject("type", "airfare") );
in Mongo Java driver, how to translate this one into Mongo java driver?
$match : { score : { $gt : 70, $lte : 90 } }
EDIT
This is what I actually wanted to do:
$match : { bookingDateTime: { $gte : fromDate, $lte : toDate } }
And this is how implemented in mongodb java driver, with no luck so far:
DBObject matchFields2 = new BasicDBObject("$match", new BasicDBObject("bookingDateTime", new BasicDBObject("$gte", fromDate.getTime()).append("$lte", toDate.getTime())));
DBObject match2 = new BasicDBObject("$match", matchFields2);
fromDate is a Calendar object so hence the getTime() method to convert Calendar class to Date class.
Object match = new BasicDBObject("$match",
new BasicDBObject("score",
new BasicDBObject("$gt", 70).append("$lte", 90) ) )

MongoDB $in with $and Query

Is this type of query possible?
I need to query the database for data for a specified date for a set of specified stocks. So the data needs to have "this" date and be one of "these" symbols.
I have the following code:
public void findDateStockSet(String date, ArrayList<String> symbolSet) throws UnknownHostException {
this.stocks = this.getCollectionFromDB();
BasicDBObject objectToFind = new BasicDBObject();
List<BasicDBObject> obj = new ArrayList<BasicDBObject>();
obj.add(new BasicDBObject("date", date));
obj.add(new BasicDBObject("symbol", new BasicDBObject("$in", symbolSet)));
objectToFind.put("$and", obj);
DBCursor cursor = this.stocks.find(objectToFind);
System.out.println("Finding Stocks");
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
System.out.println();
}
This always comes up null. Can someone explain how to make a query like this work?
You don't need to use $and operator, just build the query as the json below:
{ "date" : "20100223", "symbol" : { $in : [ "appl", "goog" ] } }
I like to use BasicDBObjectBuilder util class to build DBObjects. So your query will be:
DBObject query = BasicDBObjectBuilder.start()
.add("date", date)
.push("symbol")
.add("$in", symbolSet)
.get();

Updating fields in embedded documents with MongoDB Java Driver?

I'm trying to perform an update on an embedded document in MongoDB with the Java driver but receive an IllegalArgumentException that states "fields stored in the db can't have . in them"
My document has the structure:
{
"_id" : ObjectId("5155d102a47d7b00b7e4bed2"),
"foo" : {
"bar" : {
"name" : "Now"
}
}
}
and I want to perform an update like this
var query = {_id:ObjectId("5155d102a47d7b00b7e4bed2")};
var update = {"foo.bar.time":new Date()};
var withSet = {$set:update};
db.samples.update(query,withSet);
which from the console correctly modifies the document
{
"_id" : ObjectId("5155d102a47d7b00b7e4bed2"),
"foo" : {
"bar" : {
"name" : "Now",
"time" : ISODate("2013-03-29T18:02:51.591Z")
}
}
}
Trying to do the same in Java I have not been successful. I've tried this:
BasicDBObject query = new BasicDBObject("_id", new ObjectId("5155d102a47d7b00b7e4bed2"));
BasicDBObject time = new BasicDBObject("time", new Date());
BasicDBObject bar = new BasicDBObject("bar", time);
BasicDBObject foo = new BasicDBObject("foo", bar);
BasicDBObject withSet = new BasicDBObject("$set", foo);
samples.update(query, withSet);
But it clobbers the embedded bar object, destroying name.
I've also tried:
BasicDBObject foo = new BasicDBObject();
foo.append("foo.bar.time", new Date());
samples.update(query, foo)
But receive an IllegalArgumentException.
I've seen other answers chosen on Stack Overflow that include this dot notation. Is there something I'm missing? What is the proper way to update a field in an embedded document?
In your last example the $set part is missing, maybe this causes the problem. Try
samples.update(
query,
new BasicDBObject(
"$set",
new BasicDBObject("foo.bar.time", new Date())
));
instead.

Categories

Resources