Generic MongoRepository Interface for Multiple Documents in MongoDB - mongodb

Here is an example on accessing Person Document from MongoDB.
public interface PersonRepository extends MongoRepository<Person, String>
#Query("{ 'firstname' : ?0 }")
List<Person> findByThePersonsFirstname(String firstname);
}
If I have 5 documents ex: Person, Family, Relation. etc.
Do I need to create 5 Repositories and Extend MongoRepository or is there a way to achieve in one MongoRepository.

Related

JPA query attribute with a converter

I have a SpringBoot 2.6.11 application with JPA 2.2.
I have an entity like this:
#Data
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
....
#Convert(converter = ListConverter.class)
private List<String> referenceCode;
....
}
I have this Converter:
#Converter(autoApply = true)
public class ListConverter implements AttributeConverter<List<String>, String> {
#Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(";", attribute);
}
#Override
public List<String> convertToEntityAttribute(String dbData) {
return new ArrayList<>(Arrays.asList(dbData.split(";")));
}
}
And when I insert or extract this element all working fine. But now I wanna query that element and I don't know how to do it. If I do something like that:
public List<Entity> findByReferenceCode(String reference);
It doesn't work, if I do:
#Query("select e from Entity e where e.referenceCode IN ?1")
public List<Entity> findByReferenceCode(List<String> reference);
Still doesn't work..
The only way I found is by the nativeQuery but is really an extrema ratio. Ho can I solve this?
Thank you
To really do what you want here, you need to use an #ElementCollection. The reason being that there is no reliable way for JPA to query a single column and treat it as a collection. Reliably querying a collection requires a second table (which is what #ElementCollection does). You can continue to use the #Converter, but your queries will have to be customized to handle the disparity between the entity attribute type (list) and the actual database column type (string).
If you are okay with the limitations of the #Converter then it's fine (I have used them this way) but if you truly need to query the attribute like a collection (e.g. search for multiple independent items, perform counts, aggregations, etc) and you want those queries to be generated by a JPA layer, then you will have to use #ElementCollection and let it create a second table.

MongoDB - FindById is not working and giving null

I am using Spring Boot(V2.2.2.RELEASE) + Spring Data Mongo Example. In this example, I've records like below
{
"_id" : ObjectId("5cb825e566135255e0bf38a4"),
"firstName" : "John",
"lastName": "Doe"
}
My Repository
#Repository
public interface EmployeeRepository extends CrudRepository<Employee, ObjectId>{
Employee findById(String id);
}
Code
Employee findById = employeeRepository.findById("5cb825e566135255e0bf38a4");
System.out.println(findById);
Even below code not working
Query query = new Query(Criteria.where("id").is(new ObjectId("5cb825e566135255e0bf38a4")));
List<Employee> find = mongoTemplate.find(query, Employee.class);
Seems there might be two issues
ObjectId should be used
employeeRepository.findById(new ObjectId("5cb825e566135255e0bf38a4"))
ID field goes with underscore
new Query(Criteria.where("_id").is(new ObjectId("5cb825e566135255e0bf38a4")))
I'm not a Java guy, so might miss smth, but at least give it a try :)
Using the input document with _id: ObjectId("5cb825e566135255e0bf38a4") you can use either of the approaches. Assuming there is the document in the employee collection you can query by the _id's string value.
public class Employee {
#Id
private String id;
private String name;
// constructors (with and with arguments)
// get methods
// override toString()
}
// Spring-data app using MongoTemplate
MongoOperations mongoOps = new MongoTemplate(MongoClients.create(), "test");
Query query = new Query(where("id").is("5cb825e566135255e0bf38a4"));
Employee emp = mongoOps.findOne(query, Employee.class);
-or-
MongoRepository
interface extends CrudRepository and exposes the capabilities of the
underlying persistence technology in addition to the generic
persistence technology-agnostic interfaces such as CrudRepository.
#Repository
public interface EmployeeRepository extends MongoRepository<Employee, String> {
#Query("{ 'id' : ?0 }")
Optional<Employee> findById(String id);
}
// Spring-data app using the Repository
Optional<Employee> empOpt = employeeRepository.findById("5cb825e566135255e0bf38a4");
if (empOpt.isPresent()) {
System.out.println(empOpt.get());
}
else {
System.out.println("Employee not found!");
}
I too faced the issue. Interestingly things are working fine with version 2.1.7RELEASE
<documentRepository>.findById(<StringID>) treats as String Id in 2.2.2RELEASE
while 2.1.7RELEASE transforms it to ObjectId and hence the find query works.

