How to convert MongoDB results from $dateToString operation to Java object - java

I have obtained "date only" results from ISODate datatype from my mongo document using following query :
db.getCollection('tournamentactivity').aggregate( [
{
$project:{
"yearMonthDay":
{
"$dateToString" :
{
"format" : "%Y-%m-%d",
"date" : "$activityDate"
}
},
"tid" : 1,
"dayActivity.type" : 1
}
},
{
$match:{
$and : [
{ "tid" : "12345678" },
{ "yearMonthDay" : "2017-11-05" }
]
}
}] )
All good till here. But I am trying to convert the same in Java object as I have to send the same in my response type.
I have followed this tutorial : Projections and tried to do the same in my Java code as below :
ProjectionOperation projectToMatchModel = project().
andExpression("format").as("%Y-%m-%d").
andExpression("date").as("$activityDate");
I have written Aggregation operations prior to this, but I have no idea how to convert this result/query in Java code.
Can anyone guide me where am I going wrong and how to use the same in
Aggregation aggregation = newAggregation(
); method ?
Thanks.
FYI : I have no idea on converting this query in my aggregation object and I might be wrong on choice of projection operations. Please go easy on me if I am wrong !

You can try below aggregation.
Add below imports
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.Fields.field;
import static org.springframework.data.mongodb.core.aggregation.Fields.from;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.DateOperators;
Option 1 ( Using extension method )
Aggregation aggregation = newAggregation(
project(from(field("dayActivity.type", "dayActivity.type"),
field("tid"))).
and("activityDate").dateAsFormattedString("%Y-%m-%d").as("yearMonthDay"),
match(where("yearMonthDay").is("2017-11-05").and("tid").is("12345678"))
);
Option 2 ( Using Aggregation Expression )
Aggregation aggregation = newAggregation(
project(from(field("dayActivity.type", "dayActivity.type"),
field("tid"))).
and(DateOperators.dateOf("activityDate").toString("%Y-%m-%d")).as("yearMonthDay"),
match(where("yearMonthDay").is("2017-11-05").and("tid").is("12345678"))
);

Related

scala MongoDB update with $cond and $not (not display the same result)

