How to query elasticsearch from spring with LocalDate - spring

Having this SearchQuery:
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.rangeQuery("updateTime").gte(LocalDate.now())).build();
final List<ActivityValue> listOf = elasticsearchTemplate.queryForList(searchQuery, ActivityValue.class);
With Entity ActivityValue:
#Document(indexName = "tracking1", type = "activity")
public class ActivityValue {
#Id
private String id;
#Field(type = FieldType.Date, index = false, store = true, format = DateFormat.custom, pattern = "yyyy-MM-dd")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate updateTime;
#Field(type = FieldType.Object, includeInParent = true)
private Vendor vendor;
#Field(type = FieldType.Object, includeInParent = true)
private QCriteria quality;
public ActivityValue() {
}
//setter and getter
}
If i run the query and try to receive the list i get following exception:
caused by: java.io.IOException: can not write type [class java.time.LocalDate]
The entity is stored before with the actual date as LocalDate.
I'm uncertain what is the best/easiest way to query elasticsearch and to resolve this error. Can anybody help?

final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.rangeQuery("updateTime").gte(LocalDate.now().toString())).build();
final List<ActivityValue> listOf = elasticsearchTemplate.queryForList(searchQuery, ActivityValue.class);
I solved this problem by using LocalDate.now().toString() instead of LocalDate.now()

Related

Searching multiple fields with multiple queries in Spring Elasticsearch

I am using Spring Elasticsearch. This is my java class :
#Entity
#Document(indexName = "shopindex")
public class Shop implements Serializable {
private #Id #GeneratedValue(strategy = GenerationType.IDENTITY) Long id;
private String imagePath;
#Field(type = FieldType.Text, name = "name")
private String name;
#Field(type = FieldType.Text, name = "description")
private String description;
#Field(type = FieldType.Text, name = "address")
private String address;
#Field(type = FieldType.Text, name = "locality")
private String locality;
#Field(type = FieldType.Keyword, name = "city")
private String city;
#Field(type = FieldType.Keyword, name = "state")
private String state;
private String timing;
#Field(type = FieldType.Nested, includeInParent = true)
private ArrayList<Listing> listings;
Shop () {}
}
I want to have two query strings, location and query. I want query to search through fields name, description, and listing and location to search through fields address, location, city, and state. I am using this query for search but I am getting exception :
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQuery.must(QueryBuilders.multiMatchQuery(location, "address", "locality", "city", "state"))
.must(boolQuery.should(QueryBuilders.multiMatchQuery(query, "name", "description"))
.should(QueryBuilders
.nestedQuery("listings",
QueryBuilders.multiMatchQuery(query, "listings.name", "listings.description"),ScoreMode.Avg))))
.build();
Iterable<Shop> itr = searchRepository.search(searchQuery);

Error while indexing in Hibernate Search - Could not get property value

I am using Hibernate Search with Spring Boot to create a searchable rest api. Trying to POST an instance of "Training", I receive the following stack traces. None of the two are very insightful to me which is why I am reaching out for help.
Stack trace:
https://pastebin.com/pmurg1N3
It appears to me that it is trying to index a null entity!? How can that happen? Any ideas?
The entity:
#Entity #Getter #Setter #NoArgsConstructor
#ToString(onlyExplicitlyIncluded = true)
#Audited #Indexed(index = "Training")
#AnalyzerDef(name = "ngram",
tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class ),
filters = {
#TokenFilterDef(factory = StandardFilterFactory.class),
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = StopFilterFactory.class),
#TokenFilterDef(factory = NGramFilterFactory.class,
params = {
#Parameter(name = "minGramSize", value = "2"),
}
)
}
)
#Analyzer(definition = "ngram")
public class Training implements BaseEntity<Long>, OwnedEntity {
#Id
#GeneratedValue
#ToString.Include
private Long id;
#NotNull
#RestResourceMapper(context = RestResourceContext.IDENTITY, path = "/companies/{id}")
#JsonProperty(access = Access.WRITE_ONLY)
#JsonDeserialize(using = RestResourceURLSerializer.class)
private Long owner;
#NotNull
#Field(index = Index.YES, analyze = Analyze.YES, store = Store.YES)
private String name;
#Column(length = 10000)
private String goals;
#Column(length = 10000)
private String description;
#Enumerated(EnumType.STRING)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO, bridge=#FieldBridge(impl=EnumBridge.class))
private Audience audience;
#Enumerated(EnumType.STRING)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO, bridge=#FieldBridge(impl=EnumBridge.class))
private Level level;
#ManyToMany
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#NotNull #Size(min = 1)
#IndexedEmbedded
private Set<ProductVersion> versions;
#NotNull
private Boolean enabled = false;
#NotNull
#Min(1)
#IndexedEmbedded
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO)
#NumericField
private Integer maxStudents;
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
private Agenda agenda;
#NotNull
#Min(1)
#Field(index = Index.YES, store = Store.YES, analyze = Analyze.NO)
#NumericField
private Integer durationDays;
#IndexedEmbedded
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToMany(cascade = CascadeType.PERSIST)
private Set<Tag> tags = new HashSet<>();
I'd say either your versions collection or your tags collection contains null objects, which is generally not something we expect in a Hibernate ORM association, and apparently not something Hibernate Search expects either.
Can you check that in debug mode?

