How to find elements in QMap spring-data-mongoDB queryDSL - java

we are using spring-boot 2.2.1 and query-dsl-mongoDB 4.2.1.
we are using spring-data.mongodb findAll method tp find books from book collection using
various predicates like bookUid, authorId, customerId, status, isbn and provisioningId.
I can able to construct for all the attributes except bookInfo.
Please find the sample collection for reference.
{
"_id" : ObjectId("6036323daa819c04005cff68"),
"bookUid" : "spring_boot",
"authorId" : "602bc44827e37ca2ba281f54",
"customerId" : "75e1c48e",
"status" : "ACTIVE",
"name" : "Spring boot",
"statusTimestamp" : ISODate("2021-02-24T11:07:28.000Z"),
"deleted" : false,
"bookInfo" : {
"isbn" : "240220211202",
"provisioningId" : "240220211202"
},
"customInfo" : {},
"version" : 1,
"countryCode" : "CZ",
"_class" : "book-collection"
}
And this is the Java class,
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Book {
#Id
private String id;
#Indexed
#WhiteSpaceTrim
private String bookUid;
private String authorId;
private String customerId;
private String status;
private String name;
private Date statusTimestamp;
private boolean deleted;
#WhiteSpaceTrim
private Map<String, String> provisionInfo;
private SmartObject customInfo;
private int version;
private String countryCode;
}
While looking into query DSL autogenerated class I see the types for BookInfo is QMap
public final ext.java.util.QMap provisionInfo = new
ext.java.util.QMap(forProperty("bookInfo"));
I tried to construct below predicate whether bookInfo matches the bey key and value
Map<String, String> expre = new HashMap<>();
expre.put(key, value);
predicates.add(QBook.book.provisionInfo.in(expre).isTrue());
But no luck it was not working and thrown exception, Then tried following expression
PathBuilder entityPath = new PathBuilder<>(Book.class,
"bookInfo");
predicates.add(entityPath.getMap("map", String.class, String.class).get(key).eq(value));
But it returns an empty collection always even though we have matching isbn or provisioningId.
I was looking into the documentation but I couldn't find any help either.
Any help would be really appreciable.

Related

Aggregation Java MongoDB with MongoTemplate not return group value

In my Spring Controller I cannot figure out how to retrieve correctly the result of a MongoDB with MongoTemplate aggregation. Below is the code from my Controller class:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("modality").is("check")),
Aggregation.group("name").count().as("check_count"),
Aggregation.project("name").and("check_count").as("check_count")
);
AggregationResults<Response> res = mongoTemplate.aggregate(aggregation, "user", Response.class);
Below the simple class of User and Response:
#Document(collection = "user")
class User{
#Id
private String id;
private String name;
private String modality;
//constructor and get/set
}
class Response{
private String name;
private string check_count;
//constructor and get/set
}
So I retrieve correctly my response but I do not see the name, that is always null:
{
"user": [
{
"name": null,
"check_count": 61
},
{
"name": null,
"check_count": 15
},...
What is wrong in my Aggregation.group ? Thanks
When you do the group aggregation, the pipeline that gets generated is mostly
{ "$group" : { "_id" : "$name", "check_count" : { "$sum" : 1}}}
So, the result of the group stage will have _id as the field, not name.
Subsequent, stage should use _id field.
Aggregation.project().and("_id").as("name").and("check_count").as("check_count");

How to apply List in one field with MapStruct?

I have this POJO :
public class PlayerDto {
private Long id;
private String name;
private String past;
}
And I have this entity :
public class Player {
private Long id;
private String name;
private List<String> past;
}
How can I map the List<String> past into the String past of the DTO wih MapStruct ? For example the List is containing [ Monty , Boto , Flaouri ] and the String of the DTO has to contain "Monty, Boto, Flaouri" in a single String.
This classic way doesn't work with the target and source :
#Mappings({
#Mapping(target = "past", source = "past"),
})
PlayerDto entityToDto(final Player entity);
Thanks
I guess you need to define a default method in your mapper interface to handle data conversion from List<String> to String. Mapstruct will automatically use the default method.
The default method signature for your mapping should be like this :
String map(List<String> past)
Example :
default String map(List<String> past) {
return past.stream().collect(Collectors.joining(","));
}

