I'm updating a 2014 Java project with the latest libraries and MongoDB Java Driver changed from 3.0 to 3.6. Most of the codes has been updated but there is a specific query pretty complicated which is giving me problems.
The Document is like this
{
"_id" : ObjectId("58750a67ae28bc28e0705b0f"),
"info": "description",
"parentId", "variable-id-here"
"issues": [
{"name": "a", "closed": true},
{"name": "b", "closed": false},
{"name": "c", "closed": true}
],
"bugs": [
{"name": "d", "closed": false},
{"name": "e", "closed": false},
{"name": "f", "closed": true}
],
"errors": [
{"name": "g", "closed": true},
{"name": "h", "closed": true},
{"name": "i", "closed": false}
]
}
(even if elements in arrays are similar, they can't be grouped in a single array in the document with an extra "type" key with values [issues, bugs, errors], but that's not the point)
The old script is this one
List<DBObject> aggregators = new ArrayList<>();
DBObject match = new BasicDBObject("$match", new BasicDBObject("parentId", myId));
DBObject project = new BasicDBObject();
List<String> domains = Arrays.asList("issues", "bugs", "errors");
for (Iterator<String> d = domains.iterator(); d.hasNext();) {
String domain = d.next();
//Reset values
aggregators = new ArrayList<>();
// Define MongoDB "$project" to find 'true' values on 'closed' flags
project = new BasicDBObject("$project", new BasicDBObject("closedProblems",
new BasicDBObject("$filter",
new BasicDBObject("input", "$"+domain)
.append("as", "myObject")
.append("cond", new BasicDBObject("$eq",
Arrays.<Object> asList("$$myObject.closed", true)
)
)
)
));
aggregators.add(match);
aggregators.add(project);
//db defined before. AggregationOutput is deprecated so must be changed
AggregationOutput output = db.getCollection("myColl").aggregate(aggregators);
// Now I can iterate results
for (DBObject result : output.results()) {
// ...
}
}
I tried to use projects, expressions, etc. but I can't find a way to duplicate the MongoDB project with new Aggregation methods.
The final result should use mongoTemplate.anyMethods to execute the Aggregation in order to join new project guidelines
Use below code in 3.6 driver.
DBCursor output = (DBCursor) db.getCollection("myColl").aggregate(aggregators, AggregationOptions.builder().build());
for (Iterator<DBObject> r = output.iterator(); output.hasNext();) {
DBObject result = r.next();
...
}
Update:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.filter;
import static org.springframework.data.mongodb.core.aggregation.ComparisonOperators.Eq.valueOf;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.aggregation.*;
List<AggregationOperation> aggregators;
MatchOperation match = Aggregation.match(Criteria.where("parentId").is(myId));
List<String> domains = Arrays.asList("issues", "bugs", "errors");
for (Iterator<String> d = domains.iterator(); d.hasNext();) {
String domain = d.next();
aggregators = new ArrayList<>();
ProjectionOperation project = project().and(filter(domain)
.as("myObject")
.by(valueOf(
"myObject.closed")
.equalToValue(
true)))
.as("closedProblems");
aggregators.add(match);
aggregators.add(project);
Aggregation aggregation = newAggregation(aggregators).withOptions(newAggregationOptions().cursor(new Document()).build());
AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, "myColl", Document.class );
for (Document result : results) {
// ...
}
}
Related
I have multiple document like below in a collection: (Below is part of document where I am applying query)
"failedTaskDetails": [
{
"status": "status1",
"taskType": "tasktype1",
"columnName": "column1",
"columnValue": "value1"
},
{
"status": "status2",
"taskType": "tasktype2",
"columnName": "column2",
"columnValue": "value2"
}
],
When i am applying group by to get count based on columnValue I am getting "value1,value1 = 1" in result instead of single value like "value1 =1".
Criteria criterias = new Criteria().andOperator(Criteria.where("fileName").is(fileName)
.and("failedTaskDetails.taskType").is(taskType)
.and("failedTaskDetails.columnName").is(columnName));
final Aggregation agg = newAggregation(
match(criterias),
group("failedTaskDetails.columnValue").count().as("count"),
project("count").and("columnValue").previousOperation());
final AggregationResults<ColumnNameWiseDetail> groupResults = mongoTemplate.aggregate(
agg, Product.class, ColumnNameWiseDetail.class);
return groupResults.getMappedResults();
Here is the code :
AggregateIterable<Document> result = chatLogCollection.aggregate(Arrays.asList(
new Document("$match", new Document("displayName", "user")),
new Document("$group", new Document("_id","$sessionGUID")
.append("time", new Document("$first","$ts"))
.append("makerID", new Document("$first","$makerID"))),
new Document("$sort", new Document("time", -1)),
new Document("$skip", skip),
new Document("$limit", limit)
));
This will generate the below type out put.
{
"displayName": "test test",
"_id": "123a54be-4b69-cd49-edb3-9b264fea077b",
"time": {
"$date": 1499759619016
},
"makerID": "xxxxx"
}
I need to format this to look like this:
{
"displayName": "test test",
"sessionID": "123a54be-4b69-cd49-edb3-9b264fea077b",
"time": {
"$date": 1499759619016
},
"makerID": "xxxxx"
}
That means i need to appear _id as sessionId. Please help me to do that.I am using mongoDB, java and windows 7.
I have Collection as following. I have query this collection by user.
{
"user": "username",
"sites": {
"site": "abc",
"keywords": [
{
"keyword": "keyword1",
"dailyranks": [
{
"fild1": "value1"
},
{
"fild2": "value2"
},
{
"fild3": "value3"
},
]
},
{
"keyword": "keyword2",
"dailyranks": [
{
"fild1": "value1"
},
{
"fild2": "value2"
},
{
"fild3": "value3"
},
]
},
],
}
}
I want to get the result from the collection as follows, I want to get the last elements of keyword array in the collection
[
{
"keyword" : "keyword1"
"fild2" : "value2",
"fild3" : "value3"
},
{
"keyword" : "keyword2"
"fild2" : "value2",
"fild3" : "value3"
},
]
I have aggregate using $project, but didn't work out. help me to sort this out.
Code used-
BasicDBObject siteObject = new BasicDBObject();
siteObject.append("keywords", "$sites.keywords.keyword");
siteObject.append("lastrank", "$sites.keywords.dailyranks");
BasicDBList aDBList = new BasicDBList();
aaa.add(new BasicDBObject("user", modelLogin.getUSER_NAME()));
ArrayList<BasicDBObject> doc = new ArrayList<>();
doc.add(new BasicDBObject().append("$unwind", "$sites"));
doc.add(new BasicDBObject("$match", aDBList));
doc.add(new BasicDBObject().append("$project", siteObject));
AggregationOutput output = coll.aggregate(doc);
You can try below aggregation.
Use $map to transform the Keywords array.
Within $map, use $arrayElemAt to project the last and second last values from dailyranks and $let operator to hold the result from $arrayAtElem and project the fild value.
db.coll.aggregate({
$project: {
keywords: {
$map: {
input: "$sites.keywords",
as: "result",
in: {
keyword: "$$result.keyword",
fild2: {$let: {vars: {obj: {$arrayElemAt: ["$$result.dailyranks", -2]}},in: "$$obj.fild2"}},
fild3: {$let: {vars: {obj: {$arrayElemAt: ["$$result.dailyranks", -1]}},in: "$$obj.fild3"}}
}
}
}
}
})
Java Equivalent
MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("db")
MongoCollection<Document> collection = db.getCollection("collection");
List<Document> results =
collection.aggregate(
Arrays.asList(
Aggregates.match(Filters.eq("user", modelLogin.getUSER_NAME() )),
Aggregates.project(
Projections.fields(
new Document("keywords",
new Document("$map",
new Document("input", "$sites.keywords").
append("as", "result").
append("in",
new Document("keyword","$$result.keyword").
append("fild2",
new Document("$let",
new Document("vars", new Document("obj",
new Document("$arrayElemAt", Arrays.asList("$$result.dailyranks", -2)))).
append("in", "$$obj.fild2" ))).
append("fild3",
new Document("$let",
new Document("vars", new Document("obj",
new Document("$arrayElemAt", Arrays.asList("$$result.dailyranks", -1)))).
append("in", "$$obj.fild3" )))
)
)
)))
)).into(new ArrayList<>());
I want to add a field to the returned subdocument like this:
{
"type": "product",
"code": "abc",
"team": "A1",
"modules": [{
"type": "module",
"code": "cde"
//Add a new field here
}]
}
I can add new field in document by Document.append() but can't add to the subdocument.
Here is my Java code:
BasicDBObject query = new BasicDBObject("code", "abc");
AggregateIterable<Document> findIterable = collection.aggregate(Arrays.asList(
new Document("$match", query),
new Document("$project", new Document("_id", 0).append("type", 1)
.append("code", 1)
.append("team", 1)
.append("modules", 1)
),
new Document("$sort", new Document("code", 1))
));
mongoCursor = findIterable.iterator();
while(mongoCursor.hasNext()){
Document document = mongoCursor.next();
}
Can someone give some help please? Thanks a lot in advance!!!
Okey, let's start. Imagine that we have the next mongo collection:
{
"city": "TWENTYNINE PALMS",
"loc": [-116.06041, 34.237969],
"pop": 11412,
"state": "CA",
"_id": "92278"
}
{
"city": "NEW CUYAMA",
"loc": [-74.823806, 34.996709],
"pop": 80,
"state": "CA",
"_id": "93254"
}
{
"city": "WATERBURY",
"loc": [-72.996268, 41.550328],
"pop": 25128,
"state": "CT",
"_id": "06705"
}
Notice that loc array is [latitude,longitude]
I would like to obtain using java mongo driver the "pop" average of the cities that have the altitude beetwen -75,-70.
So, using SQL I know that the query is:
SELECT avg(pop)
WHERE loc.altitude > -75 AND lloc.altitude < -70
I am very noob in mongodb, this is my current code:
BasicDBObject doc = new BasicDBObject("loc.0", new BasicDBObject("$gte",
-75).append("$lte", -70));
DBCursor cursor = collection.find(doc);
The previous code returns me all the documents that altitude are beetwen (-75,-70), but I do not know how to obtain the average,using mongo driver, I know that I can iterate over results using java..
Thank you
Use the aggregation framework with following aggregation pipeline (Mongo shell implementation):
db.collection.aggregate([
{
"$match": {
"loc.0": { "$gte": -75 },
"loc.1": { "$lte": 70 }
}
},
{
"$group": {
"_id": 0,
"average": {
"$avg": "$pop"
}
}
}
])
With the example above, this outputs to console:
/* 1 */
{
"result" : [
{
"_id" : 0,
"average" : 12604
}
],
"ok" : 1
}
With Java, this can be implemented as follows:
DBObject match = new BasicDBObject();
match.put("loc.0", new BasicDBObject("$gte", -75));
match.put("loc.1", new BasicDBObject("$lte", 70));
DBObject groupFields = new BasicDBObject( "_id", 0);
groupFields.put("average", new BasicDBObject( "$avg", "$pop"));
DBObject group = new BasicDBObject("$group", groupFields);
AggregationOutput output = collection.aggregate( match, group );