spring-data-elasticsearch - #Field mapping type ignored when #Id annotated on the same field

#Document(indexName = "opportunity_data", type = "opportunities", createIndex = false)
#Setting(settingPath = "/search/settings.json")
#Data
#Accessors(chain = true)
#JsonIgnoreProperties(value = {"id"}, allowGetters = true, allowSetters = false)
public class OpportunityVo extends AbstractGenericVo<Opportunity> {
#Id
#Field(type = FieldType.Long)
private Long opportunityId;
#Field(type = FieldType.Long)
private Long prospectId;
#Field(type = FieldType.Text)
private String prospectName;
}
mapping to keyword snapshot
the opportunityId above is mapped to keyword other than the long type. Anyone knows how to map the opportunityId to long type when #Id annotated at the same time since I'd like to do stats metrics aggregation on this property?
Stats aggregation can be only applied to numerics. Change the mappings works fine. But #Id will be mapped to keyword , still no luck.

Spring data elastic search with sort not working

I am using elastic search db and spring data.
Following is my document in which I am searching and that search result should return sorted and pageable list.
#Data
#EqualsAndHashCode(exclude = { "id" })
#Document(indexName = "job", type = "job")
public class JobDocument implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#Field(type = FieldType.String, index = FieldIndex.analyzed, store = true)
private String name;
#Field(type = FieldType.String, index = FieldIndex.analyzed, store = true)
private String desc;
#Field(type = FieldType.Date, store = true)
private LocalDateTime dateTime; // java.Time
}
I am searching text in desc field and want to sort it by dateTime field. My search service does following,
BoolQueryBuilder queryBuilder = boolQuery();
queryBuilder.must(QueryBuilders.queryStringQuery("*" + desc + "*").lenient(true).field("desc"));
NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
searchQuery.withPageable(pageable);
searchQuery.withSort(SortBuilders.fieldSort("dateTime")
.order(SortOrder.DESC))
searchQuery.withQuery(queryBuilder);
Page<JobDocument> jobs = jobRepo.search(searchQuery.build());
Following is my repo,
public interface JobDAO extends ElasticsearchRepository<JobDocument, String>
{}
Search and pageable is working but Sorting is not working.
Am I missing something?
Their is a sort option available in pageable object.Instead of using the sort and pagination separately, you can add them into a single object PageRequest.
BoolQueryBuilder queryBuilder = boolQuery();
queryBuilder.must(QueryBuilders.queryStringQuery("*" + desc + "*").lenient(true).field("desc"));
NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
searchQuery.withPageable(new PageRequest(0, 10, Sort.Direction.DESC, "dateTime"));
searchQuery.withQuery(queryBuilder);

Elastic search case insensitive

I have the following annotation based elastic search configuration, I've set the index not to be analyzed because I don't want these fields to be tokenized:
#Document(indexName = "abc", type = "efg")
public class ResourceElasticSearch {
#Id
private String id;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String name;
#Field(type = FieldType.String, store = true)
private List<String> tags = new ArrayList<>();
#Field(type = FieldType.String)
private String clientId;
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
private String virtualPath;
#Field(type = FieldType.Date)
private Date lastModifiedTime;
#Field(type = FieldType.Date)
private Date lastQueryTime;
#Field(type = FieldType.String)
private String modificationId;
#Field(type = FieldType.String)
private String realPath;
#Field(type = FieldType.String)
private String extension;
#Field(type = FieldType.String)
private ResourceType type;
Is it possible by using annotations to make the searches on the name, virtualPath and tags to be case-insensitive?
The search looks like this, search by wildcard is required:
private QueryBuilder getQueryBuilderForSearch(SearchCriteria criteria) {
String virtualPath = criteria.getPath();
return boolQuery()
.must(wildcardQuery("virtualPath", virtualPath))
.must(wildcardQuery("name", criteria.getName()));
}
Not really possible what you want to do and it's not about Spring Data configuration, it's about Elasticsearch itself: you indexed data as not_analyzed and it will stay that way.
Also, if you wanted case insensitive data I suggest indexing with keyword analyzer combined with a lowercase token filter.
I've found something based on Andrei Stefan's suggestion which has a similar result to using the annotations:
#Bean
public Client client() throws IOException {
TransportClient client = new TransportClient();
TransportAddress address = new InetSocketTransportAddress(env.getProperty("elasticsearch.host"), Integer.parseInt(env.getProperty("elasticsearch.port")));
client.addTransportAddress(address);
XContentBuilder settingsBuilder = XContentFactory.jsonBuilder()
.startObject()
.startObject("analysis")
.startObject("analyzer")
.startObject("keyword")
.field("tokenizer", "keyword")
.array("filter", "lowercase")
.endObject()
.endObject()
.endObject()
.endObject();
if (!client.admin().indices().prepareExists("abc").execute().actionGet().isExists()) {
client.admin().indices().prepareCreate("abc").setSettings(settingsBuilder).get();
}
return client;
}
You can add #Setting, which consumes file path, after #Document, settings file should contain json like this:
{"analysis":{"analyzer":{"case_insensitive":{"type":"custom","tokenizer":"whitespace","char_filter":["html_strip"],"filter":["lowercase","asciifolding"]}}}}
and field annotation with analyzer #Field(type = FieldType.Keyword, analyzer = "case_insensitive")

Resources