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");
Related
Statement: I am trying to get the documents from MongoDB collection (Emp) using java.
Condition: Where it matches with the DOB(Date of birth) of a person.
Problem: However, it never returns a record.
But it works perfectly for other fields such as EmpID or EmpName etc. The document of my collection looks like this,
{
"_id" : ObjectId("5d4d9059f0b31921a4916a0c"),
"EmpID" : "1001",
"EmpName" : "John",
"Sal" : 30000.0,
"DOB" : ISODate("1989-06-09T18:30:00.000+0000"),
"Age" : 31.0
}
Please find the following java code that I have tried,
BasicDBObject dbo = new BasicDBObject();
dbo.append("DOB", new BasicDBObject("$eq","1989-06-10T00:00:00.000"));
FindIterable<Document> doc = coll.find(dbo);
for (Document dox : doc)
{
System.out.println(dox.toJson());
}
Please help
For ISODate it's needed to pass the Date object in BasicDBObject, not String, also timezone must be provided:
dbo.append("DOB", new BasicDBObject("$eq",new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX").parse("1989-06-10T00:00:00.000+0000")));
For Date Of Birth better to use $gte and $lt comparition operators together in order to take full range of single day, like that:
Date dayStart = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX").parse("1989-06-10T00:00:00.000+0000");
Date dayEnd = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX").parse("1989-06-11T00:00:00.000+0000");
BasicDBObject query = new BasicDBObject("Date", new BasicDBObject("$gt", dayStart).append("$lte", dayEnd));
I've an index in my elasticsearch and I want to have a query to compare 2 date fields.
assuming fields name are creationDate and modifiedDate. I want to get all documents which these 2 dates are the same in them.
I know it was possible to use FilteredQuery which is deprecated right now.
something like the bellowing code:
FilteredQueryBuilder query = QueryBuilders.filteredQuery(null,
FilterBuilders.scriptFilter("doc['creationDate'].value = doc['modifiedDate'].value"));
Also it's maybe possible to write manual scripts as string, but I doubt that this is the right solution. Any idea's to create the properly query would be appreciated.
Filtered query have been replaced by bool/filter queries You can do it like this:
BoolQueryBuilder bqb = QueryBuilders.boolQuery()
filter(QueryBuilders.scriptQuery("doc['creationDate'].value = doc['modifiedDate'].value"));
However, instead of using scripts at search time, you'd be better off creating a new field at indexing time that contains the information of whether creationDate and modifiedDate are the same dates. Then, you could simply check that flag at query time, it would be much more optimal and fast.
If you don't want to reindex all your data, you can update all of them with that flag, simply run an update by query like this:
POST my-index/_update_by_query
{
"script": {
"source": """
def creationDate = Instant.parse(ctx._source.creationDate);
def modifiedDate = Instant.parse(ctx._source.modifiedDate);
ctx._source.modified = ChronoUnit.MICROS.between(creationDate, modifiedDate) > 0;
""",
"lang": "painless"
},
"query": {
"match_all": {}
}
}
And then your query will simply be
BoolQueryBuilder bqb = QueryBuilders.boolQuery()
filter(QueryBuilders.termQuery("modified", "false");
Here is the sample document of my MongoDB:
user:{
_id:1,
name:'xyz',
age:12,
mobile:21321312,
transaction:[{
trans_id:1,
prod:'a',
purchasedAt:ISODate("2015-02-01"),
},
{
trans_id:2,
prod:'b',
purchasedAt:ISODate("2015-02-01")
},
{
trans_id:3,
prod:'c',
purchasedAt:ISODate("2014-11-24")
}]
,...
}
My query looks like:
db.user.find({transaction:{$elemMatch:{prod:'a', purchasedAt:ISODate("2015-02-01")}}, transaction:{$elemMatch:{prod:{$nin:['b','c']}, purchasedAt:ISODate("2015-02-01")}}}).count()
I am trying to get the user count who have purchased product 'a' on date "2015-02-01" but not have purchased product b & c on same day.
So while trying to do this in Java with the query:
coll.find(new BasicDBObject().append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", 'a').append("purchasedAt", Date))).append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", new BasicDBObject("$nin",['b','c'])).append("purchasedAt", Date)));
I have also tried:
coll.find(new BasicDBObject("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", 'a').append("purchasedAt", Date))).append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", new BasicDBObject("$nin",['b','c'])).append("purchasedAt", Date)));
where Date is "2015-02-01" in util.Date object.
I found out that Java ignores the $in part of the query, i.e. it ignores {transaction:{$elemMatch:{prod:'a', purchasedAt:ISODate("2015-02-01")}} & performs only $nin part.
I found out it by DBCursor object.
Here's the output of the cursor:
Cursor: Cursor id=0, ns=mydb.user, query={ "transaction" : { "$elemMatch" : { "prod" : { "$nin" : [ "b" , "c"]} , "purchasedAt" : { "$date" : "2015-02-01T00:00:00.000Z"}}}}, numIterated=0, readPreference=primary
Because of this my result is inaccurate. I wonder why the exact same query works well in Mongo shell but doesn't with Java API. Is there anything wrong with my query structure?
My guess is that this question is now moot, but, if you still do not consider it answered, are you looking for the "$not" operator, which can check for non-existance sort of.
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 have a list of files in a GridFS that I am attempting to query by date. The sample document looks like the following:
{
"_id" : ObjectId("52e431d3e84f6fa18c53c808"),
"chunkSize" : NumberLong(262144),
"length" : NumberLong(13021),
"md5" : "0eb01f0d266f4bf4764d4ffc7e70a7ed",
"filename" : "120_1390686674383",
"contentType" : null,
"uploadDate" : ISODate("2014-01-25T21:51:15.049Z"),
"aliases" : null
}
I am attempting to get the "most recent" according to timestamp by doing the following:
DateTime dt = new DateTime(queryObj.getTime()); //org.joda.DateTime
BasicDBObject sort = new BasicDBObject();
sort.put("uploadDate", -1);
BasicDBObject query = new BasicDBObject();
query.put("uploadDate", new BasicDBObject("$gte", dt));
DBCursor cursor = fileStore.getFileList(query, sort);
If I simply sort the fileStore I get numerous records back and I can enumerate via the cursor. However, whenever I try to use $gte or $lte I get zero results.
Is there a missing step?
You're passing in a joda DateTime reference which the driver doesn't know how to do. If you check the logs, you'll probably find that it essentially calls a toString() on that and passes that in. Since a string is not a Date, the comparison fails. Try passing in just a java.util.Date (you can get one from the DateTime, iirc).