Spring data mongodb repository. How can I search by a list of IDs? - spring

I have the following class!
public class Task{
ObjectId id;
String title;
String description;
/* Getters and Setters removed for brevity */
}
and I have the following mongoRepository class, very simple :
public interface TaskRepository extends MongoRepository<Task, String> {
}
As you can see, I have not yet tried to extend this class - What would I want to do here if I want to have a find method, where I could just hand it a list of Ids, and get my list of corresponding tasks back?

The CrudRepository which MongoRepository extends has a findAll method, which takes an Itereable<ID>
I think that is exactly what you are looking for.
Note that it is renamed to findAllById in the latest Milestone releases.

You can create a custom query method that searches for an array of _id values:
#Query(value = "{ '_id' : {'$in' : ?0 } }", fields = "{ 'description': 0 }")
Iterable<Task> findAllThin(Iterable<String> ids);
(in this case it returns the fields id and title only)
#Neil Lunn brought me to the answer.

Related

Spring data mongo - unique random generated field

I'm using spring data mongo. I have a collection within a document that when I add an item to it I would like to assign a new automatically generated unique identifier to it e.g. (someGeneratedId)
#Document(collection = "questionnaire")
public class Questionnaire {
#Id
private String id;
#Field("answers")
private List<Answer> answers;
}
public class Answer {
private String someGeneratedId;
private String text;
}
I am aware I could use UUID.randomUUID() (wrapped in some kind of service) and set the value, I was just wondering if there was anything out of the box that can handle this? From here #Id seems to be specific to _id field in mongo:
The #Id annotation tells the mapper which property you want to use for
the MongoDB _id property
TIA
No there is no out of the box solution for generating ids for properties on embedded documents.
If you want to keep this away from your business-logic you could implement a BeforeConvertCallback which generates the id's for your embedded objects.
#Component
class BeforeConvertQuestionnaireCallback implements BeforeConvertCallback<Questionnaire> {
#Override
public Questionnaire onBeforeConvert(#NonNull Questionnaire entity, #NonNull String collection) {
for (var answer : entity.getAnswers()) {
if (answer.getId() == null) {
answer.setId(new ObjectId().toString());
}
}
return entity;
}
}
You could also implement this in a more generic manner:
Create a new annotation: #AutogeneratedId.
Then listen to all BeforeConvertCallback's of all entities and iterate through the properties with reflection. Each property annotated with the new annotation gets a unique id if null.

Elasticsearch returns empty json-objects

