n-gram implementaion in spring-boot ElasticSearch - elasticsearch

I am trying to achieve autocomplete in elasticsearch, I am using it inside spring boot, I have tried a lot and tried with many example from internet but not able to make it. below is my code example pls help me on this.
Main Class:-
#SpringBootApplication
#EnableNatsAnnotations
#EnableAutoConfiguration
#EnableConfigurationProperties(ElasticsearchProperties.class)
#EntityScan(basePackages = {
"com.text.model"
})
#ComponentScan(
{
"com.text.elastic",
"com.text.elastic.controller",
"com.text.elastic.service",
"com.text.elastic.service.impl",
"com.text.nats.utils"
}
)
public class ElasticServicesApplication {
public static void main(String[] args) {
SpringApplication.run(ElasticServicesApplication.class, args);
}
}
Bean Class:-
#Setting(settingPath = "elasticsearch-settings.json")
#Document(indexName = "content", type = "content", shards = 1, replicas = 0, createIndex = true, refreshInterval = "-1")
public class Content {
#Id
private String id;
private Locale locale;
// #Field(type = text, index = true, store = true, analyzer = "standard")
#Field(
type = FieldType.String,
index = FieldIndex.analyzed,
searchAnalyzer = "standard",
//indexAnalyzer = "type_ahead",
analyzer = "standard"
/*,
store = true*/
)
private String contentTitle;
Here I want to achieve it in contentTitle.

Mapping
Concise way of using annotation:
#CompletionField()
private Completion suggest;
Or more powerful but tedious way:
{
"content" : {
"properties" : {
"contentTitle" : { "type" : "string" },
"suggest" : { "type" : "completion",
"analyzer" : "simple",
"search_analyzer" : "simple"
}
}
}
}
//Then refer to the mapping by `#Mapping`:
#Setting(settingPath = "elasticsearch-settings.json")
#Document(indexName = "content", type = "content", shards = 1, replicas = 0, createIndex = true, refreshInterval = "-1")
#Mapping(mappingPath = "/mappings/content-mapping.json")
public class Content {...}
Index
We can index as our common entity:
esTemplate.save(new File(...));
Query
The ElasticsearchTemplate has the method for query suggest:
public SuggestResponse suggest(SuggestBuilder.SuggestionBuilder<?> suggestion, String... indices);
Ref
Blog posts about completion
Official document about completion

Related

Spring Boot, query Elasticsearch specific fields from already indexed data created by Elastic Stack

