Get last created document in Mongodb using Spring Data repository - spring

I'm trying to get the created datetime of the last created item in a mongodb repository.
I could obviously use a findAll(Sort sort) function, and get the first element, but this would not be very practical on a large database.
Mongo queries do not support an "orderBy" query method so this is also not a solution.
The order of creation is in chronological order of "created" so if I can get the last created document in the collection that would be good too.
So my question is:
What is the best way to retrieve the last created document in a mongodb repo using Spring data?
My current code:
#Data
#Document
public class Batch
{
#Id
String id;
LocalDateTime created;
//other stuff
}
public interface BatchRepository extends MongoRepository<Batch,String>
{
//this does not work
//Batch findOneOrderByCreatedDesc();
}

Try the following one, it should work well
public interface BatchRepository extends MongoRepository<Batch,String>
{
Batch findTopByOrderByCreatedDesc();
}
Notice that the method name slightly differs from your variant, this difference is important as spring parses the method name and builds a query based on the result of parsing.

Related

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 ...

Replacing entire contents of spring-data Page, while maintaining paging info

Using spring-data-jpa and working on getting data out of table where there are about a dozen columns which are used in queries to find particular rows, and then a payload column of clob type which contains the actual data that is marshalled into java objects to be returned.
Entity object very roughly would be something like
#Entity
#Table(name = "Person")
public class Person {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
#Column(name="PAYLOAD") #Lob private String payload;
//Bunch of other stuff
}
(Whether this approach is sensible or not is a topic for a different discussion)
The clob column causes performance to suffer on large queries ...
In an attempt to improve things a bit, I've created a separate entity object ... sans payload ...
#Entity
#Table(name = "Person")
public class NotQuiteAWholePerson {
#Column(name="PERSON_ID", length=45) #Id private String personId;
#Column(name="NAME", length=45) private String name;
#Column(name="ADDRESS", length=45) private String address;
//Bunch of other stuff
}
This gets me a page of NotQuiteAPerson ... I then query for the page of full person objects via the personIds.
The hope is that in not using the payload in the original query, which could filtering data over a good bit of the backing table, I only concern myself with the payload when I'm retrieving the current page of objects to be viewed ... a much smaller chunk.
So I'm at the point where I want to map the contents of the original returned Page of NotQuiteAWholePerson to my List of Person, while keeping all the Paging info intact, the map method however only takes a Converter which will iterate over the NotQuiteAWholePerson objects ... which doesn't quite fit what I'm trying to do.
Is there a sensible way to achieve this ?
Additional clarification for #itsallas as to why existing map() will not suffice..
PageImpl::map has
#Override
public <S> Page<S> map(Converter<? super T, ? extends S> converter) {
return new PageImpl<S>(getConvertedContent(converter), pageable, total);
}
Chunk::getConvertedContent has
protected <S> List<S> getConvertedContent(Converter<? super T, ? extends S> converter) {
Assert.notNull(converter, "Converter must not be null!");
List<S> result = new ArrayList<S>(content.size());
for (T element : this) {
result.add(converter.convert(element));
}
return result;
}
So the original List of contents is iterated through ... and a supplied convert method applied, to build a new list of contents to be inserted into the existing Pageable.
However I cannot convert a NotQuiteAWholePerson to a Person individually, as I cannot simply construct the payload... well I could, if I called out to the DB for each Person by Id in the convert... but calling out individually is not ideal from a performance perspective ...
After getting my Page of NotQuiteAWholePerson I am querying for the entire List of Person ... by Id ... in one call ... and now I am looking for a way to substitute the entire content list ... not interively, as the existing map() does, but in a simple replacement.
This particular use case would also assist where the payload, which is json, is more appropriately persisted in a NoSql datastore like Mongo ... as opposed to the sql datastore clob ...
Hope that clarifies it a bit better.
You can avoid the problem entirely with Spring Data JPA features.
The most sensible way would be to use Spring Data JPA projections, which have good extensive documentation.
For example, you would first need to ensure lazy fetching for your attribute, which you can achieve with an annotation on the attribute itself.
i.e. :
#Basic(fetch = FetchType.LAZY) #Column(name="PAYLOAD") #Lob private String payload;
or through Fetch/Load Graphs, which are neatly supported at repository-level.
You need to define this one way or another, because, as taken verbatim from the docs :
The query execution engine creates proxy instances of that interface at runtime for each element returned and forwards calls to the exposed methods to the target object.
You can then define a projection like so :
interface NotQuiteAWholePerson {
String getPersonId();
String getName();
String getAddress();
//Bunch of other stuff
}
And add a query method to your repository :
interface PersonRepository extends Repository<Person, String> {
Page<NotQuiteAWholePerson> findAll(Pageable pageable);
// or its dynamic equivalent
<T> Page<T> findAll(Pageable pageable, Class<T>);
}
Given the same pageable, a page of projections would refer back to the same entities in the same session.
If you cannot use projections for whatever reason (namely if you're using JPA < 2.1 or a version of Spring Data JPA before projections), you could define an explicit JPQL query with the columns and relationships you want, or keep the 2-entity setup. You could then map Persons and NotQuiteAWholePersons to a PersonDTO class, either manually or (preferably) using your object mapping framework of choice.
NB. : There are a variety of ways to use and setup lazy/eager relations. This covers more in detail.

Spring Data MongoDB document expiry time

I've set up a simple document model (below) along with a Spring Data repository
#Document(collection = "users")
public class UserDocument {
#Id
private String userId;
#Indexed(expireAfterSeconds=3600)
private LocalDateTime registeredDate;
}
This seems to work fine and deletes the user documents after whatever time I set expireAfterSeconds to. However, rather than typing in a value of 3600 I'd like to pull that number from a config .yml. The usual way of adding #Value("${config.file.path.of.expiry}") won't work because #Indexedrequires the value to be a runtime constant, which #Value isn't.
Is there any other way to set up document expiry dates in Spring/Mongodb that doesn't use #Indexed(expireAfterSeconds=x)?
Instead of using the Indexed annotation to declare an index which Spring Data Mongo will create on your behalf ... you could create the index explicitly using a MongoTemplate instance.
#Value("${config.file.path.of.expiry}")
long expireAfterSeconds;
...
mongoTemplate.indexOps("users").ensureIndex(
new Index().on("registeredDate", Order.ASCENDING)
.expire(expireAfterSeconds)
);
This would allow you to source the value for expireAfterSeconds from a configuration file at runtime rather than hardcoding it in an annotation.

Spring Hibernate - Does it support nested objects?

I recently asked this question : Spring Mongodb - Insert Nested document?
And found out that Spring-Data-MongoDB does not support such behavior - so now I need a working alternative.
Now - to avoid having you look at the code on another page, I am going to paste it here from the other question... Here are my two POJOs :
#Document
public class PersonWrapper {
#Id
private ObjectId _Id;
#DBRef
private Person leader;
#DBRef
List<Person> delegates;
// Getters and setters removed for brevity.
}
public class Person
{
#Id
private ObjectId _Id;
private String name;
// Getters and setters removed for brevity.
}
Now, what I want to be able to do here - is send up a JSON object in my POST request as follows :
{
"personWrapper":
{
"_Id":"<ID HERE (MIGHT WANT SQL TO GENERATE THIS DURING CREATE>",
"leader":{
"_Id":"<ID HERE (MIGHT WANT SQL TO GENERATE THIS DURING CREATE>",
"name":"Leader McLeaderFace"
},
delegates:[{...},{...},{...}]
}
}
At this point - I would like the SQL side of this to create the individual records needed - and then insert the PersonWrapper record, with all of the right foreign keys to the desired records, in the most efficient way possible.
To be honest, if one of you thinks I am wrong about the Spring-Data-MongoDB approach to this, I would still be interested in the answer - because it would save me the hassle of migrating my database setup. So I will still tag the spring-data-mongodb community here, too.
If I understand well you want to cascade the save of your objects ?
ex : you save a PersonWrapper with some Person in the delegates property and spring data will save PersonneWrapper in a collection and save also the list of Person in another Collection.
It is possible to do that with Spring DATA JPA if you annotate your POJO with the JPA annotation #OneToMany and setup cascade property of this annotation. See this post
However the cascade feature is not available for Spring DATA mongoDB. See documentation .First you have to save the list of Person and then you save PersonWrapper.

Spring Data JPA querying with transitive sorting

I got a problem with simple Spring Data issue. Let's assume we got two entities.
public class Request {
// all normal stuff
#ManyToOne
private Document doc;
}
public class Document {
private Long id;
private String name;
}
Simple relation. My question is - is it possible to retrieve Request entities using Spring Data Method-DSL and sorting by Document? So what I want to achieve is to create repository method like:
public List<Request> findAllOrderByDoc()
or similar:
public List<Request> findAllOrderByDocId()
Unfortunately when I try that I am given error message saying that there is no Doc field or it cannot be mapped to long. I assume it is possible to be done using QueryDSL and predicates but I am wondering if this pretty obvious and simple thing can be done by plain Spring Data?
Yes, sure.
you need to provide the direction:
public List<Request> findAllOrderByDocAsc()
public List<Request> findAllOrderByDocDesc()

Resources