Java Spring + MongoTemplate cannot retrieve List of Object

I had a Java Class linked to a MongoDB Collection:
#Document(collection = "my_collection")
public class Ev extends MyDTO{
#Id
private String id;
#Indexed
private String sessionId;
private List<String> findings;
}
I had to change findings in this
private List<MyObject> findings;
Declared as
public class MyObject {
private String find;
private String description;
private int number;
private List<SecondaryObj> details;
}
Here are the constructors
public MyObject(String find, int number) {
super();
this.find= find;
this.number= number;
}
public MyObject(String find, int number, List<SecondaryObj> details) {
super();
this.find= find;
this.details = details;
this.number= number;
}
So in mongoDB I have a situation similar to
{
"_id" : ObjectId("5b487a2667a1aa18f*******"),
"sessionId" : "abc123mySessionId",
"findings" : [
{
"find" : "HTTPS",
"description" : "I found HTTPS",
"number" : 10,
"details": [
{"a":"1", "b":"2"},
{"a":"2", "b":"3"}
]
},
{
"find" : "NAME",
"description" : "I found name",
"number" : 3,
"details": [
{"a":"1", "b":"2"},
{"a":"2", "b":"3"}
]
}
]
}
I obviously updated all the methods to match the new data set, but if I try to retrieve
Query searchQuery = new Query(Criteria.where("sessionId").is("abc123mySessionId"));
Ev result = mongoTemplate.findOne(searchQuery, Ev.class);
I obtain this error
Request processing failed; nested exception is org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate com.my.project.domain.MyObject using constructor NO_CONSTRUCTOR with arguments
with root cause
java.lang.NoSuchMethodException: om.my.project.domain.MyObject.<init>()
I'm using spring-data-mongodb version 2.0.8 and mongo-java-driver version 3.8.0
I think I should declare MyObject somewhere, but I'm pretty new in Spring, so I'm trying in a kinda blind way... Any suggestion?
You have two non-zero-argument constructors and Spring does not know which one to call. It tries to call no-args constructor, but your class does not have that one.
Check Spring Data Mongo docs
You can create no-args constructor and mark it with #PersistenceConstructor annotation. This way Spring calls it to create an object and sets fields via reflection based on a document fields names, so no setters are required.
#Document(collection = "my_collection")
public class Ev extends MyDTO{
#Id
private String id;
#Indexed
private String sessionId;
private List<MyObject> findings;}
public class MyObject {
private String find;
private String description;
private int number;}
In this it work fine for me in spring-boot-starter-data-mongodb - version 2.0.3.RELEASE

How to map one-to-one relationship with JPA in spring boot?