The target is to query specific fields from an index via a spring boot app.
Questions in the end.
The data in elasticsearch are created from Elastic Stack with Beats and Logstash etc. There is some inconsistency, eg some fields may be missing on some hits.
The spring app does not add the data and has no control on the fields and indexes
The query I need, with _source brings
GET index-2022.07.27/_search
{
"from": 0,
"size": 100,
"_source": ["#timestamp","message", "agent.id"],
"query": {
"match_all": {}
}
}
brings the hits as
{
"_index": "index-2022.07.27",
"_id": "C1zzPoIBgxar5OgxR-cs",
"_score": 1,
"_ignored": [
"event.original.keyword"
],
"_source": {
"agent": {
"id": "ddece977-9fbb-4f63-896c-d3cf5708f846"
},
"#timestamp": "2022-07-27T09:18:27.465Z",
"message": """a message"""
}
},
and with fields instead of _source is
{
"_index": "index-2022.07.27",
"_id": "C1zzPoIBgxar5OgxR-cs",
"_score": 1,
"_ignored": [
"event.original.keyword"
],
"fields": {
"#timestamp": [
"2022-07-27T09:18:27.465Z"
],
"agent.id": [
"ddece977-9fbb-4f63-896c-d3cf5708f846"
],
"message": [
"""a message"""
]
}
},
How can I get this query with Spring Boot ?
I lean on StringQuery with the RestHighLevelClient as below but cant get it to work
Query searchQuery = new StringQuery("{\"_source\":[\"#timestamp\",\"message\",\"agent.id\"],\"query\":{\"match_all\":{}}}");
SearchHits<Items> productHits = elasticsearchOperations.search(
searchQuery,
Items.class,
IndexCoordinates.of(CURRENT_INDEX));
What form must Items.class have? What fields?
I just need timestamp, message, agent.id. The later is optional, it may not exist.
How will the mapping work?
versions:
Elastic: 8.3.2
Spring boot: 2.6.6
elastic (mvn): 7.15.2
spring-data-elasticsearch (mvn): 4.3.3
official documentation states that with RestHighLevelClient the versions should be supported
Support for upcoming versions of Elasticsearch is being tracked and
general compatibility should be given assuming the usage of the
high-level REST client.
You can define an entity class for the data you want to read (note I have a nested class for the agent):
#Document(indexName = "index-so", createIndex = false)
public class SO {
#Id
private String id;
#Field(name = "#timestamp", type = FieldType.Date, format = DateFormat.date_time)
private Instant timestamp;
#Field(type = FieldType.Object)
private Agent agent;
#Field(type = FieldType.Text)
private String message;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Instant getTimestamp() {
return timestamp;
}
public void setTimestamp(Instant timestamp) {
this.timestamp = timestamp;
}
public Agent getAgent() {
return agent;
}
public void setAgent(Agent agent) {
this.agent = agent;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
class Agent {
#Field(name = "id", type = FieldType.Keyword)
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
}
The query then would be:
var query = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withSourceFilter(new FetchSourceFilter(
new String[]{"#timestamp", "message", "agent.id"},
new String[]{}))
.build();
var searchHits = operations.search(query, SO.class);

how to mapping join type by using spring data elasticSearch

i reindex data from es 2.4 to 5.6.
data in es 2.4 have 2 types,and the 2 type is parent-child relation.
when reindex it to es 5.6,the index only contains single type,the parent-child relation by using join type to resolving.
the data above works ok.
the mapping example like this, it contains a join type:
"mappings": {
"doc": {
"properties": {
"my_join_field": {
"eager_global_ordinals": true,
"type": "join",
"relations": {
"question": "answer"
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
}
}
}
}
how to mapping join type by using spring data elasticSearch:
in old version code es 2.4,i can mapping it like this:
#Document(indexName = ParentEntity.INDEX, type = ParentEntity.PARENT_TYPE, shards = 1, replicas = 0, refreshInterval = "-1")
public class ParentEntity {
public static final String INDEX = "parent-child";
public static final String PARENT_TYPE = "parent-entity";
public static final String CHILD_TYPE = "child-entity";
#Id
private String id;
#Field(type = FieldType.Text, store = true)
private String name;
public ParentEntity() {
}
public ParentEntity(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
#Override
public String toString() {
return new ToStringCreator(this).append("id", id).append("name", name).toString();
}
#Document(indexName = INDEX, type = CHILD_TYPE, shards = 1, replicas = 0, refreshInterval = "-1")
public static class ChildEntity {
#Id
private String id;
#Field(type = FieldType.Text, store = true)
#Parent(type = PARENT_TYPE)
private String parentId;
#Field(type = FieldType.Text, store = true)
private String name;
public ChildEntity() {
}
public ChildEntity(String id, String parentId, String name) {
this.id = id;
this.parentId = parentId;
this.name = name;
}
public String getId() {
return id;
}
public String getParentId() {
return parentId;
}
public String getName() {
return name;
}
#Override
public String toString() {
return new ToStringCreator(this).append("id", id).append("parentId", parentId).append("name", name).toString();
}
}
}
how can i Mapping join type by using spring data elasticSearch v3.0.10?
Today, i tried the entity below to working at spring data elasticSearch 3.0.10:
#Document(indexName = "join_index", type = "join_mapping")
#Data
public class JoinEntity {
#Id
private String id;
#Mapping(mappingPath = "/mappings/join_type.json")
private Map<String,String> relationType;
#Field(type = FieldType.Keyword)
private String name;
//#Parent(type = "question")
#Field(type = FieldType.Keyword)
private String parentId;
}
join_type.json below:
{
"type": "join",
"relations": {
"question": "answer"
}
}
it create index and put mapping work ok:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateJoinTests {
#Autowired
private ElasticsearchTemplate elasticsearchTemplate;
#Before
public void before() {
clean();
elasticsearchTemplate.deleteIndex(JoinEntity.class);
elasticsearchTemplate.createIndex(JoinEntity.class);
elasticsearchTemplate.putMapping(JoinEntity.class);
elasticsearchTemplate.refresh(JoinEntity.class);
}
#Test
public void shouldCreateIndexAndMappingSuccess(){
Map mapping = elasticsearchTemplate.getMapping(JoinEntity.class);
assertThat(mapping, is(notNullValue()));
Map properties = (Map) mapping.get("properties");
assertThat(properties, is(notNullValue()));
assertThat(properties.containsKey("name"), is(true));
Map file = (Map) properties.get("relationType");
assertThat(file, is(notNullValue()));
assertThat(((String) file.get("type")), is("join"));
}
}
when index parent work ok too,but index child it throws exception:
#Test
public void shouldIndexParentAndChildSuccess(){
JoinEntity parenEntity = new JoinEntity();
parenEntity.setName("parent_name");
parenEntity.setRelationType(Collections.singletonMap("name","question"));
IndexQuery parentQuery = new IndexQueryBuilder().withId("11").withObject(parenEntity).build();
final String id = elasticsearchTemplate.index(parentQuery);
assertThat("11",is(id));
JoinEntity childEntity = new JoinEntity();
childEntity.setName("child_name");
Map<String,String> joinRelation = new HashMap<>(2);
joinRelation.put("name","answer");
joinRelation.put("parent", "11");
childEntity.setRelationType(joinRelation);
childEntity.setParentId("11");
IndexQuery childQuery = new IndexQueryBuilder().withId("22").withObject(childEntity).build();
elasticsearchTemplate.index(childQuery);
}
exception:
MapperParsingException[failed to parse
]; nested: IllegalArgumentException[[routing] is missing for join field [relationType]];
at org.elasticsearch.index.mapper.DocumentParser.wrapInMapperParsingException(DocumentParser.java:171)
how can i resolve this problem or Mapping the new version Parent-child relation correctly?thks!!
Elasticsearch needs the parent document routing parameter when you index child document check this
This is because both parent and child documents must be indexed in same shard to join to work.
However I couldn't figure out a way to solve this using Spring data elasticsearch. The only way that worked was using RestHighLevelClient
The recent version of Spring Data ElasticSearch had added support for this doc
Your child indexing would be something like,
IndexRequest indexRequest = new IndexRequest();
indexRequest.source(objectMapper.writeValueAsString(childEntity),XContentType.JSON);
indexRequest.id("22"); //child doc id
indexRequest.index(INDEX_NAME);
indexRequest.type(INDEX_TYPE);
indexRequest.routing("11"); //parent doc id
restHighLevelClient.index(indexRequest);
Finally, i gived up the parent-child relation, i split them into two separate indexs. some advance feature should be used less if not neccessary.

Partial match with Spring Data Elasticsearch

I was able to search by whole words, for example searching phrase "secret" text "This is a secret word" was found. But If I search for phrase "secre" I get an empty array. I need this for autocomplete function. I'm using Spring Boot release 1.3.1.RELEASE (it uses Elasticsearch version 1.5.2 and I can't upgrade spring). What am I doing wrong? I'l be thankful for a link to a working example also. I know Elasticsearch is too heavy for this purpose but I want to learn how to use it. Many thanks in advance!
SearchConfig.java
#Configuration
#EnableElasticsearchRepositories(basePackages = {"cz.project.search"})
public class SearchConfig {
#Autowired
private Client elasticsearchClient;
#Bean
public ConstructionWorkIndexInitializer constructionWorkIndexInitializer() {
return new ConstructionWorkIndexInitializer();
}
}
ConstructionWorkIndexRepository.java
public interface ConstructionWorkIndexRepository extends ElasticsearchRepository<ConstructionWorkIndex, String> {
Page<ConstructionWorkIndex> findByCodeOrDescription(String code, String description, Pageable pageable);
}
ConstructionWorkIndex.java
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document(indexName = "works", type = "work", shards = 1, replicas = 0, indexStoreType = "memory", refreshInterval = "-1")
#Setting(settingPath = "classpath:construction-works-settings.json")
public class ConstructionWorkIndex {
#Id
#Field(indexAnalyzer = "standard", searchAnalyzer = "standard", type = FieldType.String, store = true)
private String code;
#Field(indexAnalyzer = "standard", searchAnalyzer = "standard", type = FieldType.String, store = true)
private String description;
public ConstructionWorkIndex(ConstructionWorkVO constructionWork) {
requireNonNull(constructionWork, "constructionWork must not be null");
this.code = constructionWork.getCode();
this.description = constructionWork.getDescription();
}
}
construction-works-settings.json
{
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
ConstructionWorkIndexInitializer.java
#Slf4j
public class ConstructionWorkIndexInitializer {
#Autowired
private ConstructionWorkRepository workRepository;
#Autowired
private ConstructionWorkIndexRepository workIndexRepository;
#PostConstruct
#Transactional(readOnly = true)
#Async
public void init() {
initWorks();
}
private void initWorks() {
List<ConstructionWorkVO> works = workRepository.findAll();
works.forEach(work -> {
workIndexRepository.save(new ConstructionWorkIndex(work));
log.debug("Added construction work code '{}'", work.getCode());
});
log.debug("Indexed {} construction works", works.size());
}
}

Adding more information to the HATEOAS response in Spring Boot Data Rest

I have the following REST controller.
#RepositoryRestController
#RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {
#Autowired
public BooksService booksService;
#Autowired
private PagedResourcesAssembler<Books> booksAssembler;
#RequestMapping("/search")
public HttpEntity<PagedResources<Resource<Books>>> search(#RequestParam(value = "q", required = false) String query, #PageableDefault(page = 0, size = 20) Pageable pageable) {
pageable = new PageRequest(0, 20);
Page<Books> booksResult = BooksService.findBookText(query, pageable);
return new ResponseEntity<PagedResources<Resource<Books>>>(BooksAssembler.toResource(BooksResult), HttpStatus.OK);
}
My Page<Books> BooksResult = BooksService.findBookText(query, pageable); is backed by SolrCrudRepository. When it is run BookResult has several fields in it, the content field and several other fields, one being highlighted. Unfortunately the only thing I get back from the REST response is the data in the content field and the metadata information in the HATEOAS response (e.g. page information, links, etc.). What would be the proper way of adding the highlighted field to the response? I'm assuming I would need to modify the ResponseEntity, but unsure of the proper way.
Edit:
Model:
#SolrDocument(solrCoreName = "Books_Core")
public class Books {
#Field
private String id;
#Field
private String filename;
#Field("full_text")
private String fullText;
//Getters and setters omitted
...
}
When a search and the SolrRepository is called (e.g. BooksService.findBookText(query, pageable);) I get back these objects.
However, in my REST response I only see the "content". I would like to be able to add the "highlighted" object to the REST response. It just appears that HATEOAS is only sending the information in the "content" object (see below for the object).
{
"_embedded" : {
"solrBooks" : [ {
"filename" : "ABookName",
"fullText" : "ABook Text"
} ]
},
"_links" : {
"first" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
},
"self" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook"
},
"next" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
},
"last" : {
"href" : "http://localhost:8080/booksCustom/search?q=ABook&page=0&size=20"
}
},
"page" : {
"size" : 1,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
Just so you can get a full picture, this is the repository that is backing the BooksService. All the service does is call this SolrCrudRepository method.
public interface SolrBooksRepository extends SolrCrudRepository<Books, String> {
#Highlight(prefix = "<highlight>", postfix = "</highlight>", fragsize = 20, snipplets = 3)
HighlightPage<SolrTestDocuments> findBookText(#Param("fullText") String fullText, Pageable pageable);
}
Ok, here is how I did it:
I wrote mine HighlightPagedResources
public class HighlightPagedResources<R,T> extends PagedResources<R> {
private List<HighlightEntry<T>> phrases;
public HighlightPagedResources(Collection<R> content, PageMetadata metadata, List<HighlightEntry<T>> highlightPhrases, Link... links) {
super(content, metadata, links);
this.phrases = highlightPhrases;
}
#JsonProperty("highlighting")
public List<HighlightEntry<T>> getHighlightedPhrases() {
return phrases;
}
}
and HighlightPagedResourcesAssembler:
public class HighlightPagedResourcesAssembler<T> extends PagedResourcesAssembler<T> {
public HighlightPagedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
super(resolver, baseUri);
}
public <R extends ResourceSupport> HighlightPagedResources<R,T> toResource(HighlightPage<T> page, ResourceAssembler<T, R> assembler) {
final PagedResources<R> rs = super.toResource(page, assembler);
final Link[] links = new Link[rs.getLinks().size()];
return new HighlightPagedResources<R, T>(rs.getContent(), rs.getMetadata(), page.getHighlighted(), rs.getLinks().toArray(links));
}
}
I had to add to my spring RepositoryRestMvcConfiguration.java:
#Primary
#Bean
public HighlightPagedResourcesAssembler solrPagedResourcesAssembler() {
return new HighlightPagedResourcesAssembler<Object>(pageableResolver(), null);
}
In cotroller I had to change PagedResourcesAssembler for newly implemented one and also use new HighlightPagedResources in request method:
#Autowired
private HighlightPagedResourcesAssembler<Object> highlightPagedResourcesAssembler;
#RequestMapping(value = "/conversations/search", method = POST)
public HighlightPagedResources<PersistentEntityResource, Object> findAll(
#RequestBody ConversationSearch search,
#SortDefault(sort = FIELD_LATEST_SEGMENT_START_DATE_TIME, direction = DESC) Pageable pageable,
PersistentEntityResourceAssembler assembler) {
HighlightPage page = conversationRepository.findByConversationSearch(search, pageable);
return highlightPagedResourcesAssembler.toResource(page, assembler);
}
RESULT:
{
"_embedded": {
"conversations": [
..our stuff..
]
},
"_links": {
...as you know them...
},
"page": {
"size": 1,
"totalElements": 25,
"totalPages": 25,
"number": 0
},
"highlighting": [
{
"entity": {
"conversationId": "a2127d01-747e-4312-b230-01c63dacac5a",
...
},
"highlights": [
{
"field": {
"name": "textBody"
},
"snipplets": [
"Additional XXX License for YYY Servers DCL-2016-PO0422 \n  \n<em>hi</em> bodgan \n  \nwe urgently need the",
"Additional XXX License for YYY Servers DCL-2016-PO0422\n \n<em>hi</em> bodgan\n \nwe urgently need the permanent"
]
}
]
}
]
}
I was using Page<Books> instead of HighlightPage to create the response page. Page obviously doesn't contain content which was causing the highlighted portion to be truncated. I ended up creating a new page based off of HighlightPage and returning that as my result instead of Page.
#RepositoryRestController
#RequestMapping(value = "/booksCustom")
public class BooksController extends ResourceSupport {
#Autowired
public BooksService booksService;
#Autowired
private PagedResourcesAssembler<Books> booksAssembler;
#RequestMapping("/search")
public HttpEntity<PagedResources<Resource<HighlightPage>>> search(#RequestParam(value = "q", required = false) String query, #PageableDefault(page = 0, size = 20) Pageable pageable) {
HighlightPage solrBookResult = booksService.findBookText(query, pageable);
Page<Books> highlightedPages = new PageImpl(solrBookResult.getHighlighted(), pageable, solrBookResult.getTotalElements());
return new ResponseEntity<PagedResources<Resource<HighlightPage>>>(booksAssembler.toResource(highlightedPages), HttpStatus.OK);
}
Probably a better way of doing this, but I couldn't find anything that would do what I wanted it to do without having a change a ton of code. Hope this helps!

href link retrieves a not paginated json - spring data rest jpa

I've started working on a REST API using Spring. I'm using the tutorial project gs-accessing-data-rest-initial, which is easy to dowload via Spring Tool Suite, in order to get some stuff working as soon as possible.
I've exposed two related entities (aplicacion and registros_app), using PagingAndSortingRepository and annotated both with #RepositoryRestResource, which enables me to expose entities correctly. The result I'm getting when I query on aplicacion is
**GET http://localhost:8090/aplicacion**
{
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion/{?page,size,sort}",
"templated" : true
}
},
"_embedded" : {
"aplicacion" : [ {
"nombre" : "app1",
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion/2"
},
"registrosApp" : {
"href" : "http://localhost:8090/aplicacion/2/registrosApp"
},
"tipoRegistrosApp" : {
"href" : "http://localhost:8090/aplicacion/2/tipoRegistrosApp"
}
}
}, {
"nombre" : "app2",
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion/1"
},
"registrosApp" : {
"href" : "http://localhost:8090/aplicacion/1/registrosApp"
},
"tipoRegistrosApp" : {
"href" : "http://localhost:8090/aplicacion/1/tipoRegistrosApp"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 2,
"totalPages" : 1,
"number" : 0
}
}
Which is exactly what I've expected to obtain. So, I was expecting to get the same when I navigate to registrosApp, in terms of pagination; however, when I perform a get on any registrosApp link, what I retrieve from the query is
**GET http://localhost:8090/aplicacion/2/registrosApp**
{
"_embedded" : {
"registrosapp" : [ {
"datos" : "{\"FechaInicio\":\"2014-09-16 18:08:44\",\"UsoMemoria\":\"UsedMemory:3 FreeMemory:491 Total Memory:495 Max Memory:989 \",\"InfoPool\":\"Active: 2\"}",
"fecha_hora" : "2014-09-17T14:04:07.000+0000",
"codTipoRegistro" : 1,
"_links" : {
"self" : {
"href" : "http://localhost:8090/registrosApp/605"
},
"aplicacion" : {
"href" : "http://localhost:8090/registrosApp/605/aplicacion"
}
}
},{
"datos" : "{\"FechaInicio\":\"2014-09-16 18:08:44\",\"UsoMemoria\":\"UsedMemory:3 FreeMemory:491 Total Memory:495 Max Memory:989 \",\"InfoPool\":\"Active: 2\"}",
"fecha_hora" : "2014-09-17T14:04:07.000+0000",
"codTipoRegistro" : 1,
"_links" : {
"self" : {
"href" : "http://localhost:8090/registrosApp/667"
},
"aplicacion" : {
"href" : "http://localhost:8090/registrosApp/667/aplicacion"
}
}
} ]
}
}
Which is not actually paginated. I need to get a paginated json when I navigate across links because registrosApp table grows very quickly. ¿What can I do about it?
Here is the code for my registrosApp and aplicacion repository
#RepositoryRestResource(collectionResourceRel = "registrosapp", path = "registrosApp")
public interface RegistrosAppRepository extends PagingAndSortingRepository<RegistrosApp, Long> {
}
#RepositoryRestResource(collectionResourceRel = "aplicacion", path = "aplicacion")
public interface AplicacionRepository extends PagingAndSortingRepository<Aplicacion, Long> {
//List<Person> findByLastName(#Param("name") String name);
}
And those are the entities I've defined
#Entity
#Table(name = "registros_app")
public class RegistrosApp {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long idRegistrosApp;
private String datos;
private Date fecha_hora;
private long codTipoRegistro;
public long getCodTipoRegistro() {
return codTipoRegistro;
}
public void setCodTipoRegistro(long codTipoRegistro) {
this.codTipoRegistro = codTipoRegistro;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "idAplicacion", nullable = false, insertable = false, updatable = false)
Aplicacion aplicacion;
// private long idAplicacion;
/*
* public long getRegistros_app() { return idAplicacion; }
*
* public void setRegistros_app(long registros_app) { this.idAplicacion =
* registros_app; }
*/
public String getDatos() {
return datos;
}
public void setDatos(String datos) {
this.datos = datos;
}
public Date getFecha_hora() {
return fecha_hora;
}
public void setFecha_hora(Date fecha_hora) {
this.fecha_hora = fecha_hora;
}
}
#Entity
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Aplicacion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long aplicacionId;
private String nombre;
//relaciones uno a varios
//relacion con la tabla registros_app
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "idAplicacion", nullable = false)
private Set<RegistrosApp> registrosApp = null;
//relacion con la tabla tipo_registro_app
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "idApp", nullable = false)
private Set<TipoRegistrosApp> tipoRegistrosApp = null;
public Set<TipoRegistrosApp> getTipoRegistrosApp() {
return tipoRegistrosApp;
}
public void setTipoRegistrosApp(Set<TipoRegistrosApp> tipoRegistrosApp) {
this.tipoRegistrosApp = tipoRegistrosApp;
}
#JsonProperty
public Set<RegistrosApp> getRegistrosApp() {
return registrosApp;
}
/**
* Sets list of <code>Address</code>es.
*/
public void setRegistrosApp(Set<RegistrosApp> rapps) {
this.registrosApp= rapps;
}
#JsonProperty
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
You can notice that I have a #onetomany annotation between aplicacion and registrosapp in my entities.
TL;DR When I query directly on registrosapp I get a paginated result as I expect. The problem here is when I navigate between related entities, I'm not getting the pagination information I need. ¿What can I do in order to get pagination when I navigate across entities? Any help with this will be truly appreciated. Thanks in advance.
I will answer myself in order to get this question useful for someone else who is struggling with this problem. This answer is closely related to - Spring Data Rest Pageable Child Collection -
What I've done is to set a method within RegistrosAppRepository, so it stays like this
#RepositoryRestResource(collectionResourceRel = "registrosapp", path = "registrosApp")
public interface RegistrosAppRepository extends PagingAndSortingRepository<RegistrosApp, Long> {
#RestResource(path = "byAplicacion", rel = "byAplicacion")
public Page<RegistrosApp> findByAplicacion(#Param("aplicacion_id") Aplicacion aplicacion, Pageable p);
}
Then I hide the link to registrosApp which appears in aplicacion, by setting the annotation #RestResource(exported=false) before the Set of registrosApp. So the aplicacion entity stays like this
#Entity
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Aplicacion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long aplicacionId;
private String nombre;
//relaciones uno a varios
//relacion con la tabla registros_app
#RestResource(exported=false)
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "idAplicacion", nullable = false)
private Set<RegistrosApp> registrosApp = null;
//relacion con la tabla tipo_registro_app
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "idApp", nullable = false)
private Set<TipoRegistrosApp> tipoRegistrosApp = null;
public Set<TipoRegistrosApp> getTipoRegistrosApp() {
return tipoRegistrosApp;
}
public void setTipoRegistrosApp(Set<TipoRegistrosApp> tipoRegistrosApp) {
this.tipoRegistrosApp = tipoRegistrosApp;
}
#JsonProperty
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
finally, I'm able to navigate between those entities this way:
**GET http://localhost:8090/registrosApp/search/byAplicacion?aplicacion_id=2&page=1&size=1**
{
"_links" : {
"next" : {
"href" : "http://localhost:8090/registrosApp/search/byAplicacion?aplicacion_id=2&page=2&size=1"
},
"prev" : {
"href" : "http://localhost:8090/registrosApp/search/byAplicacion?aplicacion_id=2&page=0&size=1"
},
"self" : {
"href" : "http://localhost:8090/registrosApp/search/byAplicacion?aplicacion_id=2&page=1&size=1{&sort}",
"templated" : true
}
},
"_embedded" : {
"registrosapp" : [ {
"datos" : "{\"FechaInicio\":\"2014-09-16 18:08:44\",\"UsoMemoria\":\"UsedMemory:2 FreeMemory:492 Total Memory:495 Max Memory:989 \",\"InfoPool\":\"Active: 2\"}",
"fecha_hora" : "2014-09-17T14:04:07.000+0000",
"codTipoRegistro" : 1,
"_links" : {
"self" : {
"href" : "http://localhost:8090/registrosApp/593"
},
"aplicacion" : {
"href" : "http://localhost:8090/registrosApp/593/aplicacion"
}
}
} ]
},
"page" : {
"size" : 1,
"totalElements" : 56,
"totalPages" : 56,
"number" : 1
}
}
and the link in aplicacion doesn't show the registrosApp link whithin the json:
**GET http://localhost:8090/aplicacion**
{
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion{?page,size,sort}",
"templated" : true
}
},
"_embedded" : {
"aplicacion" : [ {
"nombre" : "app1",
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion/2"
},
"tipoRegistrosApp" : {
"href" : "http://localhost:8090/aplicacion/2/tipoRegistrosApp"
},
"aplicacion" : {
"href" : "http://localhost:8090/aplicacion/2/aplicacion"
}
}
}, {
"nombre" : "app2",
"_links" : {
"self" : {
"href" : "http://localhost:8090/aplicacion/1"
},
"tipoRegistrosApp" : {
"href" : "http://localhost:8090/aplicacion/1/tipoRegistrosApp"
},
"aplicacion" : {
"href" : "http://localhost:8090/aplicacion/1/aplicacion"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 2,
"totalPages" : 1,
"number" : 0
}
}

Resources