spring-data-mongo querying arrays example - spring-data

I am newbie to the spring-data-mongo. I've the following documents Food and I would like to query using Querying Arrys. I've following Food collection
> db.food.find()
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "kumquat", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
>
And I am using following query I get the proper data. Now same I would like to achieve using Spring-data-mongo
> db.food.find({fruit : {$all : ["apple","banana"]}})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
Please refer my development so far:
Food.java
package com.mkyong.model;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
#Document
public class Food {
#Id
private Double id;
#Field
private List<String> fruits;
public Double getId() {
return id;
}
public void setId(Double id) {
this.id = id;
}
public List<String> getFruits() {
return fruits;
}
public void setFruits(List<String> fruits) {
this.fruits = fruits;
}
#Override
public String toString() {
return "Food [id=" + id + ", fruits=" + fruits + "]";
}
}
FoodRepository:
package com.mkyong.reposiroty;
import org.springframework.data.repository.CrudRepository;
import com.mkyong.model.Food;
public interface FoodRepository extends CrudRepository<Food, String>{
}
SpringMongoConfig.java
package com.mkyong.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
#Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration {
#Override
public String getDatabaseName() {
return "test";
}
#Override
#Bean
public Mongo mongo() throws Exception {
return new MongoClient("127.0.0.1");
}
}
SpringConfig.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mongo:mongo host="127.0.0.1" port="27017" />
<mongo:db-factory dbname="test" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
</beans>
App.java
package com.mkyong.core;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import com.mkyong.config.SpringMongoConfig;
import com.mkyong.model.Food;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations mongoOperations = (MongoOperations) ctx.getBean("mongoTemplate");
Query q = new Query(Criteria.where("fruit").all("apple","banana"));
List<Food> foods = mongoOperations.find(q, Food.class);
for (Food food : foods) {
System.out.println("-------------------");
System.out.println("ID : "+ food.getId());
System.out.println("Fruits : "+ food.getFruits());
}
}
}
Result of main metthod, it doesn't pull anything. Fruits shows empty. Could any expert please help me to pull data from Fruits Array/List? Please let me know if you need any other information ?
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
-------------------
ID : 1.0
Fruits : null
-------------------
ID : 3.0
Fruits : null

Yes, I am able to resolved the error. The following changes required:
change data type of in Food class to private String[] fruit;
#Document
public class Food {
#Id
private Double id;
#Field
private String[] fruit;
public Double getId() {
return id;
}
public void setId(Double id) {
this.id = id;
}
public String[] getFruit() {
return fruit;
}
public void setFruit(String[] fruit) {
this.fruit = fruit;
}
}
and the following is the output:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Query : Query: { "fruit" : { "$all" : [ "apple" , "banana"]}}, Fields: null, Sort: null
-------------------
ID : 1.0
Fruits : apple,banana,peach
-------------------
ID : 3.0
Fruits : cherry,banana,apple

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
}
]

Get Embed Document based on DBRef in MongoDB using Spring data

I'm developing a spring boot application. I've 2 models that is
PersonEntity
#Data
#Document(collection = "products")
public class ProductEntity implements Serializable {
private static final long serialVersionUID = -8118791879505582652L;
#MongoId(FieldType.STRING)
private String id;
#Indexed
#Field("name")
private String name;
#Field("category")
#DBRef(lazy = true)
private CategoryEntity category;
// getters and setters
}
CategoryEntity
#Data
#Document(collection = "categories")
public class CategoryEntity implements Serializable {
private static final long serialVersionUID = -636356399519451958L;
#MongoId(value = FieldType.STRING)
private String id;
#Field("name")
private String name;
#Field("desc")
private String desc;
public CategoryEntity(String id) {
super();
this.id = id;
}
// getters and setters
}
when I'm saving data into DB then stored data is
Products Document
{
"_id": {
"$oid": "6141eeab92432109463ae8c4"
},
"name": "Adidas Running Shoes",
"category": {
"$ref": "categories",
"$id": "614049d91042cf40b5b9b304"
}
}
Categories Document
{
"_id": {
"$oid": "614049d91042cf40b5b9b304"
},
"name": "Shoes",
"desc": "Men's running Shoes",
}
But when I'm try to findAll() the ProductEntity the data by using Spring-Data-MongoDB the category will return null.
public List<ProductModel> getProducts() {
List<ProductEntity> entities = productRepo.findAll();
return entities.stream().map(entity -> {
ProductModel product = new ProductModel();
BeanUtils.copyProperties(entity, product);
if (Objects.nonNull(entity.getCategory())) {
BeanUtils.copyProperties(entity.getCategory(), product.getCategory());
}
return product;
}).collect(Collectors.toList());
}
and I need the output will be in below format.
{
"_id": {
"$oid": "6141eeab92432109463ae8c4"
},
"name": "Adidas Running Shoes",
"category": {
"_id": {
"$oid": "614049d91042cf40b5b9b304"
},
"name": "Shoes",
"desc": "Men's running Shoes",
}
}
Please let me know how to get desired output.
I've done a minor change and issue has been resolved. I've changed
from
#MongoId(value = FieldType.STRING)
private String id;
to
#MongoId(value = FieldType.OBJECT_ID)
private ObjectId id;
in the both entities and code works!

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