How to use criteria query on refrence collection in mongo db

How do I find all the person which are having city ="XYZ" in Address collection
public class Person {
#Id
private String id;
private String description
#DBRef
private Address address;
// Getters and Setters
}
public class Address
{
#Id
private String id;
private String area
private String city
// Getters and Setters
}
Mongo understands #DBRef as a reference to another document, in this case, an Address document and ultimately when the object is loaded from MongoDB, those references will be eagerly resolved and this will get populated to the user as a HATEOAS friendly link. You will get back a mapped object that looks the same as if it had been stored embedded within your master document.
You can define your repository, which will map the endpoints to your database, for the given object, like PersonRepository defined below as an example:
import com.mycompany.domain.Person;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> findByCity(#Param("city") String city);
}
Another way you could go around this using the query criteria methods is executing two queries.
First query would be to fetch the address documents which have the city = "XYZ". Resolve the ids from the list returned.
Generate another query on the Person entity using the ids from the previous operation.
The following demonstrates this approach
Query addressQuery = new Query(where("city").is("XYZ"));
addressQuery.fields().include("_id");
List<Address> addressList = mongoTemplate.find(addressQuery, Address.class, "address"); // get the addresses list that satisfy the given city criteria
// Resolve the ids for the addresses
final List<ObjectId> addressIds = new ArrayList<ObjectId>(addressList.length);
for(final Address address : addressList) {
addressIds.add(new ObjectId(address.getId()));
}
// Get the Person list using the ids from the previous operation
Query personQuery = new Query(where("address.$id").in(addressIds));
List<Person> list = mongoTemplate.find(personQuery, Person.class, "person");
If you knew the address id before hand you can then use a custom query:
public interface PersonRepository extends MongoRepository<Person, String> {
#Query("{ 'address': {'$ref': 'address', '$id': { '$oid': ?0 } } }")
List<Person> findByAddres(String addressIdAsString);
}

spring data mongodb #Query with exclude option

Using spring data mongo repository class, how do we declare a method to return the documents with few fields excluded? Spring data reference document shows 'include' fields mechanism but not exclude.
Code from spring documentation:
public interface PersonRepository extends MongoRepository<Person, String>
#Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
List<Person> findByThePersonsFirstname(String firstname);
}
I need a mechanism to specify the fields to be excluded? Is this supported for repository methods?
specify the fields value as 0. Ex:
public interface PersonRepository extends MongoRepository<Person, String>
#Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 0}")
List<Person> findByThePersonsFirstname(String firstname);
}
This will not fetch firstname property of the document and value will be null in returned java object.
Add an empty filter criteria for findAll query:
public interface PersonRepository extends MongoRepository<Person, String> {
#Query(value = "{}", fields = "{ 'firstname' : 0 }")
List<Person> findAll(Sort sort);
}

Query to find Parent entity by Child attribute where Parent also have same attritube, in spring data jpa

I am trying to create a query in Spring-data-jpa to find a Person entity by Address's id. Person have OneToOne relationship with Address and both have id as primary key.
public abstract class AbstractEntity{
#Id
Long id;
}
public class Person extends AbstractEntity {
#OneToOne
Address address;
}
public class Address extends AbstractEntity {
}
public interface PersonRepository implements JpaRepository<Person, Long> {
Person findByAddressId(Long addressId); // Throws cannot create metamodel exception
Person findByAddress_Id(Long addressId); // Throws cannot create metamodel exception
}
I'm thinking that the method name findByAddressId and findByAddress_Id are ambiguous for Spring data jpa query look-up strategies as both Person and Address entities have id as their attributes.
Is it possible to write query in Spring-data-jpa to find parent entity by child attribute where parent and child both have same attribute, without writing SQL?