I am new bee to mongo. Below is the Aggregation operation I am doing in mongodb shell . But In my java ProjectionAggregation , am not able to give the $toObjectId. Please correct me what am I missing.
db shell query
db.getCollection('UserData').aggregate([
{
$project : {
"username" : "$username",
"beneficiaries" : "$beneficiaries"
}
},
{
$unwind : {
path : "$beneficiaries",
preserveNullAndEmptyArrays: true
}
},
{
$project : {
"username" : "$username",
"beneficiaries" : "$beneficiaries",
---- dont know how to give $toObjectId in java ProjectionOperation .
"beneficiaryStudId" : { $toObjectId : "$beneficiaries.studentId" }
}
},
{
$lookup:
{
from: "StudentProfileData",
localField: "beneficiaryStudId",
foreignField: "_id",
as: "studProfile"
}
}
])
Java code Projection Operation
ProjectionOperation projectUserAndBeneficiaries = Aggregation.project()
.andExpression("username").as("username")
.andExpression("beneficiaries").as("beneficiaries");
ProjectionOperation projectUserAndOtherDetails = Aggregation.project()
.andExpression("username").as("username")
.andExpression("beneficiaries").as("beneficiaries")
---- How to give $toObjectId in projection operation .andExpression("beneficiaries.studentId").as("beneficiaryStudId");
LookupOperation lookupOperation = LookupOperation.newLookup().
from("StudentProfileData").
localField("beneficiaryStudId").
foreignField("_id").
as("studProfile");
Aggregation agg = Aggregation.newAggregation(projectUserAndBeneficiaries, unwindBeneficiars,
projectUserAndOtherDetails
,lookupOperation);
AggregationResults<UserAndStudentData> output
= mongotemplate.aggregate(agg, "UserData", UserAndStudentData.class);
Sample Ouput
Output in db shell
{
"_id" : ObjectId("5d2f08574de2690001c281ac"),
"username" : "ks241#goo.com",
"beneficiaries" : {
"studentId" : "5d2f0e9c3bcf3e0001a7e562",
"mcBeneficiaryId" : "597418",
"enabled" : true
},
"beneficiaryStudId" : ObjectId("5d2f0e9c3bcf3e0001a7e562"),
"studProfile" : [
{
"_id" : ObjectId("5d2f0e9c3bcf3e0001a7e562"),
"lastName" : "Sharma",
"firstName" : "Kapil",
"studentRegisterCustomFieldValues" : [
{
"bcfdValue" : "One",
"bcfdName" : "Year"
}
],
"gender" : "M",
"merchantId" : "38788943"
}
]
}
where as in java
the studProfile array is always empty if I add $toObjectId the above java aggregation projection query and ran it produces the values as same as that db shell .
Spring data doesn't support to few type of methods. This problem may include into it. But we may use this solution.
Aggregation aggregation=newAggregation(
p-> new Document("$project",
new Document()
.append("username","$username"),
.append("beneficiaries","$beneficiaries)
.append("beneficiaryStudId",
new Document("$toObjectId","$beneficiaries.studentId")
)
)
)
There is a way to do this with Spring's ProjectionOperation and ConvertOperators classes:
ProjectionOperation projection = Aggregation.project()
.andInclude("username", "beneficiaries")
.and(ConvertOperators.ToObjectId.toObjectId("$beneficiaries.studentId"))
.as("beneficiaryStudId");
Related
I have the following mongodb document:
{
"_id" : ObjectId("5f283e7d39187d9ab77e7ece"),
"resourceType" : "VM",
"resourceInstanceName" : "virtual_machine_1",
"properties" :
{ "name" : "CentOS-VM", "cpu" : 2, "memory_in_gb" : 2, }
},
{
"_id" : ObjectId("5f28jh58hjf9ab77e7ece"),
"resourceType" : "VM",
"resourceInstanceName" : "virtual_machine_2",
"properties" :
{ "name" : "CentOS-VM", "cpu" : 8, "memory_in_gb" : 8, }
}
I use the following query in mongo shell which works fine
db.collection.aggregate({
$match:
{ "resourceType":"VM" }
}, {
$group: {
_id: '',
instance:
{ $sum: 1 }
,
cpu:
{ $sum: '$properties.cpu' }
,
memory_in_gb:
{ $sum: '$properties.memory_in_gb'}
}
})
and the output was
{ "_id" : "", "instance" : 2.0, "cpu" : 10, "memory_in_gb" : 10 }
using spring-data I have written the following code to produce the same result but it ends up in an Exception:
MatchOperation matchOperation = Aggregation.match(Criteria.where(RESOURCE_TYPE).is("VM"));
UnwindOperation unwindOperation = Aggregation.unwind("properties");
GroupOperation groupOperation = Aggregation.group(ID);
GroupOperation instanceOperation = Aggregation.group().count().as("instance");
GroupOperation cpuOperation = Aggregation.group("properties").sum("properties.cpu").as("cpu");
GroupOperation memoryOperation = Aggregation.group("properties").sum("properties.memory_in_gb").as("memory_in_gb");
Aggregation aggregation = Aggregation.newAggregation(matchOperation,unwindOperation,groupOperation,
instanceOperation, cpuOperation, memoryOperation);
return mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class).getMappedResults();
Here is the stackTrace:
[http-nio-9083-exec-2] ERROR c.s.c.c.v.service.ComputeService.getComputeSummary - Error in Cloud Account-summary :java.lang.IllegalArgumentException: Invalid reference 'properties'!
at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:114)
at org.springframework.data.mongodb.core.aggregation.ExposedFieldsAggregationOperationContext.getReference(ExposedFieldsAggregationOperationContext.java:77)
at org.springframework.data.mongodb.core.aggregation.AbstractAggregationExpression.unpack(AbstractAggregationExpression.java:74)
I am trying to read the document of nested list of another list of a document using mongoTemplate of Java code, I have tried from almost a day for the same but facing difficulty in doing that, kindly help me to sort it out.
Document in my database is:
{
"_id" : "DEPT5",
"subDepartmentList" : [
{
"subDepartmentId" : "SUBDEPT19",
"subDepartmentName" : "ABCD",
"labServiceList" : [
{
"_id" : "123abc",
"departmentId" : "DEPT5",
"subDepartmentId" : "SUBDEPT19",
"labServiceName" : "serviceOne"
},
{
"_id" : "123def",
"departmentId" : "DEPT5",
"subDepartmentId" : "SUBDEPT19",
"labServiceName" : "hello"
}
]
},
{
"subDepartmentId" : "SUBDEPT21",
"subDepartmentName" : "IJKL",
"labServiceList" : [
{
"_id" : "456abc",
"departmentId" : "DEPT5",
"subDepartmentId" : "SUBDEPT21"
"labServiceName" : "hello"
}
]
}
]
}
Query in my shell window is as follows:
db.labServiceMasters.aggregate([{$unwind: '$subDepartmentList'},
{$unwind: '$subDepartmentList.labServiceList'},
{$match: {'subDepartmentList.labServiceList._id': '123def'}},
{$project: {_id: 0, labService: '$subDepartmentList.labServiceList'}}
])
The above query works fine in my shell window. I tried to implement the same query using Java code below, but it did not work.
DBObject unwind1 = new BasicDBObject("$unwind" , "$subDepartmentList");
unwind1 = new BasicDBObject("$unwind" , "$subDepartmentList.labServiceList");
DBObject match = new BasicDBObject("$match", new BasicDBObject("_id", "123def"));
DBObject project = new BasicDBObject("$project", new BasicDBObject("_id", 0)
.append("subDepartmentList.labServiceList", 1));
AggregationOutput output=mongoTemplate.getCollection(MasterCollection).aggregate(unwind1, match, project);
System.out.println("OUTPUT: "+output);
for(DBObject result: output.results()){
System.out.println("INside for each loop of Results.");
System.out.println(result);
}
Please help me to solve the above query. Any suggestions would be appreciable. Thanks in advance.
In my Java Play framework application, I want to store the ArrayList values in mongoDB.
{
"_id" : ObjectId("5832f29bd4c6721e4e8ba4a7"),
"_class" : "com.netas.innovation.entity.Idea",
"title" : "fsaf",
"desc" : "adgg",
"keyWords" : "dgds",
"createdDate" : ISODate("2016-11-21T13:11:55.823Z"),
"checkbox1" : false,
"checkbox2" : false,
"checkbox3" : false,
"scopeOfIdea" : "Herkes",
"template" : false,
"creatorUser" : {
"$ref" : "user",
"$id" : ObjectId("5832f27dd4c6721e4e8ba4a5")
},
"owners" : [
{
"$ref" : "user",
"$id" : ObjectId("5832f27dd4c6721e4e8ba4a5")
}
],
"answer" : {
"$ref" : "answer",
"$id" : ObjectId("5832f29bd4c6721e4e8ba4a6")
},
"fileList" : []
}
i want to search in owners.
My query doesnt work
if(owners != null && !owners.isEmpty()) {
for(int i=0; i<owners.size(); i++) {
criteriaList.add(new **Criteria().elemMatch(Criteria.where("owners.$id").is(owners.get(i).getId())));**
}
}
How can i fix?
i can search by owners.
owners can be two people or three
Thanks for answers
"owners" : [
{
"$ref" : "user(list)",
"$id" : ObjectId("5832ecdb0deb78cc88392c83")
}
$ref : ozgurk,volkany ...
http://prntscr.com/ddz3c9 this example for output... title desc vs vsdate and owners
I'm not a mongodb expert but, I guess you should do somethink like:
List<Criteria> criteriaList = new ArrayList<Criteria>();
...
List idList = new ArrayList();
for (int i = 0; i < owners.size(); i++) {
idList.add(owners.get(i).getId());
}
criteriaList.add(new Criteria().where("owners.$id").in(idList));
...
If you add each owner=owners.get(i).getId() criteria to the list one by one and finally combine all criterias with AND operation you will not get your desired output.
I guess you use Spring data. I tried to write following mongodb query:
db.getCollection('idea').find(
{
"owners.$id": {
"$in" : [
ObjectId("58451c5f13c97bdde9950641"),
ObjectId("28451c5f13c97bdde9950642")
]
}
}
)
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"]}}}]}
Problem
Query is working fine but not limit and skip, it is fetching all records at once.
Kindly suggest what I am doing wrong.
MongoDB Collection
{
"_id" : ObjectId("559666c4e4b07a176940c94f"),
"postId" : "559542b1e4b0108c9b6f390e",
"user" : {
"userId" : "5596598ce4b07a176940c943",
"displayName" : "User1",
"username" : "user1",
"image" : ""
},
"postFor" : {
"type": "none",
"typeId" : ""
},
"actionType" : "like",
"isActive" : 1,
"createdDate" : ISODate("2015-07-03T10:41:07.575Z"),
"updatedDate" : ISODate("2015-07-03T10:41:07.575Z")
}
Java driver Query
Aggregation aggregation = newAggregation(
match(Criteria.where("isActive").is(1).and("user.userId").in(feedUsers)),
group("postId")
.last("postId").as("postId")
.last("postFor").as("postFor")
.last("actionType").as("actionType")
.last("isActive").as("isActive")
.last("user").as("user")
.last("createdDate").as("createdDate")
.last("updatedDate").as("updatedDate"),
sort(Sort.Direction.DESC, "createdDate")
);
aggregation.skip( skip );
aggregation.limit( limit );
AggregationResults<UserFeedAggregation> groupResults =
mongoOps.aggregate(aggregation, SocialActionsTrail.class, UserFeedAggregation.class);
return groupResults.getMappedResults();
Thanks
Aggregation pipelines are "sequential" in operation. These are not like .find() operations where .sort() .limit() .skip() are "modifiers" to the query operation:
Aggregation aggregation = newAggregation(
match(Criteria.where("isActive")
.is(1).and("user.userId").in(feedUsers)),
group("postId")
.last("postId").as("postId")
.last("postFor").as("postFor")
.last("actionType").as("actionType")
.last("isActive").as("isActive")
.last("user").as("user")
.last("createdDate").as("createdDate")
.last("updatedDate").as("updatedDate"),
sort(Sort.Direction.DESC, "createdDate"),
skip( skip ),
limit( limit )
);
Unless you define the operations in "sequence" then the pipeline does not know the order of execution. So define the pipeline as a whole.
A basic example:
Aggregation aggregation = newAggregation(
group("postId"),
skip(1),
limit(1)
);
System.out.println(aggregation)
Outputs a perfect pipeline:
{
"aggregate" : "__collection__" ,
"pipeline" : [
{ "$group" : { "_id" : "$postId" } },
{ "$skip" : 1 },
{ "$limit" : 1 }
]
}
See $skip and $limit in the core documentation.