How to get data with referenced object in Spring data mongo?

This is City document class which i used.
#Document("city")
public class City {
#Id
private String id;
#Indexed(unique = true)
private String name;
#DBRef(lazy = true)
private District district;
public City() {
}
public City(String id) {
this.id = id;
}
public City(String id, String name, District district) {
this.id = id;
this.name = name;
this.district = district;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public District getDistrict() {
return district;
}
public void setDistrict(District district) {
this.district = district;
}
}
and this is the District document class
#Document("district")
public class District {
#Id
private String id;
#Indexed
private String name;
public District() {
}
public District(String id) {
this.id = id;
}
public District(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and this is the district collection data.
/* 1 */
{
"_id" : ObjectId("5d9482f3ff7ab743f0542070"),
"name" : "Ampara",
"_class" : "com.test.sample.model.District"
}
/* 2 */
{
"_id" : ObjectId("5d9482f3ff7ab743f0542071"),
"name" : "Anuradhapura",
"_class" : "com.test.sample.model.District"
}
and this is the city collection data.
/* 1 */
{
"_id" : ObjectId("5d948333ff7ab743f0542089"),
"name" : "Polgampola",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542070")
},
"_class" : "com.test.sample.model.City"
}
/* 2 */
{
"_id" : ObjectId("5d948333ff7ab743f054208a"),
"name" : "Porawagama",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542070")
},
"_class" : "com.test.sample.model.City"
}
/* 3 */
{
"_id" : ObjectId("5d948333ff7ab743f054208b"),
"name" : "Akkaraipattu",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542071")
},
"_class" : "com.test.sample.model.City"
}
this is the repository class for the above city document class
#Repository
public interface CityDao extends MongoRepository<City,String> {
List<City> findByDistrict(String id);
}
in the city class, i have used referenced document class called District. this a another document. I need to get the cities which belong to one district. findByDistrict(String id) method doesn't return values. Its just returning empty List. So How can i do this ?
I have tried with another alternative.As #pvpkirn said, You cannot query with #DBRef. But i tried with MongoOperations. With this we can pass the Filter objects as Json object. Using this i got the answer. This is what i tried.
public List<City> findByDistrictId(String districtId) {
final Bson filter = eq("district.$id", new ObjectId(districtId));
FindIterable<Document> documents = super.mongoOperations.getCollection("city").find(filter);
List<City> cities = new ArrayList<>();
for(Document document : documents){
City city = new City(document.getObjectId("_id").toString(),document.getString("name"),new District(districtId));
cities.add(city);
}
return cities;
}
in here i have used com.mongodb.client.model.Filters.eq to get the relevant bson object.

iterate objects in java 8 streams

I'd like to iterate the following mongo db collection in java8
{ "_id" : ObjectId("59d1dc99a1a79605342f15a7"), "_class" : "com.test.User", "vehicle" : [ { "name" : "aaa", "color" : "blue" } ], "count" : 1,"createdBy" : "Admin", "lastModifiedBy" : "Admin" }
Expected O/P fields:
_id,name,color,count,createdBy
I tried the below
queryvalue.stream().flatMap(vehicle->vehicle.getVehicles().stream()).collect(Collectors.toList());
queryvalue is the one where i'm pulling the data from spring mongo db repository. I'm new to java8 pls help.
Below are the classes:
Class User{
#CreatedDate
protected DateTime createdDate;
#LastModifiedDate
protected DateTime lastModifiedDate;
List<Vehicle> vehicle;
public void setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
}
public String getCreatedDate() {
return createdDate;
}
public List<Vehicle> getVehicles() {
return vehicle;
}
public void setVehicle(List<Vehicle> vehicle) {
this.vehicle= vehicle;
}
-- similarly for lastModifieddate and comments etc--
}
Class Vehicle{
private String name;
private string color;
public void setColor(String color){
this.color = color;
return this;
}
public String getColor(){
return color;
}
public void setName(String name){
this.name = name;
return this;
}
public void getName(){
return name;
}
}
And am pulling using this:
List<Vehicle>queryvalue = repository.findByVehicleName(vehiclename);