Skip to content
KK

Kerim Kadusic

1 article

October 21, 2022

Converting logical expressions into ElasticSearch’s Java API using QueryBuilders class

Software Development

Converting logical expressions into ElasticSearch’s Java API using QueryBuilders class

In this Tech Bite, we will see how to use ElasticSearch's Java API to perform some of the common Elasticsearch queries. Elasticsearch query types The queries in Elasticsearch can be broadly classified into leaf and compound queries. Leaf queries search for specific values in a certain field (or fields) and can be used independently. Some of these queries include match, term, and range queries. Compound queries use the combination of leaf or compound queries. Essentially, they combine multiple queries to achieve their target results. Basic search query The “match” query is one of the most basic and commonly used queries in Elasticsearch and functions as a full-text query. We can use this query to search for text, numbers, or boolean values. GET index/_search { "query": { "match" : { "name" : "Pizza" } } } The QueryBuilders class provides a variety of static methods used as dynamic matchers to find specific entries in the cluster. Converting the above Elasticsearch query in JSON format to a QueryBuilder object will give the following: final QueryBuilder qb = QueryBuilders.matchQuery("name", "Pizza"); Both the Elasticsearch query and the query written using Querybuilders class will return all the documents with the name “Pizza.” The match query also analyzes provided search term before performing a search. Keep in mind that Elasticsearch will analyze the text field lowercase unless you define a custom mapping. To search for the exact term you provide, use the term query. To find the objects having values between the ranges of given values, we can use the rangeQuery parameter followed by the field and the conditions to check. final QueryBuilder rangeQuery = QueryBuilders .rangeQuery(field) .from(startDate) .to(endDate) .includeLower(false) .includeUpper(true); final QueryBuilder rangeQuery = QueryBuilders .rangeQuery(field) .gte(fromTimeInMillis) .lte(toTimeInMillis); Bool query for logical expressions The AND/OR/NOT operators can be used to fine-tune search queries to provide more relevant or specific results. Now, let’s convert a more complex logical expression into a QueryBuilder object. For the sake of simplicity and readability, this expression is divided into three parts. Figure 1. An example of logical expression Elasticsearch provides the facility for combining these queries using the bool query (one of the compound query clauses). GET index/_search { "query": { "bool": { "should": [ { "bool": { "must": [ { "term": { "field1": { "value": "value1" } } }, { "term": { "field2": { "value": "value2" } } } ] } }, { "bool": { "must": [ { "term": { "field1": { "value": "value3" } } } ], "must_not": [ { "term": { "field2": { "value": "value3" } } } ] } } ] } } } final BoolQueryBuilder first = QueryBuilders.boolQuery() .must(QueryBuilders.termQuery(field1, value1)) .must(QueryBuilders.termQuery(field2, value2)); final BoolQueryBuilder second = QueryBuilders.boolQuery() .must(QueryBuilders.termQuery(field1, value3)) .mustNot(QueryBuilders.termQuery(field2, value3)); final BoolQueryBuilder filter = new BoolQueryBuilder() .should(first) .should(second); Table 1. Boolean operations with the Bool Query Fields analogy Using nested query with nested object To run a nested query, you must have an index that includes nested mapping. Elasticsearch has no concept of inner objects. The nested type is a specialized version of the object data type that allows arrays of objects to be indexed such that they can be queried independently of each other. Let’s say that this is the type of data we have stored on the Elasticsearch index: { "name": "pizza", "main_ingredient": { "id": 1, "name": "cheese", "weight": 200 }, "other_ingredients": [ {"id": 10, "name": "onion", "weight": 80}, {"id": 11, "name": "tomato", "weight": 100}, {"id": 12, "name": "olive", "weight": 70}, {"id": 13, "name": "pepperoni", "weight": 150}, {"id": 14, "name": "mushroom", "weight": 180} ] } We have to find recipes having cheese as the main ingredient (weight less than 200) with two other ingredients: tomato (weight less than 100) and mushroom (weight greater than 50). GET index/_search { "query": { "bool": { "must": [ { "match": { "main_ingredient.name": "cheese" } }, { "range": { "main_ingredient.weight": { "lte": 200 } } }, { "nested": { "path": "other_ingredients", "query": { "bool": { "must": [ { "match": { "other_ingredients.name": "tomato" } }, { "range": { "other_ingredients.weight": { "lte": 100 } } } ] } } } }, { "nested": { "path": "other_ingredients", "query": { "bool": { "must": [ { "match": { "other_ingredients.name": "mushroom" } }, { "range": { "other_ingredients.weight": { "gte": 50 } } } ] } } } } ] } } } The nested query searches nested field objects as if they were indexed as separate documents. final QueryBuilder firstNestedQuery = QueryBuilders.nestedQuery( "other_ingredients", QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("other_ingredients.name", "tomato")) .must(QueryBuilders.rangeQuery("other_ingredients.weight").lte(100)), ScoreMode.None ); final QueryBuilder secondNestedQuery = QueryBuilders.nestedQuery( "other_ingredients", QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("other_ingredients.name", "mushroom")) .must(QueryBuilders.rangeQuery("other_ingredients.weight").gte(50)), ScoreMode.None ); final QueryBuilder queryBuilder = QueryBuilders.boolQuery() .must(QueryBuilders.matchQuery("main_ingredient.name", "cheese")) .must(QueryBuilders.rangeQuery("main_ingredient.weight").lte(200)) .must(firstNestedQuery) .must(secondNestedQuery);   "Converting logical expressions into ElasticSearch’s Java API using QueryBuilders class" Tech Bite was brought to you by Kerim Kadušić, Software Engineer at Atlantbh. (more…)

Ready to Achieve More?

We’ll help you reach your goals quickly with an easy and straightforward process to kick off our collaboration. Here’s what happens next.

STEP 1

Discovery Call

Let’s chat to understand your company, project needs, and answer any questions along the way.

STEP 2

Free Consultation

Work closely with our experts to explore the right solutions for your business.

STEP 3

Collaboration Proposal

We'll recommend the best strategy for your goals, ensuring you get the most from our expertise.

STEP 4

30-Day Cancellation
Policy Contract

Spoiler: It’s Never Been Used

Enjoy peace of mind while we deliver excellence from day one—our track record speaks for itself.

Services you're interested in (Optional)