Query document in list - spring data mongodb - spring-data-mongodb

I have the following object:
{
"id" : "sampleId";
foos : [{
"prop1":"value1",
"prop2":"value2"
},
{
"prop1":"value3",
"prop2":"value4"
}
]}
How can I get foos, where prop2 is value4? I'm using Spring data mongodb.

If you use spring data mongodb repositories, it can make your life easy. You will need a domain class. e.g.
public class Foo {
String prop1;
String prop2;
}
public class MyClass {
String id;
List<Foo> foos;
}
public interface MyClassRepository extends MongoRepository<MyClass, String> {
List<MyClass> findByFoosProp2(String prop2ValueToLookFor);
// assuming you can find multiple matches
}
Then simply call this method from your code
public clsss SomeBusinessClass {
#Autowired
private MyClassRepository repository;
public void mySomeBusinessMethod() {
List<MyClass> myObjects = repository.findByFoosProp2("value4");
}
}

This code will return a SampleBean with id sampleId wich will have only matching items in collection foos.
// Match one document with sampleId
Query q1 = new Query(where("id").is("sampleId"));
// match only foos with prop2 value value2
Query q2 = query(where("foos").elemMatch(where("prop2").is("value2))
BasicQuery query = new BasicQuery(q1.getQueryObject(), q2.getQueryObject());
SampleBean result = mongoTemplate.findOne(query, SampleBean.class)

Related

Reactive Panache relation JsonMappingException: Collection cannot be initialized

The non-reactive version of this code works fine. But in the reactive version, something happens when mapping a null or empty collection from the database.
The POST of a new Template object returns a 201 with nothing unusual in the logs. But, when I do the GET on Template, the listAll() returns the error below.
I've tried initializing the "sections" member to an empty collection, but the result is the same.
What am I missing?
The Reactive Entity:
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
#Entity
public class Template extends PanacheEntity {
public String name;
#OneToMany(mappedBy = "template", cascade = CascadeType.ALL)
public List<Section> sections;
}
The Resource API:
#GET
#Path("template")
public Uni<List<Template>> listTemplates() {
return Template.<Template>listAll();
}
#POST
#Path("template")
#Consumes("application/json")
#Produces("application/json")
#ReactiveTransactional
public Uni<Response> addTemplate(Template template) {
return Panache.<Template>withTransaction(template::persist)
.onItem().transform(inserted -> {
return createdResponse("/template/%d", inserted.id);
});
}
The Dependencies:
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
<artifactId>quarkus-resteasy-reactive</artifactId>
<artifactId>quarkus-reactive-pg-client</artifactId>
The error:
JsonMappingException: HR000056: Collection cannot be initialized: score.Template.sections (through reference chain: java.util.ArrayList[0]->score.Template["sections"])
try this workaround fetch = FetchType.EAGER
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
#Entity
public class Template extends PanacheEntity {
public String name;
#OneToMany(mappedBy = "template", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
public List<Section> sections;
}

SpringBoot : RestAPI Returning JSON Array But I want value with label to set in AngularJS

Requirement:
Trying to populate all UNIQUE record in Angular Drop Down List
I am using predefined Table have already data.
REST API URL => http://localhost:8080/getAllCategory
Facing Problem:
API is giving the reponse in JSON Array like [xxx,yyyy,zzzz]. So I am thinking if I can convert JSON Array with some label value which can solve my problem.
Either any other way to get over with this issue.
Note :
If I am not using the native query and using the below code then I am getting all the table value in JSON with label and populating all the record in drop down but I want only UNIQUE
#Repository
public interface CategoryRepository extends JpaRepository<ccCategory,Integer>
{}
My Implementation :
Model :
#Table(name = "cccategory")
public class ccCategory
{
#Id
#Column(name = "[catid]")
public Integer catID;
#Column(name = "[categoryname]")
public String categoryName;
#Column(name = "[active]")
public int active;
public ccCategory() {
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public int getActive() {
return active;
}
public void setActive(int active) {
this.active = active;
}
}
Repository:
#Repository
public interface CategoryRepository extends JpaRepository<ccCategory,Integer>
{
public static final String FIND_CATEGORYNAME = "SELECT DISTINCT catID,categoryName from ccCategory";
#Query(value = FIND_CATEGORYNAME, nativeQuery = true)
List<ccCategory> getByactive(int active);
}
Controller :
#GetMapping("/getAllCategory")
public List<Object> getAllCategory() {
// public List<ccCategory> getAllCategory() {
System.out.println("***** Call : API getAllCategory() ******");
List<Object> cCategory = categoryRepository.getCategoryName();
return categoryData;
}

RequestBody POJO always empty

I want to store some data from an HTML form (working with AngularJS) into my database, using Spring.
For this, I'm using the #RequestBody annotation with a POJO, but I can't make it work: my POJO is instancied, but it looks like the POJO attributes are not mapped with my form values (they are all null).
Controller :
#RequestMapping(value = "/createEntities", method = RequestMethod.POST)
#ResponseBody
public List<Entity> createEntities(#RequestBody final EntityList resource, #RequestParam final String kind) {
System.out.println("Creating entity for: " + kind);
Preconditions.checkNotNull(resource);
List<Entity> newEntities = new ArrayList<Entity>();
System.out.println("Entity test = " + resource.getTest()); // Prints "Entity test = null"
// Code below returns NullException
//System.out.println("Entity list nb = " + resource.getEntity().size());
if (resource.getEntities() != null && !resource.getEntities().isEmpty()) {
System.out.println("Entity list is OK");
for (EntityForm eForm : resource.getEntities()) {
if (eForm.getGrant() != null) {
Entity ent = new Entity();
if ("RTS".equals(kind)) {
ent.setDept(deptService.findByAbr(DeptEnum.RTS.name()));
} else {
ent.setDept(deptService.findByAbr(DeptEnum.RTB.name()));
}
ent.setGrant(eForm.getGrant());
ent.setCountry(eForm.getCountry());
ent.setName(eForm.getName());
ent = service.create(ent);
newEntities.add(ent);
}
}
}
return newEntities;
}
EntityList is the POJO for my form. This POJO contains a list of EntityForm (+ a string for test purpose), which is a DTO for my database entity Entity.
EntityList POJO :
public class EntityList implements Serializable {
private static final long serialVersionUID = 6335318686899794229L;
private List<EntityForm> entities;
private String test;
public EntityList() {
super();
}
public EntityList(List<EntityForm> entities, String test) {
super();
this.entities = entities;
this.test = test;
}
public List<EntityForm> getEntities() {
return entities;
}
public void setEntities(List<EntityForm> entities) {
this.entities = entities;
}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
I thought the problem came from a bad mapping between my list of entities in my form and my List<EntityForm> in my POJO, that's why I added a simple String to my POJO.
AngularJS side
Service :
app.factory("Entities", function($resource) {
return $resource("api/auth/entities", null,
{
createEntities: {method:'POST', url: "api/auth/entities/createEntities", params: { kind: '#kind' }, isArray:true}
});
})
Controller :
$scope.EntForm = {};
$scope.EntForm.entities = [];
$scope.EntForm.test = "myTest";
/* ... */
$scope.saveEnt= function() {
console.log($scope.EntForm);
Entities.createEntities($scope.EntForm,{kind:"RTS"},function(res) {
var msg = 'Entities created...';
ngToast.create(msg);
$location.path("/entities");
});
}
In my firefox console, I see that $scope.EntForm is correctly set (I have all my entity objects with the fields set, as well as the test string defined in the controller).
Result
All this code will display :
Creating entity for: RTS
Entity test = null
What am I doing wrong ?
Have you checked out the POST payload with Firefox developer tools, is your custom createEntities method working correctly?
(Would have added this as a comment, but unfortunately I don't yet have enough reputation for that.)
I had to remove the #RequestParam final String kind part from my Spring controller, and the param in AngularJS code.
To get the kind, I just added $scope.EntForm.kind = "theValueIWant" in my AngularJS controller.
I don't know if it's a good way to make it work in terms of good practice, but I get the #RequestBody content now.

Chaining filters with Objectify doesn't work

For some reason, I need to store my data in a Map and I don't want to declare each attribute of my objectify Entity one by one.
For instance, here is what my map would look like to:
"COMPANY_NAME" -> "something"
"TURNOVER_Min" -> 1000000 (a long value)
"CLIENT_STATUS"-> true (a boolean value)
And I would like to perform queries like that :
List<Lead> leads = ofy().load().type(Lead.class).filter("data.NET_INCOME_MIN >", 5.0).filter("data.NET_INCOME_MAX <", 100.0).list();
But I am getting no results although some data match this query...
I must tell you that it works for one filter at a tome...
It also works for several filters with "=" :
Here is my entity:
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
#Entity
public class Lead implements Serializable {
private static final long serialVersionUID = 5920146927107230150L;
#Id
private String url;
#Index
private Date dateCreated;
#Index
private Map<String, Object> data = new HashMap<>();
public Lead() {}
public Lead(String url) {
this.url = url;
this.dateCreated = new Date();
}
public void addData(String key, Object value) {
data.put(key, value);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
}
Thank you very much
This is not your fault and not Objectify problem either.
As per the datastore documentation:
Inequality filters are limited to at most one property
To do what you want to do :
Use the first filter to retrieve only the keys of the entities ( use .keys() instead of .list() ).
Use the second filter to retrieve only the keys of the entities.
To perform the ANDing you need to get the intersection of the two key sets retrieved above.
Now as you have the keys of the entities you want and you can fetch the entities with a batch get operation.

Spring Data Mongo | Dynamic Collection name failing with #Query methods

Consider a simple domain class:-
#Document(collection = "#{T(demo.TenantGenerator).tenant()}Employee")
public class Employee implements Serializable{
/**
* serialVersionUID
*/
private static final long serialVersionUID = -6236812201549032402L;
#Id
private String id;
protected String name;
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
Here I have configured dynamic collection name using SpEL targeting a static method:-
public class TenantGenerator {
public static String tenant = "";
public static final String tenant(){
return tenant;
}
}
This is how my repository interface looks like:-
public interface EmployeeRepository extends MongoRepository<Employee,String> {
#Query(value="{'name':?0}")
public List<Employee> someMethod(String id);
}
My test code:-
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests {
#Autowired
private EmployeeRepository repo;
#Autowired
private MongoTemplate template;
#Test
public void contextLoads() {
// Set the collection with ABC
TenantGenerator.tenant = "ABC";
Employee e = new Employee();
e.setName("test");
repo.save(e);
Employee ee = new Employee();
e.setName("test");
repo.save(ee);
List<Employee> findAll = repo.findAll();
System.out.println(findAll.size());
Employee eee = new Employee();
e.setName("test");
template.save(eee,"customercoll");
System.out.println(repo.someMethod("test"));
//Set collection name with XYZ
TenantGenerator.tenant = "XYZ";
System.out.println(repo.someMethod("test")); // PROBLEM this should try to get from XYZ. But instead tries to fetch from ABC itself
System.out.println(repo.findAll());
}
}
PROBLEM
The collection name is correctly picked at run time except case when I a method in my repository that has #Query annotation.
While trying to debug the Spring Data Mongo code , I found that for #Query annotated methods,org.springframework.data.mongodb.repository.query.MongoQueryMethod caches the collection information inside it in property org.springframework.data.mongodb.repository.query.MongoQueryMethod.metadata.
So while trying to get org.springframework.data.mongodb.repository.query.MongoQueryMethod.getEntityInformation() during query execution, collection name is not fetched using the SpEL the second time.
Relavant piece of code:-
#Override
#SuppressWarnings("unchecked")
public MongoEntityMetadata<?> getEntityInformation() {
if (metadata == null) { // !!!THIS IS PROBLEM FOR ME!!!!
Class<?> returnedObjectType = getReturnedObjectType();
Class<?> domainClass = getDomainClass();
MongoPersistentEntity<?> returnedEntity = mappingContext.getPersistentEntity(getReturnedObjectType());
MongoPersistentEntity<?> managedEntity = mappingContext.getPersistentEntity(domainClass);
returnedEntity = returnedEntity == null ? managedEntity : returnedEntity;
MongoPersistentEntity<?> collectionEntity = domainClass.isAssignableFrom(returnedObjectType) ? returnedEntity
: managedEntity;
this.metadata = new SimpleMongoEntityMetadata<Object>((Class<Object>) returnedEntity.getType(),
collectionEntity.getCollection());
}
return this.metadata;
}
WHAT I WANT
How can I pick collection name at run time even for the #Query annotated repository methods?
Thanks
Looks like you're running into this issue which has been fixed recently, was just released in Spring Data MongoDB 1.7 M1 and is scheduled to be in to-be-released 1.6.2 and 1.5.5. Feel free to give the milestones a spin.

Resources