ElasticSearch7.15.2 Date range search using Java Api - elasticsearch

I am trying to search the documents using a date range filter along with some keywords. I couldn't find any java api samples to do so. Could you please help me.
Normal search works with the following code: But I am after a date range filter search.
SearchResponse<JsonNode> search = esClient.search(s -> s
.index(index)
.query(q -> q
.match(f -> f.field(searchKey).query(searchValue).fuzziness(fuzziness).operator(Operator.And))),
JsonNode.class);
But not sure how to create a range query in the similar manner. The following rest query works but unable to convert it to the java api.
GET: https://localhost:9200/index_01/_search
{
"query": {
"range": {
"date_at": {
"gte": "2020-06-10T00:00:00Z",
"lte": "2021-06-10T23:59:59Z",
"boost": 2.0
}
}
}
}
Version details:
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.15.2</version>
</dependency>

Following code works. Please suggest if there is any better option.
final String json = "{ \"column_date\": {\"gte\": \"2018-02-02T15:08:28Z\", \"lte\": \"2021-06-23T11:35:01.120Z\"}}";
final JsonpMapper mapper = new JsonbJsonpMapper();
final JsonParser parser = mapper.jsonProvider().createParser(new StringReader(json));
final JsonData data = JsonData.from(parser, mapper);
final JsonValue value = data.toJson(mapper);
SearchResponse<JsonNode> search = client.search(s -> s
.index(index)
.query( q -> q.range(value)), JsonNode.class, userDn);

Related

Elasticsearch Jest client add condition to json query

I am using Elasticsearch 6.3 with Jest client 6.3 (Java API)
Search search = new Search.Builder(jsonQueryString)
.addIndex("SOME_INDEX")
.build();
SearchResult result = jestClient.execute(search);
And this is my sample JSON query
{
"query": {
"bool" : {
"filter": {
"match" :{
"someField" : "some value"
}
}
}
}
}
The JSON query string is accepted as a POST request body and then passed to the Jest client. Before I can execute the json query on the Jest client, I need to add conditions to the query for e.g.
{
"query": {
"bool" : {
"filter": {
"match" :{
"someField" : "some value"
}
}
},
"must": {
"match" :{
"systemField" : "pre-defined value"
}
}
}
}
}
Is there an API that allows to parse the JSON query and add conditions to it before it can be executed on Jest client? The JSON query can be any query supported by Query DSL and not necessarily contain bool condition. I need to add a pre-defined condition to the query. I appreciate any help on this. Thanks very much.
There is no out of the box Elasticsearch or Jest API to achieve the above, the workaround I implemented is using Jackson ObjectMapper
// convert the search request body into object node
ObjectNode searchRequestNode = objectMapper.readValue(queryString, ObjectNode.class);
// extract the query
String query = searchRequestNode.get("query").toString();
// wrap the original query and add conditions
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.wrapperQuery(query));
boolQueryBuilder.filter(QueryBuilders.termsQuery("fieldA", listOfValues));
boolQueryBuilder.filter(QueryBuilders.termQuery("fieldB", value));
// convert querybuilder to json query string
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(queryBuilder);
String queryWithFilters = searchSourceBuilder.toString();
// convert json string to object node
ObjectNode queryNode = objectMapper.readValue(queryWithFilters, ObjectNode.class);
// replace original query with the new query containing added conditions
searchRequestNode.set("query", queryNode.get("query"));
String finalSearchRequestWithOwnFilters = searchRequestNode.toString();

How can I make a SearchRequest in ES 6.0 from a json string using High level REST Client in Scala