If you can help me, I have an update in mongo with $cond , this update is only done if the field is empty, otherwise it updates the field with another value. Example in mongo db
I want to update the field camp1
if camp1 no exits = values
if camp1 exits = value2
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
[{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}]);
Result:
{
"_id" : ObjectId("62dd08c3f9869303b79b323b"),
"cdAccount" : "ES3100810348150001326934",
"camp1" : "value2"
}
Now I do the same in scala with this code
def appendIfNotNull(key: String,value :Object) = {
var eq2Array = new util.ArrayList[Object]()
eq2Array.add("$"+key)
val eq2Op = new Document("$not", eq2Array)
var condList = new util.ArrayList[Object]()
condList.add(eq2Op)
condList.add(value.asInstanceOf[AnyRef])
//condList.add("$"+key)
condList.add("value2")
val availDoc =
new Document("$cond",
new Document("$cond", condList)).toBsonDocument(classOf[BsonDocument],getCodecRegistry).get("$cond")
println("availDoc : " + availDoc)
documentGrab.append(key,availDoc)
}
val finalVar = appendIfNotNull("camp1","values")
println("finalVar : " + finalVar)
availDoc : {"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}
finalVar : Document{{camp1={"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}
val updateDocument = new Document("$set" , finalVar )
println("updateDocument : " + updateDocument)
collectionA.updateMany(Filters.eq("cdAccount", "ES3100810348150001326934"),updateDocument)
The only difference I see is that in mongodb the "[" is added at the beginning of the $set and it does it well
MongoDB
[ {$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} ] --> Ok Update
Scale
{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} --> Ok in scala , but I get the result II
I am using mongodb 5.0.9
Now in mongodb I execute the statement made in scala
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
{$set :{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}});
When I run it in scala the same thing happens
Result II :
{
"cdAccount" : "ES3100810348150001326934",
"camp1" : {
"$cond" : [
{
"$not" : [
"$camp1"
]
},
"values",
"value2"
]
}
}
Can someone tell me how to fix it?
Thank you so much
You see the very important difference when priting the queries.
$cond is an aggregation pipeline operator. It is processed only when aggregation pipeline is used to update the data. When a simple (non-pipelined) update is used, the operator has no special meaning and this is exactly what you see in the output.
You indicate "pipeline update" by passing an array instead of simple object as update description in javascript API (and mongo console). In Scala/Java you have to use one of the updateMany overloads that takes update description as List, not Bson. I.e. you need something like
collectionA.updateMany(
Filters.eq("cdAccount", "ES3100810348150001326934"),
Collections.singletonList(updateDocument)
)

Mongodb Java query for date range

I needed to find all the records in mongo db within two date ranges using Mongo Driver[3.4.0] for Java.
Example:
I have books Collection.
{
"_id" : ObjectId("5acb40d27d63b61cb002bafe"),
"title" : "WingsOfFire",
"pub-date" : ISODate("2013-10-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-11-02T00:00:00.000Z")
}
Like above I have 100s of documents.
I need to find all records wherein pub-date > rel-date.
I am using Mongo DB version 3.2.6
I tried to use $expr operator but it seems to work with only Mongo 3.6+
Not able find cleaner solutions for above requirement.
Please clarify.
The MongoDB (prior to v3.4) shell command for your use case is:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$pub-date", "$rel-date" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
Translating this command into Java you'll get:
MongoClient mongoClient = ...;
MongoCollection<Document> collection = mongoClient.getDatabase("...").getCollection("...");
List<Document> documents = collection.aggregate(Arrays.asList(
new Document("$redact", new Document("$cond",
Arrays.asList(new Document("$gt", Arrays.asList("$pub-date", "$rel-date")), "$$KEEP", "$$PRUNE"))
))).into(new ArrayList<>());
for (Document document : documents) {
System.out.println(document.toJson());
}
Given a collection with these documents ...
{
"_id" : ObjectId("5acb40d27d63b61cb002bafe"),
"title" : "WingsOfFire",
"pub-date" : ISODate("2013-10-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-11-02T00:00:00.000Z")
}
{
"_id" : ObjectId("5acb662756539a6734e64e4a"),
"title" : "WingsOfSmoke",
"pub-date" : ISODate("2013-11-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-10-02T00:00:00.000Z")
}
.. the above Java code will print ...
{ "_id" : { "$oid" : "5acb662756539a6734e64e4a" }, "title" : "WingsOfSmoke", "pub-date" : { "$date" : 1383350400000 }, "rel-date" : { "$date" : 1380672000000 } }
... because this document's pub-date (2013-11-02T00:00:00.000Z) is after its rel-date (2013-10-02T00:00:00.000Z).
Note: the $where operator is functionally equivalent but use of that operator comes with some limitations:
$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt, $in).
In general, you should use $where only when you can’t express your query using another operator. If you must use $where, try to include at least one other standard query operator to filter the result set. Using $where alone requires a collection scan.
You might want to try $where-Operator:
db.books.find({ "$where": "this.pub-date > this.rel-date"});

Spring Data Mongo Template - Counting an array

Mongo document:
{
"_id" : "1",
"array" : [
{
"item" : "item"
},
{
"item" : "item"
}
]
}
My mongo shell query looks like so:
db.getCollection('collectionName').aggregate(
{$match: { _id: "1"}},
{$project: { count: { $size:"$array" }}}
)
Is there anyway to implement this using the Mongo Template from Spring?
So far I have this:
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
ProjectionOperation project = new ProjectionOperation();
Aggregation aggregate = Aggregation.newAggregation(match, project);
mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
I think I am only missing the project logic but I'm not sure if it is possible to do $size or equivalent here.
It's quite possible, the $size operator is supported (see DATAMONGO-979 and its implementation here). Your implementation could follow this example:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = new Aggregation(
match(where("_id").is("1")), //
project() //
.and("array") //
.size() //
.as("count")
);
AggregationResults<IntegerCount> results = mongoTemplate.aggregate(
agg, collectionName, Integer.class
);
List<IntegerCount> intCount = results.getMappedResults();
Please find below the sample code. You can change it accordingly for your requirement with collection name, collection name class and array field name.
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
Aggregation aggregate = Aggregation.newAggregation(match, Aggregation.project().and("array").project("size").as("count"));
AggregationResults<CollectionNameClass> aggregateResult = mongoOperations.aggregate(aggregate, "collectionName", <CollectionNameClass>.class);
if (aggregateResult!=null) {
//You can find the "count" as an attrribute inside "result" key
System.out.println("Output ====>" + aggregateResult.getRawResults().get("result"));
System.out.println("Output ====>" + aggregateResult.getRawResults().toMap());
}
Sample output:-
Output ====>[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}]
Output ====>{serverUsed=127.0.0.1:27017, waitedMS=0, result=[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}], ok=1.0}
You can write query as
Aggregation aggregate = Aggregation.newAggregation(Aggregation.match(Criteria.where("_id").is(1)),
Aggregation.project().and("array").size().as("count")); mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
It will execute the following query { "aggregate" : "collectionName" , "pipeline" : [ { "$match" : { "_id" : 1}} , { "$project" : { "count" : { "$size" : [ "$array"]}}}]}

