Spring data mongodb custom query, get by a list of nested property - spring-data

I have a following dtos and rest repository of Person
DTOs
class Person {
private String id;
private String name;
private String description;
private List<Address> addresses;
private Status status;
}
class Address {
private String id;
private String addressLineOne;
private String addressLineTwo;
}
enum Status {
ACTIVE, INACTIVE
}
Spring Data REST repository
#RestResource(path = "byAddresses")
#Query("{'$and': [{'status': 'ACTIVE'}, {'addresses.id' : {'$in' : ?1}}]}")
Page<Person> findByAddresses(List<String> ids, Pageable pageable);
I want to get persons by addresses list. For an example lets say there are 3 addresses in the list. If 5 people are using at least one of the addresses from that, all 5 people should be there in the results. How can I changes this query to to achieve that?

Related

How to update few fields in mongodb by using MongoRepository?

I have a User POJO having fields:
#Id
private String _id;
private String phone;
private String email;
private String password;
private String userName;
private String dob;
private String gender;
private String city;
private String pincode;
private String status;
private String validUpto;
private List<String> userRole;
private String persona;
I saved all the fields in MongoDB (document).
Now I want to update only few fields like city, Pincode.
I also refer this question, but it is not giving the answer via MongoRepository.
is there any way we can update only few fields via MongoRepository instead of MongoTemplate.
The repository doesn't provide an 'update' operation only .save(object);
But you can update it by retrieving the Object from the repository, change the relevant fields. Afterwards, you save the updated object to the repository.
Which will get you the desired result of 'updating'.
Spring-boot/SpringRepository example.
#Autowired
UserRepository userRepository;
#Test
public void testUpdateUser() throws Exception {
User foundUser = userRepository.findById("1");
foundUser.setCity("Helsinki");
// foundUser.setOtherFields("new values");
userRepository.save(foundUser); // Will 'update' but it essentially replaces the entity in database
}

Table per class configuration with Morphia and MongoDb is not working

I am trying to have table per class configuration with Morphia + MongoDb
#Entity("user")
public class User{
#Id
private ObjectId id;
private String username;
public void setId(ObjectId id){
this.id = id;
}
public ObjectId getId(){
return this.id;
}
public void setUsername(String username){
this.username = username;
}
public String getUsername(){
return this.username;
}
}
And Assistant
#Entity("assistant")
public class Assistant extends User{
private String fullname;
public String getFullname(){
return this.fullname;
}
public void setFullname(String fullname){
this.fullname = fullname;
}
}
I Want to have a user document and assistant document with same ObjectId. How Do this?
MongoDB is not a relational database, you will need to change your mental & database model.
If somebody is an assistant, all the data will go to the assistant collection. There are no joins (except for aggregations, but you don't want to do this for a simple user lookup), so all the information should be in one entity.
You could even keep the assistants in the user collection. Or speaking in more general terms: Keep all subclasses in the entity of its parent class.
Morphia will automatically store the Java class for every entity, so you can simply filter on that. And since there is no strict schema, there are also no null values since the JSON only stores the available attributes.

Mapping POJOs and Using Reference in MongoDB using Morphia

I have two model classes. One is Company.java, another is HumanResource.java.
Company.java
#Entity("companies")
public class Company {
#Id
private ObjectId id = new ObjectId();
private String companyName;
private String emailAddress;
private String pictureUrl;
#Reference
private List<HumanResource> humanResources;
...
HumanResource.java
#Entity("humanresources")
public class HumanResource {
#Id
private ObjectId id = new ObjectId();
private String firstName;
private String lastName;
private String emailAddress;
#Reference
private Company company;
...
What I want to achieve is when I save a list of companies to datastore, related list of human resources documents should be inserted automatically.
In addition, I declared
#Id
private ObjectId id = new ObjectId();
in every model class. Is it a good way or should I change it ?
Morphia will not call save() on those references. You must call save() on the instances you want to persist. You can pass in a list of instances so you needn't loop, necessarily, but each instance needs to get passed in explicitly.

Hibernate Search Tuple Queries

I have an entity Message with a one-to-many relation to an entity Header. How can I create a tuple based search query like
(message.headerKey="foo" and message.headerValue="123") and
(message.headerKey="bar" and message.headerValue="456")
My current logic would also match when I swap the header values in my search criteria
(message.headerKey="foo" and message.headerValue="456") and
(message.headerKey="bar" and message.headerValue="123")
How can I do a tuple based query using the Hibernate Search API?
This is my Message Entity:
#Entity
#Table(name="MESSAGE")
#Indexed
public class MessageEntity implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Long id;
#Column(name="message_timestamp")
private Date timestamp;
#Column(name="payload")
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String payload;
#OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy = "message")
#IndexedEmbedded
private List<HeaderEntity> headers;
// Getters and Setters
}
This is my Header Entity:
#Entity
#Table(name="HEADER")
public class HeaderEntity implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="header_key")
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String headerKey;
#Column(name="header_value")
Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
private String headerValue;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="message_id")
private MessageEntity message;
// Getters and Setters
}
This is my search logic:
public List<MessageEntity> search(Header[] headers) {
FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(mgr);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MessageEntity.class).get();
TermMatchingContext onFieldKey = qb.keyword().onField("headers.headerKey");
TermMatchingContext onFieldValue = qb.keyword().onField("headers.headerValue");
BooleanJunction<BooleanJunction> bool = qb.bool();
org.apache.lucene.search.Query query = null;
for (Header header : headers) {
bool.must(onFieldKey.matching(header.getKey()).createQuery());
bool.must(onFieldValue.matching(header.getValue()).createQuery());
}
query = bool.createQuery();
FullTextQuery persistenceQuery = fullTextEntityManager.createFullTextQuery(query, MessageEntity.class);
persistenceQuery.setMaxResults(10);
return persistenceQuery.getResultList();
}
Your approach will indeed not work. The problem is that Lucene is a flat data structure, in particular associations (embedded entities) are just "added" to the Lucene Document of the owning entity. In your case the MessageEntity document will contain two fields per headerKey respectively headerValue. Once with "foo" and "bar" as value and56" as values. once with "123" and "456" as values. There is no notion that two of these values are acutally a pair.
One potential solution is to create a unique field/value pair. Using a custom class bridge you could create a "keyValueField" containing header key and value as concatenated value. In your query you would then target this field using concatenated query parameters.

Query an embedded collection inside an entity using Datanucleus and mongodb

I was trying to query a embedded collection inside an entity using a query similar to the following:
Query q = em.createQuery("SELECT u FROM User u , in (u.addresses) a
WHERE a.state='xx'");
The query didn't return any result nor did it throw any error. I am using Datanucleus and MongoDb. Does Datanucleus have any limitation on such queries?
And the entity looked like:
public class User{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
protected long id;
#ElementCollection
protected List<Address> addresses;
public User(){
}
...
#Embeddable
public class Address {
private String street;
private String city;
private String state;
private Integer zip;
public Address(){
}
...