I'm just getting started with elasticsearch with spring which is both technologies which are completely new to me. I have uploaded data to an elasticseach index with logstash and I can search it successfully using kebana. However when I try to return from an index to a webpage using spring it only returns empty json-objects, but the right amount of empty objects. Did I upload the data incorrectly or is something wrong with my code? I don't understand why this is happening and would appreciate any help I can get. You can find some code below.
Code for type:
#Document(indexName="usmgbg_index", type="usmgbg_type")
public class Usmgbg {
#Id
private String ID;
private String Source, Name, Profession, Country, FileName, LastModified, OwnerID;
}
Repository:
#Repository
public interface UsmgbgRepository extends ElasticsearchRepository<Usmgbg, String>{}
Controller:
#RestController
public class UsmgbgController {
#Autowired
private UsmgbgRepository repository;
#GetMapping("usmgbg/findall")
public List<Usmgbg> findAllCustomers() {
List<Usmgbg> items = new ArrayList<>();
repository.findAll().forEach(items::add);
return items;
}
}
The output I'm getting from findAllCustomers looks like:
[{},{},{},{},....]
I realize this is an old question but I had the same issue (maybe for a different reason) and I solved it by adding getters and setters in the model.
Iterable is returned from findAll().
If you want to get list you should get content first.
Change
#Repository
public interface UsmgbgRepository extends
ElasticsearchRepository<Usmgbg, String>{
Page<Usmgbg> findAll();
}
And then
repository.findAll().getContent().forEach(items::add);
Or fix your code to iterate over the results.
Another solution is to use search method in ElasticsearchRepository using QueryBuilders API.
Iterable<Usmgbg>=
repository.search(QueryBuilders.matchAllQuery);
Adding my experience
spring-data requires getters to follow the POJO naming, i.e: getSomething()
so it did not work (spring-data did not send any fields to ElasticSearch when saving the #Document, resulting in an empty _source in ES) when having Lombok #Accessors(fluent = true), as it removes the get prefix on getters ...

Spring Data JPA Repository Method Query Cannot Parse Property Name Following OrderBy

My Repository Method Query cannot parse a property name that comes after the OrderBy, but it can if it follows the findBy or findAllBy. The attribute in my entity that is giving me issues is zIndex
Entity Class
#Entity
public class DisplayLayer
{
#Id
#Column(name="DISPLAY_LAYER_ID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="DISPLAY_CONTAINER_ID")
private DisplayContainer displayContainer;
#Column(name="NAME")
private String name;
#Column(name="Z_INDEX")
private Long zIndex;
#Column(name="DESCRIPTION")
private String description;
// Getters & Setters
}
Repository Class
public interface DisplayLayerRepository extends BaseRepository<DisplayLayer, Long>
{
// This one method query works just fine
public List<DisplayLayer> findByZIndex(Long pZIndex);
// These two throw the same error:
// Unable to locate Attribute with the the given name [ZIndex]
//
// public List<DisplayLayer> findAllByOrderByZIndexAsc();
// public List<DisplayLayer> findByDisplayContainerIdOrderByZIndexAsc(Long pDisplayLayerId);
}
So in my first method query, when OrderBy is not used, it correctly parses it as [zIndex], but when it follows the OrderBy for some reason it capitalizes the z: [ZIndex].
Is this a known issue, or am I doing something wrong?
EDIT
What makes me think there might be a bug with the parser is that if I update zIndex to IndexZ in my entity and then change my query methods from OrderByZIndex to OrderByIndexZ everything works.
This solution is not ideal though as the database table is Z_INDEX and that has a specific meaning when talking about layering of graphics.
Yes there is a bug but there is a simple workaround, please try :
displayLayerRepository.findAll(Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
I suppose DisplayLayerRepository extends JpaRepository.
It's similar when you use parameters in your findBy method. Define in your repository empty method :
public List<DisplayLayer> findByDisplayContainerId(Long pDisplayLayerId, Sort sort);
Then you can call it by :
displayLayerRepository.findByDisplayContainerId(1L, Sort.by(Sort.Order.desc("zIndex").ignoreCase()));
You can use JpaSort in query. As in your case, it looks something like this:
Sort sort = JpaSort.unsafe(Sort.Direction.ASC, "zIndex");
List<DisplayLayer> zIndexes = displayLayerRepository.findByZIndex(pZIndex, sort);
and your query in Repository will look like this:
public List<DisplayLayer> findByZIndex(Long pZIndex, Sort sort);

Spring Jpa Query by Example collection

Let's say I have an entity
public class Person {
private String id;
private String firstname;
private String lastname;
private Set<Car> ownedCars;
}
Is there a way I can use query by example to find any person named James having both a Ferrari and Lamborghini?
If I use:
Person p = new Person();
p.setName("James");
p.getOwnedCars.addCar(new Car("Lamborgnihi"));
p.getOwnedCars.addCar(new Car("Ferrari"));
Example<Person> exampleOfPerson = Example.of(p);
List<Person> foundPersons = personRepository.finaAll(exampleOfPerson);
it seems it queries only on person's attributes and ignores any child collections.
You can use a query method for that. Let's say your Car has a property name that can be "Lamborghini" or "Ferrari"
interface PersonRepository extends JpaRepository<Person, String> {
List<Person> findByOwnedCarsNameIn(Collection<String> names);
}
Then you use it like this:
personRepository.findByOwnedCarsNameIn(Arrays.asList("Ferrari","Lamborghini"));
Some gotchas:
The method parameter can take any subclass of Collection, or an array.
The property names on Person and Car must match the method signature and the parameter name as shown above for spring to know how to generate the query, i.e. Person must have a property called "cars", and Car must have a property called "name".
I used JpaRepository, but this works with any of the Repository interfaces provided with spring data JPA

How to Define Dynamic Model in Spring Framework

I am using Spring Framework as my back end
I have define know as Entity class The Entity class know contain 5 Fields
Below is the class , The code below dose not have setter getter part to make shorter and cleaner
#Entity
#Table(name="TblKnow")
public class Know {
#Id
private Double idKnow;
private String SubjectKnow;
private String BodyKnow;
private String ImgKnow;
private double CountView;
In JpaRepository interface i want to only query two column not all of columns.
public interface KnowRepository extends JpaRepository<Know,Double> {
#Query("SELECT idKnow,SubjectKnow FROM Know")
public Page<Know> findCByOrderByIdKnowDesc(Pageable pageable);
Problem: i try to run but i get below exception
java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return using requested result type [java.lang.Long]
But if i use without below query it is fine
public Page<Know> findAllByOrderByIdKnowDesc(Pageable pageable);
You can create a custom constructor and use that to select only some fields in JPA query.
public Know(Double idKnow, String SubjectKnow) {
this.idKnow = idKnow;
this.SubjectKnow = SubjectKnow;
}
And the use this constructor in JPA query. Make sure you use complete path of class with package.
#Query("SELECT NEW packagePath.Know(idKnow,SubjectKnow) FROM Know")
query :
public Page<Know> findAllByOrderByIdKnowDesc(Pageable pageable);
works dut to you select Know objects with fields that are mapped correct into Know class (and after wrapped into Page).
with query :
#Query("SELECT idKnow,SubjectKnow FROM Know")
public Page<Know> findCByOrderByIdKnowDesc(Pageable pageable);
returns some custome bean/object that spring data can't map in correct way into Know class (as you declared it as expected return class wrapped into Page). add counstructor into Know with idKnow,SubjectKnow fields , or you can wrap it into some DTO with idKnow,SubjectKnow fields.

Resources