Elasticsearch match partial text in Java - java

I have field in elasticsearch "name" which contains for example:
"some text"
I would like to do query which match any partial of this text, for example "some", "so" or "xt"
I was trying do this by queryString:
public Page<View> search(String name, Pageable pageable) {
QueryBuilder queryBuilder = buildQueryFrom(name);
HighlightBuilder highlightBuilder = buildHighlig();
SearchRequestBuilder searchRequest = client.prepareSearch()
.setQuery(queryBuilder)
.highlighter(highlightBuilder)
.setFetchSource(null, new String[]{CV_OCR});
addPageable(searchRequest, pageable);
SearchResponse response = searchRequest.execute().actionGet();
return mapper.mapResponse(response, pageable).map(this::View);
}
private QueryBuilder buildQueryFrom(String text) {
BoolQueryBuilder queryBuilder = boolQuery();
queryBuilder.must(queryStringQuery("*" + text + "*").field("name"));
return queryBuilder;
}
but if I try to search for example "%" or another special character the results are different than my expectations. I was trying also use regex:
private QueryBuilder buildQueryFrom(String text) {
BoolQueryBuilder queryBuilder = boolQuery();
queryBuilder.must(regexpQuery("name", quoteStringAndSetCaseInsensitive(text).pattern()));
return queryBuilder;
}
private Pattern quoteStringAndSetCaseInsensitive(String value) {
return Pattern.compile(Pattern.quote(value), Pattern.CASE_INSENSITIVE);
}
but it didn't work. Is there any other possibility for searching like this?

Related

How to find documents with specific field contains string using RestHighLevelClient

