Following code doesn't give the expected results, i've tried with multiple documents, those documents structured as follows.
_id: 5a7714d44c75220958e6aa01
imei:355227045347655
point: [3.143453333333333,80.10954]
heading: 0
speed:0
timestamp: 2018-02-04 19:42:36.000
point_distance: 525.25
Now i need to calculate sum of point_distance of every record match with given imei and time period. I've tried to achieve this with following code but returns nothing even if required data exists.
public Object findDistance(long imei, Date from, Date to) {
List aggregationOps = new ArrayList<>();
//operations
aggregationOps.add(match(Criteria.where("imei").is(imei)));
aggregationOps.add(match(Criteria.where("timestamp").gte(from)));
aggregationOps.add(match(Criteria.where("timestamp").lte(to)));
aggregationOps.add(group("imei").sum("point_distance").as("distance"));
aggregationOps.add(project("imei").and("distance").previousOperation());
AggregationOptions agOps = new AggregationOptions.Builder().allowDiskUse(true).cursor(new BasicDBObject()).build();
return (DistanceInfo) getMongoTemplate()
.aggregate(newAggregation(aggregationOps).withOptions(agOps), Location.class, DistanceInfo.class)
.getUniqueMappedResult();
}
Date > from and to are formatted as yyyy-MM-dd hh:mm:ss
DistanceInfo class
public class DistanceInfo {
long imei;
double distance;
}
I'm new to this mongodb stuff and no idea what did i do wrong, how can i correct this ? any help is much appreciated.
Try this. this should work
MatchOperation matchOperation = match(Criteria.where("imei").is(imei)
.and("timestamp").gte(from.getTime()).lte(to.getTime()));
GroupOperation groupOperation = group("imei").sum("point_distance").as("distance");
ProjectionOperation projectionOperation = project().andExpression("imei").as("imei")
.andExpression("distance").as("distance");
Aggregation aggregation = newAggregation(matchOperation, groupOperation, projectionOperation);
AggregationResults<DistanceInfo> results = mongoTemplate.aggregate(aggregation, "location", DistanceInfo.class);
return results.getMappedResults();
Related
I am a newbie to MongoDB and having a hard time querying documents with simple filtering. I would like to ask if what is the equivalent of CAST(datetime AS DATE) of mysql in MongoDB?
here's my code.
"inspection": {"hourbeg": "2020-11-23 10:12:20"}
Bson filters = Filters.and(new BasicDBObject("center.code", code),
new BasicDBObject("inspection.hourbeg", transactionDate));
Bson sorts = Sorts.descending("printedOn");
List<Document> vehicles = collection.find()
.sort(sorts)
.filter(filters)
.into(new ArrayList<Document>());
Im still trying to understand this Aggregate in MongoDB.
Never mind guys, I was able to solve my problem using this method. Might be useful for others as well.
String startDate = transactionDate.toString()+" 00:00:00";
String endDate = transactionDate.plusDays(1).toString()+" 00:00:00";
Bson gt = Filters.gt("inspection.hourbeg", startDate);
Bson lt = Filters.lt("inspection.hourbeg", endDate);
Bson andDate = Filters.and(gt, lt);
Bson filters = Filters.and(new BasicDBObject("center.code", code),
andDate);
Bson sorts = Sorts.descending("printedOn");
List<Document> vehicles = collection.find()
.sort(sorts)
.filter(filters)
.into(new ArrayList<Document>());
I am using Java to run queries on ElasticSearch. I'm having difficulties running range queries using boolQuery() API and queryStringQuery as input. here's the code:
QueryBuilder stringQuery = QueryBuilders.queryStringQuery(query);
QueryBuilder finalQuery = QueryBuilders.boolQuery().should(stringQuery);
The data field for date has the following format:
"startedOn" : { "type" : "date", "format": "yyyy-MM-dd HH:mm:ss.SSSSSS" }
and I am using queries like these:
StartedOn < 2020-06-29
StartedOn : [2020-06-20 TO 2020-06-25]
But none of them seem to return the correct results. Am I missing something here?
Thanks.
I already can execute the desired query on mongoshell, but i need to make the same query using Java and MongoOperations.
I have checked this question, which is very similar, but it only has one condition, as mine has two and uses the $gte and $lt operators. Here's the working mongo Query:
db.getCollection('example').update({"idVar": "desiredValued"}, { $pull: { "listaHoras": { $gte: ISODate("2016-11-06T05:50:00.000Z"), $lt: ISODate("2016-11-06T06:30:00.000Z")}}})
Sample doc:
"_id" : ObjectId("58221b4610a3c71f1894ce75"),
"idVar" : "56b11259272f5515b05d70bc",
"date" : ISODate("2016-11-06T03:00:00.000Z"),
"listaHoras" : [
ISODate("2016-11-06T05:40:00.000Z"),
ISODate("2016-11-06T06:30:00.000Z"),
ISODate("2016-11-06T06:40:00.000Z")
]
Where i'll have the ISODATE as a Date variable in Java, and the desiredValue as a String variable.
So far, i have i did the following, using the previously mentioned question as example:
BasicDBObject match = new BasicDBObject("idVar", desiredValue); // to match your document
BasicDBObject update = new BasicDBObject("listaHoras", new BasicDBObject("itemID", "1"));
coll.update(match, new BasicDBObject("$pull", update));
But, as you can see, this is NOT equivalent to the desired query. Since the match for the $pull is matching "itemID"with "1". I do not know, nor was i able to find how to properly use the $gte and $lt on the same query. Neither on how to use just one or both of them. I know it CAN be done as seen on the MongoOperatioons API which says:
"update - the update document that contains the updated object or $ operators to manipulate the existing object."
Anyone knows how it can be done? And if the Date type in Java matches the ISODATE on the Mongo?
I managed to find a solution. It is similar to what Veeram posted as a answer but it's slightly different. I simply removed his updateCriteria and used a BasicDBObject in it's place.
Here's how the full code looks:
Query findQuery = new Query();
Criteria findCriteria = Criteria.where("idVar").is(idVar);
findQuery.addCriteria(findCriteria);
Update update = new Update().pull("listaHoras", new BasicDBObject( "$gte", start).append("$lte", end));
mongoOps.updateMulti(findQuery, update, "CollectionName");
Where start and end are Date variables recieved by the method. Also important to note that Mongo uses the UTC as default timezone, so we must properly format the time in order for it to remove the desired values.
You can try something like below. This will remove the two items from the listaHoras array.
Query findQuery = new Query();
Criteria findCriteria =
Criteria.where("idVar").is("56b11259272f5515b05d70bc");
findQuery.addCriteria(findCriteria);
LocalDate startDt = LocalDate.of(2016, 11, 6);
LocalTime startTm = LocalTime.of(5, 40, 0);
LocalDate endDt = LocalDate.of(2016, 11, 6);
LocalTime endTm = LocalTime.of(6, 35, 0);
Date start = Date.from(LocalDateTime.of(startDt, startTm).toInstant(ZoneOffset.UTC));
Date end = Date.from(LocalDateTime.of(endDt, endTm).toInstant(ZoneOffset.UTC));
Query updateQuery = new Query();
Criteria updateCriteria =
Criteria.where(null).gte(start).lt(end);
updateQuery.addCriteria(updateCriteria);
mongoOperations.updateMulti(findQuery, update, "example");
How to Get the difference between the two date values in Mongo criteria and return value must be the difference of two date(i.e 10.01.2015 firs date and 20.01.2015 is second date) I need the value as 10 ? can any one help me how to get this in java using spring framework critieria?)
From this article What's new in Spring Data MongoDB 1.4 M1, you can use the andExpression to get the difference between the two date values:
.andExpression("endDateTime - startDateTime").as("duration")
As an example (untested):
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
TypedAggregation<Trip> agg = newAggregation(Trip.class,
match(Criteria.where("userId").is("54e5cead3ab5c1dd97422d86")),
project("endDateTime", "startDateTime")
.andExpression("endDateTime - startDateTime").as("duration")
);
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();
In Mongo shell, you can do the following:
db.collection.aggregate( {$match:{ your-match } },
{ $group : {
_id : "$userId",
duration : { $subtract:["$endDateTime", "$startDateTime"]}},
} } );
using spring boot reactive: This will give you duration in mili seconds like 864000000
List<AggregationOperation> operations = new ArrayList<>();
operations.add(project().andExclude("_id")
.and(ArithmeticOperators.valueOf("duration")
.subtract("startDateTime"))
.as("endDateTime"));
operations.add(groupOperation);
//response
reactiveMongoTemplate.aggregate(newAggregation(operations), "collName",
Class.class);
Rather than using minus operation inside projection, I will suggest you to use ArithmaticOperation.Subtract inside projection pipeline.
ProjectionOperation projectionOperation=Aggregation.project().and(Subtract.valueOf(Date.from(Instant.now())).subtract("fieldname"));
I am trying to convert the following query:
{ "cd" : { "$lte" : ISODate("2013-06-30T09:12:29Z") , "$gte" : ISODate("2013-06-11T09:12:29Z")}}
To use with MongoTemplate and Query.
At the moment i am doing and approach like:
Query query = new Query();
query.addCriteria(Criteria.where("cd").lte(request.getTo()).gte(request.getFrom()));
mongoTemplate.find(query,MyDesiredEntity.class)
But the query above returns no results when the first one returns around 15 which it should(request.getTo and request.getFrom are java.util.Date).
Is there a way to achieve this with org.springframework.data.mongodb.core.query.Query
I got this to work by reversing the lte and gte calls. I wrote a test to show it working:
#Test
public void shouldBeAbleToQueryBetweenTwoDates() throws Exception {
// setup
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
MongoTemplate mongoTemplate = new MongoTemplate(new Mongo(), "TheDatabase");
DBCollection collection = mongoTemplate.getCollection("myObject");
// cleanup
collection.drop();
// date that should match
Date expectedDate = dateFormat.parse("2013-06-12T00:00:00Z");
collection.insert(new BasicDBObject("cd", expectedDate));
// date that should not match (it's last year)
collection.insert(new BasicDBObject("cd", dateFormat.parse("2012-06-12T00:00:00Z")));
// create and execute the query
final Date to = dateFormat.parse("2013-06-30T09:12:29Z");
final Date from = dateFormat.parse("2013-06-11T09:12:29Z");
Query query = new Query();
query.addCriteria(Criteria.where("cd").gte(from).lte(to));
// check it returned what we expected
List<MyObject> basicDBObjects = mongoTemplate.find(query, MyObject.class);
Assert.assertEquals(1, basicDBObjects.size());
Assert.assertEquals(expectedDate, basicDBObjects.get(0).cd);
}
Notes:
This is TestNG not JUnit
I'm using SimpleDateFormat just to make testing of dates easier and (maybe) more readable
The main thing to note is:
query.addCriteria(Criteria.where("cd").gte(from).lte(to));
Before I reversed the order of the lte and gte the query was returning nothing.