Spring Data MongoDB: How do I represent $eq in project aggregation query?

I'm trying to convert this javascript code into java code to be used in Spring Data:
proj3={"$project": {
"comms" : 1,
"same" : { "$eq" : ["$comms.i" , "$max"]},
"max" : 1,
"_id" : 1
}
};
I cannot seem to figure it out.
I have tried this:
BasicDBObject o3 = new BasicDBObject();
o3.append("$eq", "[\"$comms.i\",\"$max\"]");
Aggregation aggCount2 = newAggregation(project("comms", "max", "_id").andExpression("same", o3));
logger.info(aggCount2.toString());
This is what is logged:
{ "$project" : { "comms" : 1 , "max" : 1 , "_id" : 1}}
I also read this thread: Spring Data MongoDB - $eq within $project support but the poster seemed to have given up and used the executeCommand option instead which is not the route I would like to go.
How can I get this code to work in java Spring Data Mongodb?
This is how I solved it, it might not be the most efficient way but it seem to work without too much code rewrite.
First, I looked at the answer in this thread:
Aggregation Project Group By extracted day, month and year with Spring Data
Blakes Seven proposed that I use a special custom aggregationOperation class so that the aggregation code will take BasicDBObjects:
public class CustomGroupOperation implements AggregationOperation {
private DBObject operation;
public CustomGroupOperation (DBObject operation) {
this.operation = operation;
}
#Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
Next, you just format the project code you want into the BasicDBObject:
BasicDBList basicDBList = new BasicDBList();
basicDBList.add("$comms.i");
basicDBList.add("$max");
Aggregation aggCount2 = newAggregation(
match(),
project(),
group(),
new CustomGroupOperation(new BasicDBObject("$project",
new BasicDBObject("comms", 1)
.append("max", 1)
.append("_id", 1)
.append("same", new BasicDBObject("$eq", basicDBList)))),
match(),
project(),
group(),
sort());
Print it out in the logger and you will see that the format for the javascript code is now correct.
{ "$project" : { "comms" : 1 , "max" : 1 , "_id" : 1 , "same" : { "$eq" : [ "$comms.i" , "$max"]}}}

Create aggregation in mongodb with spring

I have aggregation that works in mongo and i need to create the exact one in java with spring. I didn't find a way. Do you know if there is one?
db.collection_name.aggregate([
{
$group: {
_id : {
year : {$year : "$receivedDate" },
month : {$month: "$receivedDate"},
day : { $dayOfMonth : "$receivedDate"}
},
count : { $sum: 1 }
}
}
])
You could try projecting the fields first by using the SpEL andExpression in the projection operation and then group by the new fields in the group operation:
Aggregation agg = newAggregation(
project()
.andExpression("year(receivedDate)").as("year")
.andExpression("month(receivedDate)").as("month")
.andExpression("dayOfMonth(receivedDate)").as("day"),
group(fields().and("year").and("month").and("day"))
.count().as("count")
);

Categories