I'm migrating from ES 1.7 to 6.0 and I'm trying to figure out a way to perform a search request passing a plain query to the High Level REST Java client. To keep queries easy to read (and maintain), we want to use a Json string instead of building them with the SearchSourceBuilder. This was possible before but I'm not finding a nice way to do it now.
Example query:
{
"filter": {
"bool": {
"must": [
{
"term": {
"status": "Success"
}
},
{
"term": {
"type": "someType"
}
},
{
"range": {
"endDateTime": {
"lte": "someDateTime"
}
}
}
]
}
}
}
Example code:
val searchRequest = buildSearchRequest(indexName, indexType, query)
val searchResponse = restHighLevelClient.search(searchRequest)
I know it's possible to send the query using the LowLevelClient but it returns a Response and not a SearchResponse. Has anyone faced this issue before? Any workaround or solution?
I'm a little bit late, but I have a close solution for your problem.
Firstly, you need to build your query with something like this:
private QueryBuilder buildQuery(MyObject object) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
QueryBuilder statusTerm = QueryBuilders.termQuery("status", object.getStatus());
QueryBuilder typeTerm = QueryBuilders.termQuery("type", object.getType());
QueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(statusTerm)
.must(typeTerm);
queryBuilder.should(boolQuery);
return queryBuilder;
}
Then, you can perform your search and return SearchResponse:
private SearchResponse search(QueryBuilder query) throws IOException {
SearchRequest request = new SearchRequest(elasticSearchClient.getElasticsearchClientProperties().getIndices());
SearchSourceBuilder searchRequestBuilder = request.source();
searchRequestBuilder.query(query);
//SearchSourcebuilder allows you to set your query into your request
return this.elasticSearchClient.search(request);
}
Finally, can have a method that calls both:
public void processSearch(MyObject object) {
QueryBuilder query = buildQuery(object);
SearchResponse response = search(query);
List<FoundObjects> objects = parseResponse(response);
}
I hope this could help. Good luck!

ElasticSearch NEST Executing raw Query DSL

