I am using an aggregate query in mongodb to find the sum of an attribute in all the documents present in a collection.
Query:
db.conversation.aggregate( [
{
$match:{
$and:[{"mailBoxId":"1","isHidden":false}]
}
},
{
$group:
{
_id: {"mailBoxId":"$mailBoxId","isHidden":"$isHidden"} ,
messageCount: { $sum:"$messageCount" }
}
}
]);
The result returned by Mongodb is fine and is in this format.
{
"result" : [
{
"_id" : {
"mailBoxId" : "2",
"isHidden" : false
},
"messageCount" : 2
}
],
"ok" : 1
}
I just want the messageCount field. I am using MongoTemplate(Spring) class to query the database.
Query retrievalQuery = new Query();
retrievalQuery.addCriteria(Criteria.where("mailBoxId").is(userId).and("isHidden").is(false));
return mongoTemplate.find(retrievalQuery, );
I am confused how to store the resultant object returned by Mongodb and extract a specific field from it.
Pls help.
The way you are trying to use aggregate in mongoTemplate is wrong . Try this i am sure it will help.
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(
Criteria.where("mailBoxId").is("1").and("isHidden").is(false)),
Aggregation.group("$mailBoxId").sum("$unReadMessagesCount").as("unReadMessagesCount")
);
System.out.println("Query ==>>["+agg.toString()+"]");
AggregationResults<AggResultObj> data = mongoTemplate.aggregate(agg, "collectionName", AggResultObj.class);
System.out.println("UnReadMesasgeCode :"+data.getUniqueMappedResult().getUnReadMessagesCount());
The AggResultObj will be looks like
public class AggResultObj{
String _id;
int unReadMessagesCount;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public int getUnReadMessagesCount() {
return unReadMessagesCount;
}
public void setUnReadMessagesCount(int unReadMessagesCount) {
this.unReadMessagesCount = unReadMessagesCount;
}
}
For more information you can see my blog where i have created a example for the same for your scenario. please click https://satishkumardangi.blogspot.in/2016/09/using-mongo-db-aggregation-with-spring.html
Try this
Query retrievalQuery = new Query();
retrievalQuery.addCriteria(Criteria.where("mailBoxId").is(userId).and("isHidden").is(false));
var result = mongoTemplate.find(retrievalQuery);
var final = result[1].messageCount;
return final;
Related
How would I convert the following MongoDB query into a query to be used by my Java Spring application? I can't find a way to use pipeline with the provided lookup method.
Here is the query I am attempting to convert. I also want to note that I didn't use $unwind as I wanted the deliveryZipCodeTimings to stay as a grouped collection in the return object.
db.getCollection('fulfillmentChannel').aggregate([
{
$match: {
"dayOfWeek": "SOME_VARIABLE_STRING_1"
}
},
{
$lookup: {
from: "deliveryZipCodeTiming",
let: { location_id: "$fulfillmentLocationId" },
pipeline: [{
$match: {
$expr: {
$and: [
{$eq: ["$fulfillmentLocationId", "$$location_id"]},
{$eq: ["$zipCode", "SOME_VARIABLE_STRING_2"]}
]
}
}
},
{
$project: { _id: 0, zipCode: 1, cutoffTime: 1 }
}],
as: "deliveryZipCodeTimings"
}
},
{
$match: {
"deliveryZipCodeTimings": {$ne: []}
}
}
])
Building upon the info given by #dnickless, I was able to solve this. I'll post the complete solution in the hopes it helps someone else in the future.
I'm using mongodb-driver:3.6.4
First, I had to create a custom aggregation operation class so that I could pass in a custom JSON mongodb query to be used in the aggregation operation. This will allow me to use pipeline within a $lookup which is not supported with the driver version I am using.
public class CustomProjectAggregationOperation implements AggregationOperation {
private String jsonOperation;
public CustomProjectAggregationOperation(String jsonOperation) {
this.jsonOperation = jsonOperation;
}
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return aggregationOperationContext.getMappedObject(Document.parse(jsonOperation));
}
}
Now that we have the ability to pass a custom JSON query into our mongodb spring implementation, all that is left is to plug those values into a TypedAggregation query.
public List<FulfillmentChannel> getFulfillmentChannels(
String SOME_VARIABLE_STRING_1,
String SOME_VARIABLE_STRING_2) {
AggregationOperation match = Aggregation.match(
Criteria.where("dayOfWeek").is(SOME_VARIABLE_STRING_1));
AggregationOperation match2 = Aggregation.match(
Criteria.where("deliveryZipCodeTimings").ne(Collections.EMPTY_LIST));
String query =
"{ $lookup: { " +
"from: 'deliveryZipCodeTiming'," +
"let: { location_id: '$fulfillmentLocationId' }," +
"pipeline: [{" +
"$match: {$expr: {$and: [" +
"{ $eq: ['$fulfillmentLocationId', '$$location_id']}," +
"{ $eq: ['$zipCode', '" + SOME_VARIABLE_STRING_2 + "']}]}}}," +
"{ $project: { _id: 0, zipCode: 1, cutoffTime: 1 } }]," +
"as: 'deliveryZipCodeTimings'}}";
TypedAggregation<FulfillmentChannel> aggregation = Aggregation.newAggregation(
FulfillmentChannel.class,
match,
new CustomProjectAggregationOperation(query),
match2
);
AggregationResults<FulfillmentChannel> results =
mongoTemplate.aggregate(aggregation, FulfillmentChannel.class);
return results.getMappedResults();
}
I would like to add this my solution which is repeating in some aspect the solutions posted before.
Mongo driver v3.x
For Mongo driver v3.x I came to the following solution:
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.util.JSON;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
public class JsonOperation implements AggregationOperation {
private List<Document> documents;
public JsonOperation(String json) {
Object root = JSON.parse(json);
documents = root instanceof BasicDBObject
? Collections.singletonList(new Document(((BasicDBObject) root).toMap()))
: ((BasicDBList) root).stream().map(item -> new Document((Map<String, Object>) ((BasicDBObject) item).toMap())).collect(Collectors.toList());
}
#Override
public Document toDocument(AggregationOperationContext context) {
// Not necessary to return anything as we override toPipelineStages():
return null;
}
#Override
public List<Document> toPipelineStages(AggregationOperationContext context) {
return documents;
}
}
and then provided that aggregation steps are given in some resource aggregations.json:
[
{
$match: {
"userId": "..."
}
},
{
$lookup: {
let: {
...
},
from: "another_collection",
pipeline: [
...
],
as: "things"
}
},
{
$sort: {
"date": 1
}
}
]
one can use above class as follows:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
Collection<ResultDao> results = mongoTemplate.aggregate(newAggregation(new JsonOperation(resourceToString("aggregations.json", StandardCharsets.UTF_8))), "some_collection", ResultDao.class).getMappedResults();
Mongo driver v4.x
As JSON class was removed from Mongo v4, I have rewritten the class as follows:
import java.util.Collections;
import java.util.List;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
public class JsonOperation implements AggregationOperation {
private List<Document> documents;
private static final String DUMMY_KEY = "dummy";
public JsonOperation(String json) {
documents = parseJson(json);
}
static final List<Document> parseJson(String json) {
return (json.startsWith("["))
? Document.parse("{\"" + DUMMY_KEY + "\": " + json + "}").getList(DUMMY_KEY, Document.class)
: Collections.singletonList(Document.parse(json));
}
#Override
public Document toDocument(AggregationOperationContext context) {
// Not necessary to return anything as we override toPipelineStages():
return null;
}
#Override
public List<Document> toPipelineStages(AggregationOperationContext context) {
return documents;
}
#Override
public String getOperator() {
return documents.iterator().next().keySet().iterator().next();
}
}
but implementation is now a bit ugly because of string manipulations. If somebody has a better idea of how to parse array of objects in a more elegant way, please edit this post or drop a comment. Ideally there should be some method in Mongo core that allows to parse either JSON object or list (returns BasicDBObject/BasicDBList or Document/List<Document>).
Also note that I have skipped the step of transforming Document instances in toPipelineStages() method as it is not necessary in my case:
#Override
public List<Document> toPipelineStages(AggregationOperationContext context) {
return documents.stream().map(document -> context.getMappedObject(document)).collect(Collectors.toList());
}
The drivers are pretty much always a little bit behind the current language features that MongoDB provides - hence some of the latest and greatest features are simply not nicely accessible through the API yet. I am afraid this is one of those cases and you'll need to resort to using strings. Kind of like so (untested):
AggregationOperation match = Aggregation.match(Criteria.where("dayOfWeek").is("SOME_VARIABLE_STRING_1"));
AggregationOperation match2 = Aggregation.match(Criteria.where("deliveryZipCodeTimings").ne([]));
String query = "{ $lookup: { from: 'deliveryZipCodeTiming', let: { location_id: '$fulfillmentLocationId' }, pipeline: [{ $match: { $expr: { $and: [ { $eq: ['$fulfillmentLocationId', '$$location_id']}, { $eq: ['$zipCode', 'SOME_VARIABLE_STRING_2']} ]} } }, { $project: { _id: 0, zipCode: 1, cutoffTime: 1 } }], as: 'deliveryZipCodeTimings' } }";
Aggregation.newAggregation(match, (DBObject) JSON.parse(query), match2);
I faced some JSON parsing exceptions when I used the way explained in the accepted answer, so I dig deep the default MongoDB java driver(version 3) Document class to build up aggregation query and found out any aggregation query can be build u as follows,
Replace each of the element in the mongo console query as follows
Curly braces({) -> new Document()
parameter names are same
Colon(:) -> Coma(,)
Coma(,) -> .append()
Square bracket([) -> Arrays.asList()
AggregationOperation customLookupOperation = new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext context) {
return new Document(
"$lookup",
new Document("from", "deliveryZipCodeTiming")
.append("let",new Document("location_id", "$fulfillmentLocationId"))
.append("pipeline", Arrays.<Object> asList(
new Document("$match", new Document("$expr", new Document("$and",
Arrays.<Object>asList(
new Document("$eq", Arrays.<Object>asList("$fulfillmentLocationId", "$$location_id")),
new Document("$eq", Arrays.<Object>asList("$zipCode", "SOME_VARIABLE_STRING_2"))
)))),
new Document("$project", new Document("_id",0).append("zipCode", 1)
.append("cutoffTime", 1)
)
))
.append("as", "deliveryZipCodeTimings")
);
}
};
Finally you can use the aggregation operation in the aggrgation pipeline,
Aggregation aggregation = Aggregation.newAggregation(matchOperation,customLookupOperation,matchOperation2);
For anyone who finds a simple solution and don't want to bother with raw json queries here is wrapper:
#RequiredArgsConstructor
public class PipelineLookUpWrapper implements AggregationOperation {
private final LookupOperation lookup;
private final Aggregation pipelineAggregation;
#Override
public Document toDocument(AggregationOperationContext context) {
return lookup.toDocument(context);
}
#Override
public String getOperator() {
return lookup.getOperator();
}
#Override
public List<Document> toPipelineStages(AggregationOperationContext context) {
List<Document> lookUpPipelineStages = lookup.toPipelineStages(context);
Document lookUp = (Document) lookUpPipelineStages.iterator().next().get(getOperator());
lookUp.append("pipeline", pipelineAggregation.getPipeline().getOperations()
.stream()
.flatMap(operation -> operation.toPipelineStages(context).stream())
.toList());
return lookUpPipelineStages;
}
}
Usage:
var originalLookUp = Aggregation.lookup("from", "localField", "foreignField", "as");
Aggregation pipelineAggregation = Aggregation.newAggregation(Aggregation.match(new Criteria()), Aggregation.skip(1));
AggregationOperation lookUpWithPipeline = new PipelineLookUpWrapper(originalLookUp, pipelineAggregation);
I'm kind of new to the MongoDB Java driver and I was wondering how you could execute a query stored as a string. Is this the best way to execute them, or what would be a better approach?
I've stumbled across the piece of the below on another stackoverflow thread, but haven't been able to get anything useful out of it. The output does not contain the result of the query at all.
The code I'm running right now:
#Test
public void testExecuteStoredQueries() {
String code = "db.getCollection('users').find({})";
final BasicDBObject command = new BasicDBObject();
String formattedCode = String.format("function() { return %s ; }", code);
System.out.println("Formatted code:");
System.out.println(formattedCode);
command.put("eval", formattedCode);
Document result = DbEngine.getInstance().getDatabase().runCommand(command);
System.out.println(result.toJson());
}
Summarized output:
{
"retval": {
"_mongo": "....",
"_db": "...",
"_collection": "...",
"_ns": "cezy.users",
"_query": {},
"_fields": null,
"_limit": 0,
"_skip": 0,
"_batchSize": 0,
"_options": 0,
"_cursor": null,
"_numReturned": 0,
"_special": false
},
"ok": 1
}
I use morphia when i have to deal with objects. As when you retrieve the data from MongoDb, for the long values you get extended Json instead of Json Response. Parsing Extended Json could be a trouble and might break the code. As Gson doesn't support the conversion from Extended Json to Json.
private void createDatastore(boolean createIndexes) {
Morphia morphia = new Morphia();
morphia.map(classname.class);
datastore = morphia.createDatastore(mongoClient, databaseName);
if (createIndexes) {
datastore.ensureIndexes();
}
}
#Override
public Datastore getDatastore() {
return this.datastore;
}
#Test
public void testExecuteStoredQueries() {
String code = "db.getCollection('users').find({})";
String formattedCode = String.format("function() { return %s ; }", code);
final BasicDBObject basicObject = new BasicDBObject(new BasicDBObject("$in", formattedCode));
Query<ClassName> query = getDatastore().createQuery(<Classname>.class).filter("_eval", basicObject);
List<Classname> List = query.asList();
//if you want to access each object and perform some task
List.forEach((cursor) -> {
//perform your task
});
}
Removing the function creation and adding ".toArray()" pretty much solved the problem.
#Test
public void testExecuteStoredQueries() {
String code = "db.users.find({}).toArray();";
final BasicDBObject command = new BasicDBObject();
command.put("eval", code);
Document result = DbEngine.getInstance().getDatabase().runCommand(command);
System.out.println(result.toJson());
assertNotNull(result.get("retval"));
}
The array is in the "retval" field of the response.
I am working on a JHipster project with an AngularJS front-end and a Java back-end. I am using Spring data with the MongoDb database.
I did a grouping operation on the field budgetCode. So, for each budgetCode, I succeded to have the list of all the linked taskCodes.
Here, the method aggregateAllTaskCodes which does the grouping operation:
Repository layer
public class ClarityResourceAffectationRepositoryImpl implements ClarityResourceAffectationRepositoryCustom {
#Override
public List<ClarityResourceAffectationReport> aggregateAllTaskCodes() {
Aggregation aggregation = newAggregation(
group("budgetCode").addToSet("budgetCode").as("budgetCode").addToSet("taskCode").as("taskCode"),
sort(Sort.Direction.ASC, previousOperation(),"budgetCode"));
AggregationResults groupResults = mongoTemplate.aggregate(aggregation, ClarityResourceAffectation.class,
ClarityResourceAffectationReport.class);
List<ClarityResourceAffectationReport> clarityResourceAffectationReports = groupResults.getMappedResults();
return clarityResourceAffectationReports;
}
}
Service layer
public class ClarityResourceAffectationServiceImpl implements ClarityResourceAffectationService{
#Override
public List<ClarityResourceAffectationReport> aggregateAllTaskCodes() {
log.debug("Request to aggregateByCodeBudgetForCodeTache : {}");
List<ClarityResourceAffectationReport> result = clarityResourceAffectationRepository
.aggregateAllTaskCodes();
return result;
}
}
REST API layer
public class ClarityResourceAffectationResource {
#GetMapping("/clarity-resource-affectations/list-task-codes")
#Timed
public ResponseEntity<List<ClarityResourceAffectationReport>> aggregateTabAllTaskCodes() {
log.debug("REST request to get aggregateTabAllTaskCodes : {}");
List<ClarityResourceAffectationReport> result = clarityResourceAffectationService.aggregateAllTaskCodes();
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
ClarityResourceAffectation
#Document(collection = "clarity_resource_affectation")
public class ClarityResourceAffectation implements Serializable {
#Id
private String id;
#Field("budget_code")
private String budgetCode;
#Field("task_code")
private String taskCode;
public String getBudgetCode() {
return budgetCode;
}
public void setBudgetCode(String budgetCode) {
this.budgetCode = budgetCode;
}
public String getTaskCode() {
return taskCode;
}
public void setTaskCode(String taskCode) {
this.taskCode = taskCode;
}
}
ClarityResourceAffectationReport
public class ClarityResourceAffectationReport implements Serializable {
private static final long serialVersionUID = 1L;
private String budgetCode;
private String taskCode;
private String listTaskCode;
public String getBudgetCode() {
return budgetCode;
}
public void setBudgetCode(String budgetCode) {
this.budgetCode = budgetCode;
}
public String getTaskCode() {
return taskCode;
}
public void setTaskCode(String taskCode) {
this.taskCode = taskCode;
}
public String[] getListTaskCode() {
return listTaskCode;
}
public void setListTaskCode(String[] listTaskCode) {
this.listTaskCode = listTaskCode;
}
}
clarity-resource-affectation.service.js
(function() {
'use strict';
angular
.module('dashboardApp')
.factory('ClarityResourceAffectation', ClarityResourceAffectation);
ClarityResourceAffectation.$inject = ['$resource'];
function ClarityResourceAffectation ($resource) {
var resourceUrl = 'clarity/' + 'api/clarity-resource-affectations/:id';
return $resource(resourceUrl, {}, {
'query': { method: 'GET', isArray: true},
'aggregateAllTaskCodes': {
method: 'GET',
isArray: true,
url: 'clarity/api/clarity-resource-affectations/list-task-codes'
}
});
}
})();
When I call the function in the AngularJS front-end and I display that on a table, for each budgetCode, I have the list of the taskCodes in an array of one element. For example, for the budgetCode [ "P231P00"] I can have this list of taskCodes: [ "61985" , "43606" , "60671" , "43602"]
Well, I would like to have the list of the linked taskCodes, not in an array of one element but in an array of several elements like that:
[ ["61985"] , ["43606"] , ["60671"] , ["43602"] ]
What do I have to change in my code in order to do that?
Just for information, my javascript code which create the array based on the aggregate function:
clarity-resource-affectation-list-task-codes.controller.js
(function() {
'use strict';
angular
.module('dashboardApp')
.controller('ClarityResourceAffectationTableauBordNbCollaborateursController', ClarityResourceAffectationTableauBordNbCollaborateursController);
ClarityResourceAffectationTableauBordNbCollaborateursController.$inject = ['$timeout', '$scope', '$stateParams', 'DataUtils', 'ClarityResourceAffectation'];
function ClarityResourceAffectationTableauBordNbCollaborateursController ($timeout, $scope, $stateParams, DataUtils, ClarityResourceAffectation) {
var vm = this;
//Call of the function
allTaskCodes()
function allTaskCodes()
{
ClarityResourceAffectation.aggregateAllTaskCodes(function(readings) {
var dataAllTaskCodes;
dataAllTaskCodes = [];
alert(readings);
readings.forEach(function (item) {
dataAllTaskCodes.push({
label: item.budgetCode,
value: item.taskCode,
listvalue: item.listTaskCode
});
});
vm.dataAllTaskCodes = dataAllTaskCodes;
});
}
}
})();
Temporary solution:
Actually, I found a temporary solution by completing the method I created in the Service Layer:
#Override
public List<ClarityResourceAffectationReport> aggregateAllTaskCodes() {
log.debug("Request to aggregateAllTaskCodes : {}");
List<ClarityResourceAffectationReport> result = clarityResourceAffectationRepository
.aggregateAllTaskCodes();
Iterator<ClarityResourceAffectationReport> iterator = result.iterator();
while (iterator.hasNext())
{
ClarityResourceAffectationReport resAffectationReport = iterator.next();
String taskCodes = resAffectationReport.getTaskCode();
//Delete all exept letters, numbers and comma
taskCodes = taskCodes.replaceAll("[^a-zA-Z0-9,]","");
String[] listTaskCodes = taskCodes.split(",");
resAffectationReport.setListTaskCodes(listTaskCodes);
}
return result;
}
Also, I added an additional field to ClarityResourceAffectationReport which is listTaskCode. I updated the report class above. Finally, when I do an alert:
alert(readings[1].listvalue[0]), I have a result like 2630. So, I succeeded to have the first taskCode of a particular budgetCode.
I understood that what is important here is not the fact that as I told above for a budgetCode like [ "P231P00"], I must have a list like: [ "61985" , "43606" , "60671" , "43602"] or [ ["61985"] , ["43606"] , ["60671"] , ["43602"] ]. I just must have an array, not a string.
When I display alert(readings[1].listvalue), I have["2630","61297","61296","61299"] which is clearly an array because I can access each of the elements by calling alert(readings[1].listvalue[0]), alert(readings[1].listvalue[1]], etc...
I tried what you advised me
But, it is still not working. Here, my repository code:
#Override
public List<ClarityResourceAffectationReport> aggregateAllTaskCode() {
AggregationOperation project = new AggregationOperation() {
#Override
public DBObject toDBObject(AggregationOperationContext aggregationOperationContext) {
return new BasicDBObject("$project", new BasicDBObject("budgetCode", "$budget_code").append("taskCode", Arrays.asList("$task_code")));
}
};
Aggregation aggregation = newAggregation(project,
group("budgetCode").addToSet("budgetCode").as("budgetCode").addToSet("taskCode").as("taskCode"),
sort(Sort.Direction.ASC, previousOperation(),"budgetCode"));
AggregationResults groupResults = mongoTemplate.aggregate(aggregation, ClarityResourceAffectation.class,
ClarityResourceAffectationReport.class);
List<ClarityResourceAffectationReport> clarityResourceAffectationReports = groupResults.getMappedResults();
log.debug("clarityResourceAffectationReports.size() => " + clarityResourceAffectationReports.size());
log.debug("aggregation.toString() => " + aggregation.toString());
return clarityResourceAffectationReports;
}
Here, you can find the logs:
clarityResourceAffectationReports.size() => 1
aggregation.toString() => {"aggregate" : "__collection__" , "pipeline" : [ { "$project" : { "budgetCode" : "$budget_code" , "taskCode" : [ "$task_code"]}} , { "$group" : { "_id" : "$budgetCode" , "budgetCode" : { "$addToSet" : "$budgetCode"} , "taskCode" : { "$addToSet" : "$taskCode"}}} , { "$sort" : { "_id" : 1 , "budgetCode" : 1}}]}
Thanks in advance
You need to use $project to change the taskCodes value into array of single value before $group.
I don't see any hook in the api to address this.
You can use AggregationOperation to create $project stage using mongodb (BasicDBObject) types.
AggregationOperation project = new AggregationOperation() {
#Override
public DBObject toDBObject(AggregationOperationContext aggregationOperationContext) {
return new BasicDBObject("$project", new BasicDBObject("budgetCode", 1).append("taskCode", Arrays.asList("$taskCode")));
}
};
Something like
Aggregation aggregation = newAggregation(project,
group("budgetCode").addToSet("budgetCode").as("budgetCode").addToSet("taskCode").as("taskCode"),
sort(Sort.Direction.ASC, previousOperation(), "budgetCode"));
Using lambda
Aggregation aggregation = newAggregation(
aggregationOperationContext -> new BasicDBObject("$project", new BasicDBObject("budgetCode", 1).append("taskCode", Arrays.asList("$taskCode"))),
group("budgetCode").addToSet("budgetCode").as("budgetCode").addToSet("taskCode").as("taskCode"),
sort(Sort.Direction.ASC, previousOperation(), "budgetCode"));
I am using Spring Data with a Mongo DB embedded database and have the following document structure:
{
id : 111
messaage : abcd
commentsList: [
{
id : 123
text: test text
numberOfLikes : 5
numberOfDislikes: 2
}
]
}
I am trying to get a comment by id and update the numberOfLikes field by one and can't seem to get it to work, here is what I've tried and the java class structure:
public class Post {
private String id;
private String message;
private List<Comment> commentsList = new ArrayList<>();
...
}
public class Comment {
private String id;
private String text;
private int numberOfLikes;
private int numberOfDislikes;
...
}
Query query = new Query();
query.addCriteria(Criteria.where("commentsList._id").is("123"));
List<MongoComment> commentList = mongoTemplate.find(query, MongoComment.class);
Currently the returned list is always null.
Since you are trying to get a comment by id and update the numberOfLikes field by one, you essentially want to replicate this mongo shell update operation:
db.post.update(
{ "commentsList.id": "123" },
{
"$inc": { "commentsList.$.numberOfLikes": 1 }
}
)
The equivalent Spring Data MongoDB code follows:
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Update;
...
WriteResult wr = mongoTemplate.updateMulti(
new Query(where("commentsList.id").is("123")),
new Update().inc("commentsList.$.numberOfLikes", 1),
MongoPost.class
);
The id of the elements in the array "commentsList" is "id" (without the '_').
query.addCriteria(Criteria.where("commentsList.id").is("123"))
That should work.
The "_id" you're trying to query is the identifier Mongodb automatically generates for each document. Your document in the database looks like this:
{
_id: ObjectId("56b46e1d1d1353e38886dcc34f"),
id : 111,
messaage : "abcd",
commentsList: [
{
id : 123,
text: "test text",
numberOfLikes : 5,
numberOfDislikes: 2
}
]
}
I'm asking mongodb from my Spring data application using aggregation and when I get the result I need to cast it (or transform it) to my own object or type and I don't know how.
This is my code
AggregationResults aggregationResults = DBManager.getInstance().getMongoOperation().aggregate(aggregation, LevelEntity.class,Object.class);
I would like to have something like
AggregationResults<LevelList> aggregationResults = DBManager.getInstance().getMongoOperation().aggregate(aggregation, LevelEntity.class,LevelList.class);
and it gives me back this information
{
"_id" : {
"date" : "24/03/2015"
},
"levels" : [
{
"_id" : ObjectId("54f8627071fdac0ec132b4e5"),
"_class" : "org.xxxxxxxxxxx.persistence.model.impl.LevelEntity",
"user" : {
"_id" : ObjectId("54da19ce71fd56a173a3451a"),
...
...
...
},
"level" : 4,
"date" : ISODate("2015-03-24T14:04:32.830Z"),
"dateFormatted" : "24/03/2015"
},
{
"_id" : ObjectId("54f8627071fdac0ec132b4f4"),
"_class" : "org.xxxxxxxxxxx.persistence.model.impl.LevelEntity",
"user" : {
"_id" : ObjectId("54e34bd671fde9071569650c"),
...
...
...
},
"level" : 3,
"date" : ISODate("2015-03-24T14:04:32.866Z"),
"dateFormatted" : "24/03/2015"
}
]
}
Can you please help me!!???
Thanks a lot
Use as follow
TypedAggregation<LevelList> aggregation=Aggregation.newAggregation(LevelList.class,
match, sortOp, skipOp, limitOp);
AggregationResults<LevelList> data = mongoTemplate.aggregate(aggregation,
COLLECTION_NAME, LevelList.class);
well Cataclysm your response is not totally correct but gave me the key to solve my problem, this is what I had to do
TypedAggregation<LevelEntity> aggregation = Aggregation.newAggregation(LevelEntity.class,
userMatchOperation, dateMatchOperation, group(LevelEntity.DATE_FORMATTED_FIELD).push(ROOT).as(LevelByDate.ENTITY_NAME));
AggregationResults<LevelByDate> data = DBManager.getInstance().getMongoOperation().aggregate(aggregation, HappinessByDate.class);
And this is the LevelByDate object
#Document
public class LevelByDate {
public static final String ENTITY_NAME = "levelEntity";
#Id
private String id;
List<LevelEntity> levelEntity;
public LevelByDate() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<LevelEntity> getLevelEntity() {
return levelEntity;
}
public void setLevelEntity(List<LevelEntity> levelEntity) {
this.levelEntity = levelEntity;
}
}
Thanks a lot,
Kike.