So, I can make simple via url:
<elastic_host>/indexname/_search?pretty=true&q=text:*str*
I want to do the same with RestHighLevelClient, but it dosn't work:
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.types(INDEX_TYPE);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
QueryBuilder queryBuilder = QueryBuilders.matchQuery(IMAGE_TEXT_FIELD_NAME, "*str*" );
searchSourceBuilder.query(queryBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
The equivalent query is not a match query but a query_string query, so you can replace the following line
QueryBuilder queryBuilder = QueryBuilders.matchQuery(IMAGE_TEXT_FIELD_NAME, "*str*" );
by this one
QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("*str*").field(IMAGE_TEXT_FIELD_NAME);

Search By multiple word using AND operator

This works like a charm :
Client client = new TransportClient().addTransportAddress(new InetSocketTransportAddress("localhost", 9300));
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("field", "word_1");
fuzzyQueryBuilder.fuzziness(Fuzziness.AUTO);
SearchResponse response = client.prepareSearch("ts_index")
.setTypes("service")
.setQuery(fuzzyQueryBuilder)
.setFrom(0).setSize(60).setExplain(true)
.execute()
.actionGet();
SearchHit[] results = response.getHits().getHits();
But if I want to search by multiple words, it returns nothing, Ex :
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("field", "word_1 word_2");
When I'm using CURL, I've resolved this issue by adding operator to my JSON attributes:
curl -XGET "http://localhost:9200/_search" -d" {\"query\":{\"match\":{\"field\":{\"query\":\"word_1 word_2\",\"fuzziness\":\"AUTO\",\"operator\":\"and\"}}}}
How can I achieve this in Java ?
I believe this is possible with must for AND and should for OR:
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.fuzzyQuery("field", "word_1"))
.must(QueryBuilders.fuzzyQuery("field", "word_2"));
You can try to build this in a dynamic way with an enum:
public enum MultiQueryBuilder {
OPERATOR_AND {
#Override
public QueryBuilders createQuery(String field, String multiWord) {
String[] words = multiWord.split("\\s+");
QueryBuilder queryBuilder = QueryBuilder.boolQuery();
for(String word : words){
queryBuilder.must(QueryBuilders.fuzzyQuery(field, word));
}
return queryBuilder;
},
OPERATOR_OR {
#Override
public QueryBuilders createQuery(String field, String multiWord) {
String[] words = multiWord.split("\\s+");
QueryBuilder queryBuilder = QueryBuilder.boolQuery();
for(String word : words){
queryBuilder.should(QueryBuilders.fuzzyQuery(field, word));
}
return queryBuilder;
};
public abstract Querybuilders createQuery(String field, String multiWord);
}
You just have to call it in this way:
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders
.fuzzyQuery(MultiQuerybuilder
.valueOf(OPERATOR_AND)
.createQuery("field", "word_1 word_2"));

How to create a query with query_string using Elasticsearch Java Api

Currently my query request body looks like
{
"query": {
"query_string": {
"default_field": "file",
"query": "Email OR #gmail.com #yahoo.com"
}
},
"highlight": {
"fields": {
"file": {
}
}
}
}
My java code looks like
String queryString = "{"
+ "\"query_string\": "
+ "{"
+ "\"default_field\":"
+ " \"file\","
+ " \"query\": \"Email OR #gmail.com #yahoo.com\""
+ "}"
+ "}";
with following API calls
SearchRequestBuilder searchRequestBuilder = client.prepareSearch()
.setIndices("resume")
.setTypes("docs").setQuery(queryString).addHighlightedField("file");
SearchResponse response = searchRequestBuilder.execute().actionGet();
I would prefer a more api based approach for "queryString" part.I am not able to find any api which handles "query_string" part of the request. There are apis for match_all,match, term so on and so forth but not for query_string
Any help would be really appreciated
QueryBuilders is the factory for creating any query including query_string. From documentation:
import static org.elasticsearch.index.query.QueryBuilders.*;
QueryBuilder qb = queryStringQuery("+kimchy -elasticsearch");
Your query would be built as follows:
QueryBuilder qb = queryStringQuery("Email OR #gmail.com #yahoo.com").defaultField("file");
And the full example would be:
SearchRequestBuilder searchRequestBuilder = client.prepareSearch()
.setIndices("resume")
.setTypes("docs").setQuery(qb).addHighlightedField("file");
SearchResponse response = searchRequestBuilder.execute().actionGet();

elasticsearch QueryBuilder with dynamic list value in term query

I have a code like below where I'm doing multiple must in bool query. Here I'm passing the must term queries in field "address". Now the ip address will come to me as a list from other api and I have to pass for all the ip's in the list as a must term query. Here I'm not getting a way how to pass the address values dynamically when creating the QueryBuilder.
Please suggest how to do this.
public static SearchResponse searchResultWithAggregation(String es_index,
String es_type, List<String> ipList, String queryRangeTime) {
Client client = ESClientFactory.getInstance();
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("address", "10.203.238.138"))
.must(QueryBuilders.termQuery("address", "10.203.238.137"))
.must(QueryBuilders.termQuery("address", "10.203.238.136"))
.mustNot(QueryBuilders.termQuery("address", "10.203.238.140"))
.should(QueryBuilders.termQuery("client", ""));
queryRangeTime = "now-" + queryRangeTime + "m";
FilterBuilder fb = FilterBuilders.rangeFilter("#timestamp")
.from(queryRangeTime).to("now");
SearchResponse response = client
.prepareSearch(es_index)
.setTypes(es_type)
.setQuery(qb)
.setPostFilter(fb)
.addAggregation(
AggregationBuilders.avg("cpu_average").field("value"))
.setSize(10).execute().actionGet();
System.out.println(response.toString());
return response;
}
You can use the terms query to pass multiple values for single field.
create a string array or set. and pass it to the terms query.
Set<String> address = new HashSet<String>();
address.add("10.203.238.138");
address.add("10.203.238.137");
address.add("10.203.238.136");
if(address!=null)
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.termsQuery("address",address))
.mustNot(QueryBuilders.termQuery("address", "10.203.238.140"))
.should(QueryBuilders.termQuery("client", ""));
else
QueryBuilder qb = QueryBuilders.boolQuery()
.mustNot(QueryBuilders.termQuery("address", "10.203.238.140"))
.should(QueryBuilders.termQuery("client", ""));
Hope it helps..!
If you use TermsQuery for address array/set, it will return any documents that match with at least one or more of the provided terms.
List<String> address = new ArrayList<String>();
address.add("10.203.238.138");
address.add("10.203.238.137");
address.add("10.203.238.136");
BoolQueryBuilder qb = QueryBuilders.boolQuery();
qb.mustNot(QueryBuilders.termQuery("address", "10.203.238.140"));
qb.should(QueryBuilders.termQuery("client", ""));
for(String add: Address){
qb.must(QueryBuilders.termsQuery("address",add));
}

Using Hibernate Search (Lucene) to query a term with no spaces to match against field value with spaces

I have a Movie class with a name field. I want to match against movie names that have spaces in it for query terms with no spaces.
For example: I want the movie with name Toy Story to be in the results when the search term is toystory.
How to do I write my Hibernate Search DSL Query?
One way of addressing this is actually an index time solution. You could add the movie title as concatenated (w/o spaces) string to the the index using a custom analyzer. This way you would get the functionality you are after for free.
Add an analyzer to the Movie class with a NGramFilterFactory and LowerCaseFilterFactory filters, then use this analyzer for the name field:
#Indexed
#AnalyzerDef(name = "ngram", tokenizer = #TokenizerDef(factory = StandardTokenizerFactory.class), filters = {
#TokenFilterDef(factory = LowerCaseFilterFactory.class),
#TokenFilterDef(factory = NGramFilterFactory.class, params = {
#Parameter(name = "minGramSize", value = "3"),
#Parameter(name = "maxGramSize", value = "10")
})
})
public class Movie{
#Field( analyze = Analyze.YES )
#Analyzer( definition="ngram" )
private String name;
// standard setters and getters go here
}
The movie with name Toy Story will definitely appear in the results when the search term is toystory, just use the next query:
QueryBuilder qb = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity( Movie.class ).get();
org.apache.lucene.search.Query query = qb
.keyword()
.onField("name")
.matching("toystory");
.createQuery();
org.hibernate.Query hibQuery =
fullTextSession.createFullTextQuery(query, Movie.class);
List result = hibQuery.list();

Categories

Resources