1

Database: Elasticsearch

Field Name: "tag"

Datatype: String

Question: How to perform a search in the tag field with a wildcard character?

I tried the following (in Kibana):

User Request: { "tag" : [ "Attendance", "Employee" ] }

POST test/_search
{
    "query":{
        "bool" : {
          "should" : [
            {"wildcard":{"tag.keyword": "*Attendance*" }},
            {"wildcard":{"tag.keyword": "*Employee*" }}
          ]
        }
    }
}

This worked successfully but I don't have an idea how to perform the same thing in spring boot.

I worked with following:

.should(wildcardQuery("tag.keyword", "Attendance"))

But this is just for a single field, I have a requirement for a dynamic field where user input should be different in size and value.

Can someone please guide me?

ben3000
  • 4,629
  • 6
  • 24
  • 43
Dhwanil Patel
  • 2,273
  • 1
  • 18
  • 28

1 Answers1

3

You could use something like this :

Rest Highlevel client Query :

SearchRequest searchRequest = new SearchRequest(<your-index-name>);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
 QueryBuilder query =  QueryBuilders.boolQuery().should(new WildcardQueryBuilder("tag.keyword", "*Attendance*"))
                    .should(new WildcardQueryBuilder("tag.keyword", "*Employee*"));
searchSourceBuilder.query(query);
searchRequest.source(searchSourceBuilder);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

Here client is the RestHighLevelClient bean that I have autowired in my class as follows :

@Autowired
private RestHighLevelClient client;

and this bean I have defined in my config class as :

@Bean(destroyMethod = "close")
public RestHighLevelClient client() {

        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http")));

        return client;

}

This would construct query in this form :

Query

    {
  "bool" : {
    "should" : [
      {
        "wildcard" : {
          "tag.keyword" : {
            "wildcard" : "*Attendance*",
            "boost" : 1.0
          }
        }
      },
      {
        "wildcard" : {
          "tag.keyword" : {
            "wildcard" : "*Employee*",
            "boost" : 1.0
          }
        }
      }
    ],
    "adjust_pure_negative" : true,
    "boost" : 1.0
  }
}

I have tested by creating index with same mapping as mentioned by you :

Documents indexed :

  "_source": {
            "tag": "Attendance1"
    }


    "_source": {
            "tag": "1Employee"
    }



      "_source": { "tag": "*Employee*" }


    { "tag" : [ "Attendance", "Employee" ] }

And when I search using above rest query , I got the following response:

Response

"hits": [
        {
            "_index": "test",
            "_type": "_doc",
            "_id": "4",
            "_score": 2.0,
            "_source": {
                "tag": [
                    "Attendance",
                    "Employee"
                ]
            }
        },
        {
            "_index": "test",
            "_type": "_doc",
            "_id": "1",
            "_score": 1.0,
            "_source": {
                "tag": "Attendance1"
            }
        },
        {
            "_index": "test",
            "_type": "_doc",
            "_id": "2",
            "_score": 1.0,
            "_source": {
                "tag": "*Employee*"
            }
        },
        {
            "_index": "test",
            "_type": "_doc",
            "_id": "3",
            "_score": 1.0,
            "_source": {
                "tag": "1Employee"
            }
        }
    ]

Not sure what is meant by this part of your question:

I have requirement for dynamic field where user input should be different in size and value.

But, I am assuming it means from user you may get different tag list and based on that you want to create query.

You can do something like this to achieve the same :

        // { "tag" : [ "Attendance", "Employee" ] } you can read this tag list though converter and pass it into list kind of data structure. Currently, I have created dummy list `tags` which has Attendance and Employee as items.
            List<String> tags = new ArrayList<>();
            tags.add("Attendance");
            tags.add("Employee");

            SearchRequest searchRequest = new SearchRequest(<your-index-name>);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            List<WildcardQueryBuilder> wildcards = new ArrayList<WildcardQueryBuilder>();

            for(String tag :  tags) {
                WildcardQueryBuilder wildcard =  new WildcardQueryBuilder("tag.keyword", "*" + tag + "*");
                wildcards.add(wildcard);

            }
            BoolQueryBuilder boolQuery = new BoolQueryBuilder();
            for(WildcardQueryBuilder wildcard : wildcards) {
                boolQuery.should(wildcard);
            }

            searchSourceBuilder.query(boolQuery);
            searchRequest.source(searchSourceBuilder);

            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
  • @Dhwanil Patel I am not quite sure what was the meaning of dynamic fields in it. If you can give example of same, I will update the answer. Thanks –  Apr 27 '20 at 14:22
  • Thanks so much for the answer. I'll check the answer after placing in my code and update you soon. I also tried to implement one solution slimier to last code (not exactly same) but not completed yet. – Dhwanil Patel Apr 27 '20 at 19:02
  • Dynamic Fields means user input for search tag. It should be one,two or more it depends on user. In above example he request for two tags attendance and employee. Suppose next time he request for attendance, employee and priority. like wise. – Dhwanil Patel Apr 27 '20 at 19:05
  • In my last part of answer, I have written rest query based on dynamic values .You can look into that and please let me know if you face any issue. Thanks –  Apr 28 '20 at 00:51
  • I'm checking your solution (last block). I have one doubt what is client variable in last line. ("client.search()") – Dhwanil Patel Apr 28 '20 at 05:11
  • 1
    I have updated my answer explaining the same. Basically client is RestHighLevelClient bean that I have autowired whcih is defined in my config class. –  Apr 28 '20 at 05:24
  • Yes i also got the same from here, https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-high-getting-started-initialization.html – Dhwanil Patel Apr 28 '20 at 05:51
  • No @PrernaGupta, I don't know what happens but i suppose there is some issue encountered related to shards. – Dhwanil Patel Apr 28 '20 at 12:59
  • thanks it's done. Actually i have lot many other requirement which bind and parallel working with it, So i have to manage builder for handle that scenario. And that's why it takes that much time. But at the end it's done thanks again. – Dhwanil Patel May 28 '20 at 07:48
  • @DhwanilPatel great to hear back from you and thanks for accepting the answer, could you also please upvote it as well , TIA :) –  May 28 '20 at 10:07