How to use $facet , $addFields and $function in Spring-Data-MongoDB - mongodb

I'm developing a method in Spring-Data-MongoDB that use $facet,$addFields and $function but it return null.
This is the aggregation in MongoDB
db.utenti.aggregate([
{
$facet:
{
"aggregation": [
{
$match: { age: { $gt: 18 } }
},
{
$addFields:{
max: {
$function: {
body: function(name, scores) {
let max = Math.max(...scores);
return `Hello ${name}. Your max score is ${max}.`
},
args: [ "$name", "$scores"],
lang: "js"
}
}
}
}
]
}
}
]).pretty()
This the expected result
{
"aggregation" : [
{
"_id" : ObjectId("62c5f16b39b3d635ab6bf60d"),
"username" : "james34",
"name" : "james",
"role" : [
"user",
"specialuser"
],
"age" : 34,
"scores" : [
10,
9,
10
],
"_class" : "com.project.ecommercemongo.model.User",
"max" : "Hello james. Your max score is 10."
},
{
"_id" : ObjectId("62c5f1b839b3d635ab6bf60e"),
"username" : "elizabeth54",
"name" : "elizabeth",
"role" : [
"user",
"specialuser"
],
"age" : 54,
"scores" : [
10,
10,
10
],
"_class" : "com.project.ecommercemongo.model.User",
"max" : "Hello elizabeth. Your max score is 10."
},
{
"_id" : ObjectId("62c5f1f139b3d635ab6bf60f"),
"username" : "frank50",
"name" : "frank",
"role" : [
"user"
],
"age" : 50,
"scores" : [
10,
10,
10
],
"_class" : "com.project.ecommercemongo.model.User",
"max" : "Hello frank. Your max score is 10."
},
{
"_id" : ObjectId("62c5f27a39b3d635ab6bf610"),
"username" : "john26",
"name" : "john",
"role" : [
"user"
],
"age" : 26,
"scores" : [
8,
8,
10
],
"_class" : "com.project.ecommercemongo.model.User",
"max" : "Hello john. Your max score is 10."
}
]
}
This is the result I get
[
{
"_id": null,
"username": null,
"name": null,
"role": null,
"age": null,
"scores": null
}
]
this is the method that call facet addFields and function,but it don't add the field "max" and returns null
public List<User> aggregation1() {
List<Object> bl = new LinkedList<Object>();
bl.add("$name");
bl.add("$scores");
ScriptOperators.Function function= Function.function("function(name, scores) {let max = Math.max(...scores); return `Hello ${name}. Your max score is ${max}`}").args(bl).lang("js");//`Hello ${name}. Your max score is ${max}.`}");
System.out.println(function.toDocument());
FacetOperation facetOperation1 = Aggregation.facet(Aggregation.match(Criteria.where("age").gte(18)),Aggregation.addFields().addFieldWithValue("max", function).build()).as("aggregation");
Aggregation agg = Aggregation.newAggregation(facetOperation1);
return mongoTemplate.aggregate(agg, "utenti",User.class).getMappedResults();
}
And this is the User class
#Document(collection="utenti")
//#Sharded(shardKey = { "country", "userId" })
public class User {
#Id
private String _id;
private String username;
private String name;
private String[] role;
private Integer age;
private Integer[] scores;
public User() {
super();
}
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getRole() {
return role;
}
public void setRole(String[] role) {
this.role = role;
}
public Integer[] getScores() {
return scores;
}
public void setScores(Integer[] scores) {
this.scores = scores;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
how can i put all the users into the facet and add a field ? Thank you
Update
Now it work, I had to map the output with the aggregation fields
public List<MaxFacet> aggregation1() {
List<Object> bl = new LinkedList<Object>();
bl.add("$name");
bl.add("$scores");
ScriptOperators.Function function = Function.function(
"function(name, scores) {let max = Math.max(...scores); return `Hello ${name}. Your max score is ${max}.`}")
.args(bl).lang("js");// `Hello ${name}. Your max score is ${max}.`}");
System.out.println(function.toDocument());
FacetOperation facetOperation = Aggregation.facet().and(match(Criteria.where("age").gte(18)),
Aggregation.addFields().addFieldWithValue("max", function).build()).as("aggregation");
Aggregation agg = Aggregation.newAggregation(facetOperation);
return mongoOperations.aggregate(agg, mongoTemplate.getCollectionName(User.class), MaxFacet.class)
.getMappedResults();
}
public class MaxFacet {
private List<UserOut> aggregation;
public List<UserOut> getAggregation() {
return aggregation;
}
public void setAggregation(List<UserOut> facet1) {
this.aggregation = facet1;
}
public MaxFacet() {
// TODO Auto-generated constructor stub
}
}
#JsonPropertyOrder({"id","username","name","age","scores","max"})
public class UserOut {
#JsonProperty(value="id")
private String id;
private String username;
private String name;
private String []scores;
private String max;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getScores() {
return scores;
}
public void setScores(String[] scores) {
this.scores = scores;
}
public String getMax() {
return max;
}
public void setMax(String value) {
this.max= value;
}
}
[
{
"aggregation": [
{
"id": "62c5f16b39b3d635ab6bf60d",
"username": "james34",
"name": "james",
"scores": [
"10",
"9",
"10"
],
"max": "Hello james. Your max score is 10."
},
{
"id": "62c5f1b839b3d635ab6bf60e",
"username": "elizabeth54",
"name": "elizabeth",
"scores": [
"10",
"10",
"10"
],
"max": "Hello elizabeth. Your max score is 10."
},
{
"id": "62c5f1f139b3d635ab6bf60f",
"username": "frank50",
"name": "frank",
"scores": [
"10",
"10",
"10"
],
"max": "Hello frank. Your max score is 10."
},
{
"id": "62c5f27a39b3d635ab6bf610",
"username": "john26",
"name": "john",
"scores": [
"8",
"8",
"10"
],
"max": "Hello john. Your max score is 10."
}
]
}
]

Alternative soluition: You can simplify your code this way
1- Use inheritance mecanism for the UserOut class
public class UserOut extends User {
private String max;
public String getMax() {
return max;
}
public void setMax(String value) {
this.max= value;
}
}
2- For the complex pipelines, the custom AggregationOperation implementation is friendlier than Spring Data builder.
AggregationOperation facetOperation = ao ->
new Document("$facet",
new Document("aggregation", Arrays.asList(
new Document("$match", new Document("age", new Document("$gt", 18))),
new Document("$addFields",
new Document("max",
new Document("$function",
new Document("body", "function(name, scores) {"+
" let max = Math.max(...scores);"+
" return `Hello ${name}. Your max score is ${max}.`"+
"}")
.append("args", Arrays.asList("$name", "$scores"))
.append("lang", "js")
)
)
)
))
);
Aggregation agg = Aggregation.newAggregation(facetOperation);
return mongoOperations
.aggregate(agg, mongoTemplate.getCollectionName(User.class), MaxFacet.class)
.getMappedResults();
Alternatively, just parse your shell JSON query this way (triple quotes starting from Java 13):
Document.parse("""
{
"$facet":
{
"aggregation": [
{
"$match": { "age": { "$gt": 18 } }
},
{
"$addFields":{
"max": {
"$function": {
"body": function(name, scores) {
let max = Math.max(...scores);
return `Hello ${name}. Your max score is ${max}.`
},
"args": [ "$name", "$scores"],
"lang": "js"
}
}
}
}
]
}
}
""")

Related

How to use $accumulator in Spring-Data-MongoDb [Spring-Data-MongoDB][MongoDB]

I'm new in Spring-Data-MongoDB, I'm developing a method that performs MongoDB $accumulator operator in Spring-Data-MongoDB, but I don't know why my result on the $accumulator field return null.
Now I'm populating the collection in MongoDB:
db.game.insertMany([
{ "_id" : 1, "name" : "John", "score" : 10},
{ "_id" : 2, "name" : "Elizabeth", "score" : 10},
{ "_id" : 3, "name" : "James ", "score" : 9},
{ "_id" : 4, "name" : "John", "score" : 5},
{ "_id" : 5, "name" : "Elizabeth", "score" : 10},
{ "_id" : 6, "name" : "John", "score" : 10},
{ "_id" : 7, "name" : "Elizabeth", "score" : 10},
{ "_id" : 8, "name" : "James ", "score" : 8 },
{ "_id" : 9, "name" : "James ", "score" : 8}])
This is the accumulator aggregation in MongoDB Server:
db.game.aggregate ([
{
$group :
{
_id : "$name",
avgScore:
{
$accumulator:
{
init: function() {
return { count: 0, sum: 0 }
},
accumulate: function(state, score) {
return {
count: state.count + 1,
sum: state.sum + score
}
},
accumulateArgs: ["$score"],
merge: function(state1, state2) {
return {
count: state1.count + state2.count,
sum: state1.sum + state2.sum
}
},
finalize: function(state) {
return (state.sum / state.count)
},
lang: "js"
}
}
}
}
])
This is the result of MongoDB that I'm trying to perform via Spring-Data-MongoDB:
{ "_id" : "Elizabeth", "avgScore" : 10 }
{ "_id" : "John", "avgScore" : 8.333333333333334 }
{ "_id" : "James ", "avgScore" : 8.333333333333334 }
This is the result of My RestController:
[
{
"name": "Elizabeth",
"avgScore": null
},
{
"name": "James ",
"avgScore": null
},
{
"name": "John",
"avgScore": null
}
]
this is the model Class
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection="game")
public class Game {
#Id
private Integer _id;
private String name;
private Integer score;
public Game() {
super();
// TODO Auto-generated constructor stub
}
public int get_id() {
return _id;
}
public void set_id(Integer _id) {
this._id = _id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
}
This is the Service that performs the $accumulator operator on accumulator method, but it returns null:
import java.util.LinkedList;
import java.util.List;
import com.project.ecommercemongo.aggregation.ResultAccumulator;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationExpression;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorAccumulateArgsBuilder;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorFinalizeBuilder;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorInitArgsBuilder;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorInitBuilder;
import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorMergeBuilder;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import org.springframework.stereotype.Service;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import com.project.ecommercemongo.model.Game;
import com.project.ecommercemongo.repository.GameRepository;
#Service
public class GameService {
#Autowired
GameRepository game;
#Autowired
MongoTemplate mongoTemplate;
#Autowired
private MongoOperations mongoOperations;
public List<Game> findAll() {
List<Game> list = game.findAll();
return list;
}
public List<ResultAccumulator> accumulator() {
AccumulatorInitBuilder aib = ScriptOperators.accumulatorBuilder();
AccumulatorInitArgsBuilder aiab = aib.init("function() {return { count: 0, sum: 0 }}");
aib.lang("js");
AccumulatorAccumulateArgsBuilder aaac = aiab
.accumulate("function(state, score) { return {count: state.count + 1,sum: state.sum + score}}");
List<Object> bl = new LinkedList<Object>();
bl.add("score");
AccumulatorMergeBuilder amb = aaac.accumulateArgs(bl);
AccumulatorFinalizeBuilder afb = amb.merge(
"function(state1, state2) {return {count: state1.count + state2.count,sum: state1.sum + state2.sum}}");
// Accumulator a=afbb.build();
Accumulator a = afb.finalize("function(state) {return (state.sum / state.count)}");
Document document = a.toDocument();
/*AggregationExpression expression = new AggregationExpression() {
#Override
public Document toDocument(AggregationOperationContext context) {
Document document1 = document;
return document1;
}
};
Document document2 = expression.toDocument();*/
GroupOperation groupByNameAndAccumulator = group("name").last(String.valueOf(document)).as("avgScore");
Aggregation aggregation = newAggregation(groupByNameAndAccumulator);
List<ResultAccumulator> results = mongoOperations
.aggregate(aggregation, mongoTemplate.getCollectionName(Game.class), ResultAccumulator.class)
.getMappedResults();
// AggregationResults<ResultAccumulator> result =
// mongoTemplate.aggregate(aggregation, "game", ResultAccumulator.class);
System.out.println(document);
System.out.println(results.toString());
return results;
}
}
This is the server that print the Document containing the accumulator operator,
2022-07-04 00:42:28.696 INFO 13784 --- [nio-8080-exec-2] org.mongodb.driver.connection : Opened connection [connectionId{localValue:3, serverValue:1531}] to localhost:27118
Document{{$accumulator=Document{{init=function() {return { count: 0, sum: 0 }}, accumulate=function(state, score) { return {count: state.count + 1,sum: state.sum + score}}, accumulateArgs=[score], merge=function(state1, state2) {return {count: state1.count + state2.count,sum: state1.sum + state2.sum}}, lang=js, finalize=function(state) {return (state.sum / state.count)}}}}}
[com.project.mongo.aggregation.ResultAccumulator#37517d96, com.project.mongo.aggregation.ResultAccumulator#19e6b063, com.project.emongo.aggregation.ResultAccumulator#3d69a1a5]
this is the Result Class
import org.springframework.data.annotation.Id;
public class ResultAccumulator {
#Id
private String name;
private Double avgScore;
public ResultAccumulator() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getAvgScore() {
return avgScore;
}
public void setAvgScore(Double avgScore) {
this.avgScore = avgScore;
}
}
this is the Controller
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.project.ecommercemongo.aggregation.ResultAccumulator;
import com.project.ecommercemongo.model.Game;
import com.project.ecommercemongo.service.GameService;
#RestController
#RequestMapping("/game")
public class GameController {
#Autowired
GameService service;
#GetMapping("/find")
public List<Game> listAll() {
return service.findAll();
}
#GetMapping("/accumulator")
public List<ResultAccumulator> accumulator() {
return service.accumulator();
}
}
I don't know what I'm doing wrong, maybe the problem is in the Object Accumulator .
Bye and thank you in advance!
Update
Now it work my mistake was on the method last in group,now i have changed to accumulate(Accumulator)
AccumulatorInitBuilder aib = ScriptOperators.accumulatorBuilder();
AccumulatorInitArgsBuilder aiab = aib.init("function() {return { count: 0, sum: 0 }}");
aib.lang("js");
AccumulatorAccumulateArgsBuilder aaab = aiab
.accumulate("function(state, score) { return {count: state.count + 1,sum: state.sum + score}}");
List<Object> bl = new LinkedList<Object>();
bl.add("$score");
AccumulatorMergeBuilder amb = aaab.accumulateArgs(bl);
AccumulatorFinalizeBuilder afb = amb.merge(
"function(state1, state2) {return {count: state1.count + state2.count,sum: state1.sum + state2.sum}}");
// Accumulator a=afbb.build();
Accumulator a = afb.finalize("function(state) {return (state.sum / state.count)}");
GroupOperation groupByNameAndAccumulator = group("name").accumulate(a).as("avgScore");
Aggregation aggregation = newAggregation(groupByNameAndAccumulator);
List<ResultAccumulator> results = mongoOperations
.aggregate(aggregation, mongoTemplate.getCollectionName(Game.class), ResultAccumulator.class)
.getMappedResults();
// AggregationResults<ResultAccumulator> result =
// mongoTemplate.aggregate(aggregation, "game", ResultAccumulator.class);
return results;
public class ResultAccumulator {
#Id
private String name;
private Float avgScore;
public ResultAccumulator() {
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getAvgScore() {
return avgScore;
}
public void setAvgScore(float avgScore) {
this.avgScore = avgScore;
}
}
[
{
"name": "Elizabeth",
"avgScore": 10.0
},
{
"name": "James ",
"avgScore": 8.333333
},
{
"name": "John",
"avgScore": 8.333333
}
]

Drools rule returns value null (not firing?)

Iam new with drools. I created the following object:
package com.myspace.applicant;
public class Applicant implements java.io.Serializable {
static final long serialVersionUID = 1L;
private java.lang.Integer age;
private java.lang.Boolean approved;
private java.lang.Double money;
private java.lang.String name;
public Applicant() {
}
public java.lang.Integer getAge() {
return this.age;
}
public void setAge(java.lang.Integer age) {
this.age = age;
}
public java.lang.Boolean getApproved() {
return this.approved;
}
public void setApproved(java.lang.Boolean approved) {
this.approved = approved;
}
public java.lang.Double getMoney() {
return this.money;
}
public void setMoney(java.lang.Double money) {
this.money = money;
}
public java.lang.String getName() {
return this.name;
}
public void setName(java.lang.String name) {
this.name = name;
}
public Applicant(java.lang.Integer age, java.lang.Boolean approved,
java.lang.Double money, java.lang.String name) {
this.age = age;
this.approved = approved;
this.money = money;
this.name = name;
}
}
and a *.drl file which contains the rule:
package com.myspace.applicant;
import com.myspace.applicant.Applicant;
no-loop
rule "approve applicants"
when
$a: Applicant(age > 30, money > 1000, approved == false)
then
modify($a) {
setApproved(true);
}
end
In Postman I tried to call the rule with the following body:
{
"lookup" : null,
"commands" : [ {
"insert" : {
"objects" : {
"Applicant": {
"age": 28,
"approved": false,
"money": 10000,
"name": "boehlen"
}
},
"disconnected" : false,
"out-identifier": "Applicant",
"return-object" : true,
"entry-point" : "DEFAULT"
}
}, {
"fire-all-rules" : {}
} ]
}
I got the following answer:
{
"type" : "SUCCESS",
"msg" : "Container Applicant_1.0.0-SNAPSHOT successfully called.",
"result" : {
"execution-results" : {
"results" : [ {
"value" : null,
"key" : "Applicant"
} ],
"facts" : [ {
"value" : null,
"key" : "Applicant"
} ]
}
}
}
The problem is, that my value is null instead of the object I expect as a response. The server.log is empty and I do not see what is wrong. Please could you help me.
Thank you very much.
Your rule only triggers if the Applicant age is greater than 30. Your input value includes an age of 28.
I was able to solve the problem. It was a typo. Instead of "results" I had to write "result". Thanks a

Get nested list using spring boot from Mongodb

I'm trying to query MongoDB for Just idProfil and hashtags list from profileTwitter and bellow is the object:
{
"idProfil": "5ded2abae1692808b799b239",
"tweets": [
{
"idTweet": "5ded2dffe1692808b799b241",
"datePublication": "2019-12-08T16:58:59.702+0000",
"hashtags": [
{
"idHashtag": "5ded2c64e1692808b799b23c",
"label": "recipes "
},
{
"idHashtag": "5ded2c71e1692808b799b23d",
"label": "delicious "
},
{
"idHashtag": "5ded2c7ce1692808b799b23e",
"label": "foodrecipes"
},
{
"idHashtag": "5ded2c84e1692808b799b23f",
"label": "canada "
},
{
"idHashtag": "5ded2c8de1692808b799b240",
"label": "Usa"
},
{
"idHashtag": "5dee65d7e39e962d44a31c40",
"label": "food"
},
{
"idHashtag": "5dee65c8e39e962d44a31c3f",
"label": "cooking"
}
]
}
}
So my question is how to do this using spring boot ?
This example based on data you have provided. Your question is not clear. Can you please provide more details on it.
List<AggregationOperation> stages = new ArrayList<>();
ProjectOperation projectOperation = project("idProfil").and("$tweets.hashtags").as("hashtags");
stages.add(projectOperation);
AggregationResults<ResultDTO> result = mongoOperation.aggregate(newAggregation(stages),
"profileTwitter", ResultDTO.class);
public class ResultDTO {
private String idProfil;
private List<HashtagDTO> hashtags;
//getter setter
}
public class HashtagDTO {
private String idHashtag;
private String label;
//getter setter
}

Query results in nested DTO

Are nested DTOs in JPQL queries not allowed in Spring Data ? :
#Query("SELECT new test.customresult.CategoryCounter(c.name, "
+ "new test.customresult.CarResult(COUNT(e.category), e.category)"
+ "FROM error e JOIN e.car c "
+ "GROUP BY c.name,e.category")
List<CategoryCounter>countErrors();
Because, I get the following error message when trying to use the above mentioned JPQL query :
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: , near line 1,
I want to return such a JSON file from the JPQL query :
[{
"Car 1":
[{
"count": 1,
"category": "A"
}, {
"count": 2,
"category": "B"
}, {
"count": 0,
"category": "C"
}, {
"count": 0,
"category": "D"
}
]
}, {
"Car 2":
[{
"count": 0,
"category": "A"
}, {
"count": 0,
"category": "B"
}, {
"count": 4,
"category": "C"
}, {
"count": 5,
"category": "D"
}
]
}
]
There is a car table and an error table which contains the categories and a foreign key to the car table.
I wanted to use the nested DTO to represent the desired output :
The "wrapper" DTO
public class CategoryCounter {
private String name;
private CarResult carResult ;
public CategoryCounter (String name, CarResult carResult ) {
this.name= name;
this.carResult = carResult ;
}
public CarResult getCarResult () {
return carResult ;
}
public void setCarResult(CarResult carResult ) {
this.carResult = carResult ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
}
and the "nested" DTO :
public class CarResult {
private Long count;
private String category;
public CarResult (Long count, String category) {
this.count = count;
this.category= category;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category= category;
}
}
I'm not sure if this could be the problem... but you have your FROM clause starting immediately after the ) it says:
+ "new test.customresult.CarResult(COUNT(e.category), e.category)"
+ "FROM error e JOIN e.car c "
that is the same to: e.category)FROM so put a space in that part and try again...
+ "new test.customresult.CarResult(COUNT(e.category), e.category)"
+ " FROM error e JOIN e.car c "
the thing is that the error (Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException) is a syntax error

How to group Mongo collection using aggregation while I need other fileds also in result

I facing a situation where I need to query the result from a collection in MongoDB. I want to aggregate my collection based on one field using $group. But I want those remaining fields in my result, but without applying any aggregate functions on these field like ($first, $sum etc). These remaining fields should be in an Array.
Example: My collection:
-------------------------------------------------------------------------
| name | age | sex | province | city| area | address |
-------------------------------------------------------------------------
| A | 22 | m | Manglr | p1 | c1 | a1 |
| A | 22 | m | Kolkt | p2 | c2 | a2 |
| B | 24 | m | Mumb | p3 | c3 | a3 |
| B | 24 | m | Koch | p4 | c4 | a4 |
| B | 24 | m | Hydrbd | p5 | c5 | a5 |
-------------------------------------------------------------------------
Result I want: ($group by 'name' field only)
[
{
"name" : “A”,
"province" : [“Manglr", ‘Kolkt’]
"city" : [“p1”, ‘p2’],
"area" : [“c1”, ‘c2’],
"address" : [“a1”, ‘a2’],
},
{
"name" : “B”,
"province" : [“Mumb", ‘Koch’, 'Hydrbd']
"city" : [“p3”, ‘p4’,”p5”],
"area" : [“c3”, ‘c4’,”c5”],
"address" : [“a3”, ‘a4’,’a5’],
}
]
Please anyone help me to create a Mongo Query or Java code
You can try something like this. Group and push the other fields as needed.
aggregate([{
"$group": {
"_id": "$name",
"province": {
$push: {
"key": "$province"
}
},
"city": {
$push: {
"key": "$city"
}
},
"area": {
$push: {
"key": "$area"
}
},
"address": {
$push: {
"key": "$address"
}
}
}
}, {
"$project": {
"_id": 0,
"name": "$_id",
"province": "$province.key",
"city": "$city.key",
"area": "$area.key",
"address": "$address.key"
}
}])
Sample Output:
{ "province" : [ "Manglr", "kokat" ], "city" : [ "p1", "p2" ], "area" : [ "c1", "c2" ], "address" : [ "a1", "a2" ], "name" : "A" }
{ "province" : [ "Mumb", "Koch" ], "city" : [ "p3", "p4" ], "area" : [ "c3", "c4" ], "address" : [ "a3", "a4" ], "name" : "B" }
I do not know about MongoDB way but following is what you can do Java.
package com.grs.stackOverFlow.pack05;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
class UserAccumulated{
private String name;
private List<String> city;
private List<Integer> age;
private List<Character> sex;
public UserAccumulated(){
city=new ArrayList<>();
age=new ArrayList<>();
sex=new ArrayList<>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getCity() {
return city;
}
public void setCity(List<String> city) {
this.city = city;
}
public List<Integer> getAge() {
return age;
}
public void setAge(List<Integer> age) {
this.age = age;
}
public List<Character> getSex() {
return sex;
}
public void setSex(List<Character> sex) {
this.sex = sex;
}
public void addAge(Integer age2) {
age.add(age2);
}
public void addCity(String city2) {
city.add(city2);
}
public void addSex(Character sex2) {
sex.add(sex2);
}
public String toString(){
return String.format("{name:%s,cities : %s, sex: %s, age: %s}", name,city,sex,age);
}
}
public class User {
private String name,city;
private Integer age;
private Character sex;
public User(String name, String city, Integer age, Character sex) {
super();
this.name = name;
this.city = city;
this.age = age;
this.sex = sex;
}
public static void main(String...args){
//create a sample list ..you have to replace with code to retrieve data from mongo db
List<User> rows = Arrays.asList(new User("A", "Manglr", 22, 'm'),
new User("A", "Manglr", 22, 'm'),
new User("B", "addad", 22, 'm'),
new User("C", "addsadad", 22, 'm'),
new User("C", "sadd", 21, 'm'));
//aggregating
List<UserAccumulated> result=new ArrayList<>();
//parallestream if many records else use stream
Map<String, List<User>> map = rows.parallelStream().collect(Collectors.groupingBy(User::getName));
for(Entry<String, List<User>> entry: map.entrySet()){
UserAccumulated userA=new UserAccumulated();
userA.setName(entry.getKey());
for(User user : entry.getValue()){
userA.addAge(user.getAge());
userA.addCity(user.getCity());
userA.addSex(user.getSex());
}
result.add(userA);
}
for(UserAccumulated a: result)
System.out.println(a);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Character getSex() {
return sex;
}
public void setSex(Character sex) {
this.sex = sex;
}
}
Output looks like this:
{ name: A,cities : [Manglr, Manglr], sex: [m, m], age: [22, 22]} {name:B,cities : [addad], sex: [m], age: [22]} {name:C,cities : [addsadad, sadd], sex: [m, m], age: [22, 21]}
I did not take all your columns to keep it simple. I am not sure how quickly it runs with your actual data volume. But if performance issue let me know.