Elasticsearch QueryBuilder not all fields always there - elasticsearch

I'm trying to use a QueryBuilder but I have problems with fields not always being needed.
.setQuery(QueryBuilders.boolQuery()
.must(termQuery("country", countryName))
.must(termQuery("Region", regionName))
.must(termQuery("City", city))
.must(rangeQuery("persons").from(persons))
.get();
In the example above city might not always be needed, but if I leave it empty it searches for an empty city. This is just for city, but I expect 10+ fields later on.
Can I somehow conditionally add things to the builder or is there another smart way?

You can build your query and then pass it to the search request. During building you can conditionally add your statements to the query. It will look like this
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(termQuery("country", countryName))
.must(termQuery("Region", regionName))
.must(rangeQuery("persons").from(persons));
if(city != null && city.trim().equals("")) {
queryBuilder.must(termQuery("City", city));
}
.setQuery(queryBuilder); //add query to your search request

Related

how to get Unique records from elastic search engine based on a field

I have an elastic search index that stores the list of restaurants in an area. I'm using spring elastic search to query the restaurant based on a given geo-location (lat/long) within 10 miles distance. I have a requirement where I only need to show a restaurant chain once, I'm seeing multiple records in my search result for the restaurant chains because they have the same name but different addresses. I only need to show the nearest restaurant chain restaurant along with the other unique restaurants. Is there a single query that can do that? Below is my code [removed some stuff for brevity!]
public SearchHits<Results> search(List<String> items){
final NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
BoolQueryBuilder termsQuery = boolQuery();
termsQuery.should(termsQuery(entry.getKey(), items));
boolQuery.must(termsQuery);
// ...I do additional logic here
searchQuery.withQuery(boolQuery);
// apply the terms aggregation searchQuery.addAggregation(terms(CATEGORIES_KEY).field(CATEGORY).size(BUCKET_SIZE));
Query query = searchQuery.build();
SearchHits<Results> searchHits = elasticsearcTemplate.search(query, Results.class);
return searchHits;
}
I was going thru the documentation of elasticsearch, it turns out...there is a simple fix for that :) I can use Collapse The collapse feature removes the duplicate data based on a field. So I only needed to add this line:
searchQuery.withCollapseField("restaurant_name");
// restaurant_name is what I want unique values on

Elasticsearch query not returning expected results for multiple should filters

I am performing an Elasticsearch query using the high-level-rest-api for Java and expect to see records that are either active or do not have a reference id. I'm querying by name for the records and if I hit the index directly with /_search?q=, I see the results I want.
Is my logic correct (pseudo-code):
postFilters.MUST {
Should {
MustNotExist {referenceId}
Must {status = Active}
}
Should {
MustNotExist {referenceId}
Must {type = Person}
}
}
What I get are records that are active with a reference id. But, I want to include records that also do not have a referenceId, hence why I have MustNotExist {referenceId}.
For simplicity, the second Should clause can be dropped (for testing) as the first one is not working as expected by itself.
In my case, I had to use a match query instead of a term query because the value I was querying for was not a primitive or a String. For example, the part where Must, type = Person, Person was an enum, and so looking for "Person" was not quite right, whereas match allowed it to "match".

Only return specific fields in elastic search native query Java api

I'm building a native query but I only want to return certain fields, all of which are held within a parent field. I think I am looking for the QueryBuilders or NativeSearchQueryBuilder equivalent of the REST API's _source. Here's a code example:
NativeSearchQueryBuilder sb = new NativeSearchQueryBuilder()
.withIndices("myIndex")
.withTypes("myType")
.withQuery(QueryBuilders.queryStringQuery("parent.field2:Foo*"));
.withFields("parent.field1");
I'd expect this to return a list of only parent.field1 that are associated with objects that have parent.field2 like Foo*. But it returns nothing.
Thanks for any help!
After some research, I found the answer is in NativeSearchQueryBuilder. I was just using an older version of spring-data elastic search, so I could not see this method: withSourceFilter. The way to do this is:
NativeSearchQueryBuilder sb = new NativeSearchQueryBuilder()
.withIndices("myIndex")
.withTypes("myType")
.withQuery(QueryBuilders.queryStringQuery("parent.field2:Foo*"));
.withSourceFilter(new FetchSourceFilter(<String array of includes>, null));
FetchSourceFilter takes 2 arguments, a String[] array of includes and one of excludes. In my example, I'd have an array like new String[]{"parent.field1"} passed to FetchSourceFilter, which in turn is passed to withSourceFilter. The search above will then return (once built and ran) a list of parent.field1 with parent.field2 like Foo*.
The version I upgraded to was spring-data-elasticsearch 2.0.2.

Spring data elastic search - Query - Full text search