I'm trying to create the simplest proxy possible in an API to execute searches on ElasticSearch nodes. The only reason for the proxy to be there is to "hide" the credentials and abstract ES from the API endpoint.
Using Nest.ElasticClient, is there a way to execute a raw string query?
Example query that is valid in vanilla ES:
{
"query": {
"fuzzy": { "title": "potato" }
}
}
In my API, I tried Deserializing the raw string into a SearchRequest, but it fails. I'm assuming it cannot deserialize the field:
var req = m_ElasticClient.Serializer.Deserialize<SearchRequest>(p_RequestBody);
var res = m_ElasticClient.Search<T>(req);
return m_ElasticClient.Serializer.SerializeToString(res);
System.InvalidCastException: Invalid cast from 'System.String' to 'Newtonsoft.Json.Linq.JObject'.
Is there a way to just forward the raw string query to ES and return the string response? I tried using the LowLevel.Search method without luck.
NEST does not support deserializing the short form of "field_name" : "your_value" of the Elasticsearch Query DSL, but it does support the long form "field_name" : { "value" : "your_value" }, so the following works
var client = new ElasticClient();
var json = #"{
""query"": {
""fuzzy"": {
""title"": {
""value"": ""potato""
}
}
}
}";
SearchRequest searchRequest;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
searchRequest = client.Serializer.Deserialize<SearchRequest>(stream);
}
As Rob has answered, NEST also supports supplying a raw json string as a query
Yes, you can do this with NEST, check out the following
var searchResponse = client.Search<object>(s => s
.Type("type").Query(q => q.Raw(#"{""match_all"":{}}")));
Hope that helps.

Spring Elasticsearch Aggregation Filtering Not Working

I'm trying to query pricing stats on products I am recording in my Elasticsearch Database by product number. The pricing may be for new, used or refurbished products, so I wish to filter on condition. The condition filter works as a JSON query in Marvel returning stats based on two price documents with condition new.
When I try to do similar using the Java API, I am getting stats based on 4 documents that includes 2 new and 2 refurbished.
Could anyone please identify what I am doing wrong in the Java code below?
Thanks.
Here's the working JSON Query:
GET /stats/price/_search
{
"query": {
"match_phrase": {"mpc": "MGTX2LL/A"}
},
"size": 0,
"aggs" : {
"low_price_stats" : {
"filter": {
"term" : { "condition" : "new"}
},
"aggs" : {
"price_stats" : { "extended_stats" : { "field" : "price" } }
}
}
}
}
And the problematic Java:
public Aggregations aggByManufacturerPartNumber(String mpn) {
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("stats")
.withTypes("price")
.withQuery(termQuery("mpn", mpn))
.withFilter(
FilterBuilders.termFilter("condition", "New")
)
.addAggregation(AggregationBuilders.extendedStats("stats_agg").field("price"))
.build();
Aggregations aggregations = elasticsearchTemplate.query(searchQuery, new ResultsExtractor<Aggregations>() {
#Override
public Aggregations extract(SearchResponse response) {
return response.getAggregations();
}
});
return aggregations;
}
In your Java code you're only building the price_stats sub-aggregation without its parent filter aggregation. The call to withFilter will create a filter at the query level, not at the aggregation level. The correct Java code that matches your JSON query would be like this:
// build top-level filter aggregation
FilterAggregationBuilder lowPriceStatsAgg = AggregationBuilders.filter("low_price_stats")
.filter(FilterBuilders.termFilter("condition", "new"));
// build extended stats sub-aggregation
lowPriceStatsAgg.subAggregation(AggregationBuilders.extendedStats("stats_agg").field("price"));
// build query
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("stats")
.withTypes("price")
.withQuery(termQuery("mpn", mpn))
.addAggregation(lowPriceStatsAgg)
.build();
// then get the results
Aggregations aggs = response.getAggregations();
Filter lowPriceStats = aggs.get("low_price_stats");
ExtendedStats statsAgg = lowPriceStats.get("stats_agg");
Besides, also note that in your JSON query you have a match_phrase on the mpc field while in your Java code you have a term query on the mpn field. So you probably need to fix that, too, but the above code fixes the aggregation part only.

Obtaining string query (JSON) from SearchQuery object

For debugging purposes, I need to know what query spring-data-elasticsearch is sending to the ElasticSearch cluster. I have tried to call the toString method on the SearchQuery object, and doesn't return what I need.
What I am doing in Java (using spring-data-elasticsearch) is:
private FilterBuilder getFilterBuilder(String id) {
return orFilter(
termFilter("yaddayaddayadda.id", id),
termFilter("blahblahblah.id", id)
);
}
SearchQuery sq = NativeSearchQueryBuilder()
.withQuery(new MatchAllQuery())
.withFilter(fb)
.build();
And I expect to return something like this plain query executed in ES cluster REST API is returning:
{
"query": {
"filtered": {
"filter": {
"or": [
{
"term": {
"yaddayaddayadda.id": "9"
}
},
{
"term": {
"blahblahblah.id": "9"
}
}
]
}
}
}
}
Thanks in advance!
One way to achieve this is to log the queries on the ES/server-side into the slowlog file. Open your elasticsearch.yml config file and towards the bottom uncomment/edit the two lines below:
...
index.search.slowlog.threshold.query.info: 1ms
...
index.search.slowlog.threshold.fetch.info: 1ms
...
The advantage of this solution is that whatever client technology you're using to query your ES server (Spring Data, Ruby, Browser, Javascript, etc), you'll be able to dump and debug your queries in a single location.
SearchQuery Interface has a method getQuery() and getFilter() to get the information you need.
System.out.println(searchQuery.getQuery());
System.out.println(searchQuery.getFilter());
Hope this helps.
When using SearchRequest or SearchSourceBuilder, calling .toString() method on their instance will get you actual JSON query:
SearchRequest searchRequest = new SearchRequest("index");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// building the query
// ...
searchSourceBuilder.query(query);
searchRequest.source(searchSourceBuilder);
System.out.println(searchSourceBuilder.toString()); // prints json query
System.out.println(searchRequest.toString()); // prints json query + other information

Resources