I have read several tutorial about one-to-one relation mapping forexample:
https://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html,
http://websystique.com/hibernate/hibernate-one-to-one-unidirectional-with-foreign-key-associations-annotation-example/
https://en.wikibooks.org/wiki/Java_Persistence/OneToOne
I beleive I follow these tutorials, however my relational mapping still not works as expected. I have the following classes:
#Entity(name = "lesson")
public class Lesson {
#Id
#Type(type = "pg-uuid")
private UUID uid;
private String start_date_time;
private String end_date_time;
private String location;
#OneToOne
#JoinColumn(name="uid") //uid is the name of the Id i want to reference to in the subject class
private Subject subject_uid; // subject_uid is the name of the column in my subject table
public Lesson(UUID uid, String start_date_time, String end_date_time, String location, Subject subject_uid) {
this.uid = uid;
this.start_date_time = start_date_time;
this.end_date_time = end_date_time;
this.location = location;
this.subject_uid = subject_uid;
}
//getters setters
#Entity(name = "subject")
public class Subject {
#Id
#Type(type = "pg-uuid")
private UUID uid;
private String name;
private String start_date;
private String end_date;
private boolean is_lesson_created;
public Subject(UUID uid, String name, String start_date, String end_date, boolean is_lesson_created) {
this.uid = uid;
this.name = name;
this.start_date = start_date;
this.end_date = end_date;
this.is_lesson_created = is_lesson_created;
}
The response what the Spring Data Rest creates on /lessons endpoint looks the following:
{
"_embedded" : {
"lessons" : [ {
"start_date_time" : "2017-01-08 08:30:00",
"end_date_time" : "2017-01-08 10:15:00",
"location" : "A101 ",
"_links" : {
"self" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500"
},
"lesson" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500"
},
"subject_uid" : {
"href" : "http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500/subject_uid"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:3400/lessons{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://localhost:3400/profile/lessons"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
When I want access the http://localhost:3400/lessons/78038aeb-cdc9-4673-990e-36b8c1105500/subject_uidlink I get a 404.
Is the UUID type effects my mapping? What should I change to be able to access my student_uid?
Finally I found out the problem, which is something I haven't read anywhere. When a one-to-one join has to be done, JPA provides the default name of the join as the table name underscore id(subject_id). In my case, I have a tablename called "subject" in the database and the PK called simply "uid". So what you have to do is append the table name with the name of the attribute, which to join to:
#OneToOne
#JoinColumn(name="subject_uid")//the pattern is: "tablename_joined attribute"
private Subject subject_uid;

Spring Boot MongoDb Collections

I am pretty new to mongo so this is probably just me not understanding how to setup collections. I have 2 domain objects and I have stripped them down to the basics for simplicity.
Post.java
#Document
public class Post {
#Id
private BigInteger id;
private String title;
private String body;
private String teaser;
private String slug;
private Date postedOn;
private Author author;
public Post(){
}
// getters & setters
}
Author.java
#Document
public class Author {
private BigInteger id;
private String firstName;
private String lastName;
private String email;
#DBRef
private List<Post> posts;
public Author(){
}
// getters and setters
}
I have a method that gets called to load some data into the database.
#PostConstruct
private void initDatabase(){
authorRepository.deleteAll();
Author dv = new Author();
dv.setFirstName("Dan");
dv.setLastName("Vega");
dv.setEmail("danvega#gmail.com");
authorRepository.save( dv );
postRepository.deleteAll();
Post post = new Post();
post.setTitle("Spring Data Rocks!");
post.setSlug("spring-data-rocks");
post.setTeaser("Post Teaser");
post.setBody("Post Body");
post.setPostedOn(new Date());
post.setAuthor(dv);
postRepository.save(post);
}
When I run mongo from the console and show collections I see both the post and author collections.
When I run db.post.find() the post contains the author object
{ "_id" : ObjectId("5666201fd4c6bcfd2f4caa90"), "_class" : "com.therealdanvega.domain.Post", "title" : "Spring Data Rocks!", "body" : "Post Body", "teaser" : "Post Teaser", "slug" : "spring-data-rocks", "postedOn" : ISODate("2015-12-08T00:11:11.090Z"), "author" : { "_id" : ObjectId("5666201fd4c6bcfd2f4caa8f"), "firstName" : "Dan", "lastName" : "Vega", "email" : "danvega#gmail.com" } }
But when I run db.author.find() I don't see the post collection in there.
{ "_id" : ObjectId("5666201fd4c6bcfd2f4caa8f"), "_class" : "com.therealdanvega.domain.Author", "firstName" : "Dan", "lastName" : "Vega", "email" : "danvega#gmail.com" }
Does anyone know what I am missing?
When you save you author object there is no post inside.
Create your Author, and create you post as you do.
Then set the post just created into the author's post list and save it.
This should do the trick
#PostConstruct
private void initDatabase(){
authorRepository.deleteAll();
Author dv = new Author();
dv.setFirstName("Dan");
dv.setLastName("Vega");
dv.setEmail("danvega#gmail.com");
authorRepository.save( dv );
postRepository.deleteAll();
Post post = new Post();
post.setTitle("Spring Data Rocks!");
post.setSlug("spring-data-rocks");
post.setTeaser("Post Teaser");
post.setBody("Post Body");
post.setPostedOn(new Date());
post.setAuthor(dv);
postRepository.save(post);
// add the code below
dv.posts = new ArrayList<Post>();
dv.posts.add(post);
authorRepository.save( dv );
}

Categories