I am trying to use elastic search for full text search and Spring data for integrating elastic search with my application.
For example,
There are 6 fields to be indexed.
1)firstName
2)lastName
3)title
4)location
5)industry
6)email
http://localhost:9200/test/_mapping/
I can see these fields in the mapping.
Now, I would like to make a search against these fields with a search input.
For example, When I search "mike 123", it has to search against all these 6 fields.
In Spring data repository,
The below method works to search only in firstName.
Collection<Object> findByFirstNameLike(String searchInput)
But, I would like to search against all the fields.
I tried,
Collection<Object> findByFirstNameLikeOrLastNameLikeOrTitleLikeOrLocationLikeOrIndustryLikeOrEmailLike(String searchInput,String searchInput1,String searchInput2,String searchInput3,)
Here, even the input string is same, i need to pass the same input as 6 params. Also the method name looks bigger with multiple fields.
Is there anyway to make it simple with #Query or ....
Like,
Collection<Object> findByInput(String inputString)
Also, boosting should be given for one of the field.
For example,
When i search for "mike mat", if there is any match in the firstName, that should be the first one in the result even there are exact match in the other fields.
Thanks
Lets suppose your search term is in the variable query, you can use the method search in ElasticsearchRepository.
repo.search(queryStringQuery(query))
to use queryStringQuery use the following import
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
I found the way to achieve this and posting here. Hope, this would help.
QueryBuilder queryBuilder = boolQuery().should(
queryString("Mike Mat").analyzeWildcard(true)
.field("firstName", 2.0f).field("lastName").field("title")
.field("location").field("industry").field("email"));
Thanks
Not a spring-data elasticsearch expert. But I see two directions you can go. The first would be to use the #Query option. That way you can create your own query. The second would be to use the example in the Filter builder section:
http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.misc.filter
Within elasticearch you would want to use the multi_match query:
http://www.elastic.co/guide/en/elasticsearch/reference/1.5/query-dsl-multi-match-query.html
In java such a query would look like this:
QueryBuilder qb = multiMatchQuery(
"kimchy elasticsearch",
"user", "message"
);
Example coming from: http://www.elastic.co/guide/en/elasticsearch/client/java-api/current/query-dsl-queries.html#multimatch
We can write our own custom query as below.
we can specific index, routing value (this is used if alias is used)
SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices(INDEX)
.withRoute(yourQueryBuilderHelper.getRouteValue())
.withQuery(yourQueryBuilderHelper.buildQuery(yourSearchFilterRequestObject))
.withFilter(yourQueryBuilderHelper.buildFilter(yourSearchFilterRequestObject)).withTypes(TYPE)
.withSort(yourQueryBuilderHelper.buildSortCriteria(yourSearchFilterRequestObject))
.withPageable(yourQueryBuilderHelper.buildPaginationCriteria(yourSearchFilterRequestObject)).build();
FacetedPage<Ticket> searchResults = elasticsearchTemplate.queryForPage(searchQuery, YourDocumentEntity.class);
Its good to use your own queryBuilder helper which can seperate your elasticSearchService from queryBuilder responsibility.
Hope this helps
Thanks
QueryBuilder class is helpful to query from spring Dao to elastic search:
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryBuilder;
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("state", "KA"));
.must(QueryBuilders.termQuery("content", "test4"))
.mustNot(QueryBuilders.termQuery("content", "test2"))
.should(termQuery("content", "test3"));
.should(termQuery("content", "test3"));
Try like this, you can even set importance of the field
QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(query)
.field("name", 2.0f)
.field("email")
.field("title")
.field("jobDescription", 3.0f)
.type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX);
Another way is using Query String query
Query searchQuery = new StringQuery(
"{\"query\":{\"query_string\":{\"query\":\""+ your-query-here + "\"}}}\"");
SearchHits<Product> products = elasticsearchOperations.search(
searchQuery,
Product.class,
IndexCoordinates.of(PRODUCT_INDEX_NAME));
This will search all the field from your document of specified index

How to get total count in hibernate full text search?

I am trying using hibernate full text by following this link:
hibernate/search/4.1/reference/en-US/html/getting-started
Basically, it works, but I want to know how to get total count while I execute a full text query,then I can tell user how many results and how many pages would be in such a query.
Here is the code(Using JPA to create and execute a search):
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
em.getTransaction().begin();
// create native Lucene query unsing the query DSL
// alternatively you can write the Lucene query using the Lucene query parser
// or the Lucene programmatic API. The Hibernate Search DSL is recommended though
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity( Book.class ).get();
org.apache.lucene.search.Query query = qb
.keyword()
.onFields("title", "subtitle", "authors.name", "publicationDate")
.matching("Java rocks!")
.createQuery();
// wrap Lucene query in a javax.persistence.Query
javax.persistence.Query persistenceQuery =
fullTextEntityManager.createFullTextQuery(query, Book.class);
persistenceQuery.setFirstResult((page - 1) * PAGECOUNT);
persistenceQuery.setMaxResults(PAGECOUNT);
// execute search
List result = persistenceQuery.getResultList();
em.getTransaction().commit();
em.close();
In SQL, I can use select count(*) from something, but here I don't know how to do that. I want to just fetch one page of data every time and use another API to get total count.
query.getResultSize(); //return the total number of matching ... regardless of pagination
I'm not sure if there is such a way when using the Hibernate full text search.
If you want to know the total number of results then you have to perform the full query. After you have the full count you can set your page limiter and perform it again.
javax.persistence.Query persistenceQuery =
fullTextEntityManager.createFullTextQuery(query, Book.class);
int count = persistenceQuery.getResultList().size();
persistenceQuery =
fullTextEntityManager.createFullTextQuery(query, Book.class);
persistenceQuery.setFirstResult((page - 1) * PAGECOUNT);
persistenceQuery.setMaxResults(PAGECOUNT);
List result = persistenceQuery.getResultList();
For Hibernate(maybe for JPA)
public interface FullTextQuery extends Query
in other words, you need use
org.hibernate.search.FullTextQuery query = fullTextEntityManager.createFullTextQuery(query, Book.class);
instead of
org.hibernate.Query query = fullTextEntityManager.createFullTextQuery(query, Book.class);
and method getResultSize() will be available
When using directly Lucene/Solr, I usually use a hack* by searching for *:*, setting it to return the least possible results BUT that does return the total result count for "everything", and I proceed to extract it. Basically it's the same as the SELECT count(*) FROM whatever :P
*I say hack because I'm not sure if it's supposed to be that way or not, but it works for me...

Resources