Retrieve a document from elastic search by matching two fields - elasticsearch

My data are stored in elastic search as shown below
{
"identifier":{
"source":"source 1",
"id":"22081070"
},
"title":"Book 1",
"published":2011,
"types":[
"type1",
"type2,
"type3"
]
}
Is there a way to retrieve a document with specific "identifier.id" and "identifier.source" parameters? For example I am retrieving the above document with its id as an input with the following:
QueryBuilder queryBuilder = QueryBuilders.matchQuery("identifier.id", "22081070");
SearchResponse searchResponse = client.prepareSearch("test-index")
.setTypes("type").setQuery(queryBuilder).execute().actionGet();
but I know know how to add the "identifier.source" as a match parameter.

Try this:
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
QueryBuilder queryBuilder1 = QueryBuilders.matchQuery("identifier.id", "22081070");
QueryBuilder queryBuilder2 = QueryBuilders.matchQuery("identifier.source", "source 1");
boolQuery.must(queryBuilder1).must(queryBuilder2);
SearchResponse searchResponse = client.prepareSearch("test-index")
.setTypes("type").setQuery(boolQuery).execute().actionGet();

Related

How can I make phrase suggester query with Elasticsearch java api?

I am using 7.10. version of elasticsearch. I created an index and did settings-mappings. Then I sent query to index by using http requests. I got the results I need, but I want to do same thing with Java API. However, I couldn't.
Can you help me to send request and get the result as list in java from scratch ?
And here it is my query that I used for obtain suggestions:
{
"suggest": {
"text": "some title I want to search",
"phrase_suggester": {
"phrase": {
"field": "title.shingle",
"max_errors": 2,
"size": 5,
"confidence": 0.0,
"direct_generator": [{
"field": "title.shingle",
"max_edits": 2
}
]
}
}
}
}
How can I write this query with Elasticsearch Java API. Can you help me figure this out ?
This would be the way to build the request:
client.search(searchRequestBuilder -> searchRequestBuilder
.suggest(suggestBuilder -> suggestBuilder
.text("some title I want to search")
.suggesters("phrase_suggester", fieldSuggesterBuilder -> fieldSuggesterBuilder
.phrase(phraseBuilder -> phraseBuilder.field("title.shingle")
.maxErrors(2d)
.size(5)
.confidence(0.0)
.directGenerator(directGeneratorBuilder -> directGeneratorBuilder
.field("title.shingle")
.maxEdits(2))))),
YourEntity.class);
Btw, the new client was in 7.16, you wrote 7.14?
Finaly I've found my own answers. It was so hard to find the solution due to lack of documents about these kind of specific topics. I'm sharing my solution for those who wondered:
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
PhraseSuggestionBuilder builder = SuggestBuilders.phraseSuggestion("title.shingle")
.addCandidateGenerator(new DirectCandidateGeneratorBuilder("title.shingle")
.suggestMode("always"))
.text(query)
.maxErrors(2f)
.confidence(0f);
SuggestBuilder suggestBuilder = new SuggestBuilder().addSuggestion("suggestion", builder);
searchSourceBuilder.suggest(suggestBuilder);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("index_name");
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

Elasticsearch search Java API doesn't give correct results

I have following code for ES search:
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
qb = QueryBuilders.termQuery("title", "Java");
searchSourceBuilder.query(qb);
searchSourceBuilder.from(0);
searchSourceBuilder.size(5);
SearchRequest searchRequest = new SearchRequest("myindex");
searchRequest.types("books");
searchRequest.source(searchSourceBuilder);
SearchResponse sr = client.search(searchRequest);
System.out.println(sr.getHits().totalHits);
It gives me no results, even though there are documents meeting this criteria.
When I run similar term query from Kibana, I get results:
GET /myindex/books/_search
{
"query":
{
"term" : {
"title" : {
"value" : "java"
}
}
}
}
The difference is Java (with uppercase) in your Java code and java (in lowercase) in your Kibana query.
Since you're doing a term query, the case matters. If you used a match query, the case doesn't matter and both would work as you expect.

Elasticsearch Query: Boosting specific field

I am using Elasticsearch 2.4.3 and want to boost specific fields in my query. Is this possible? I only see how I can boost an index.
Greetings!
UPDATE
Mapping:
"firstName":{"type":"string",
"analyzer":"customNGram"
},
"lastName":{
"type":"string",
"analyzer":"customNGram"
},
"note":{
"type":"string",
"analyzer":"customNGram"
}
Query (Java API):
QueryBuilder qb = new BoolQueryBuilder()
.must(QueryBuilders.matchQuery("_all", term)
.analyzer("atsCustomSearchAnalyzer")
.operator(Operator.AND));
SearchRequestBuilder searchRequestBuilder = elasticsearchClient.prepareSearch("persons", "activities").setTypes("person", "activity")
.setQuery(qb)
.addHighlightedField("*").setHighlighterRequireFieldMatch(false)
.setHighlighterOrder("score")
.setHighlighterFragmentSize(150)
.setHighlighterForceSource(true)
.setSize(100)
.addIndexBoost("persons", 200)
.setFrom(offset);
return searchRequestBuilder.execute().get();
If you split up your match-query to match individual fields, eg using a multi match query (https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html), you can boost the field you like. So something like:
QueryBuilder qb = new BoolQueryBuilder()
.must(QueryBuilders.multiMatchQuery(term, "firstName^3",
"lastName^3", "note")
.analyzer("atsCustomSearchAnalyzer")
.operator(Operator.AND));
should boost firstName and lastName 3 times relative to the note field.

How to get selected object only from an array

I have a collection with documents of the following structure:
{
"category": "movies",
"movies": [
{
"name": "HarryPotter",
"language": "english"
},
{
"name": "Fana",
"language": "hindi"
}
]
}
I want to query with movie name="fana" and the response sholud be
{
"category": "movies",
"movies": [
{
"name": "HarryPotter",
"language": "english"
}
]
}
How do I get the above using spring mongoTemplate?
You can try something like this.
Non-Aggregation based approach:
public MovieCollection getMoviesByName() {
BasicDBObject fields = new BasicDBObject("category", 1).append("movies", new BasicDBObject("$elemMatch", new BasicDBObject("name", "Fana").append("size", new BasicDBObject("$lt", 3))));
BasicQuery query = new BasicQuery(new BasicDBObject(), fields);
MovieCollection groupResults = mongoTemplate.findOne(query, MovieCollection.class);
return groupResults;
}
Aggregation based approach:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.query.Criteria.where;
public List<BasicDBObject> getMoviesByName() {
Aggregation aggregation = newAggregation(unwind("movies"), match(where("movies.name").is("Fana").and("movies.size").lt(1)),
project(fields().and("category", "$category").and("movies", "$movies")));
AggregationResults<BasicDBObject> groupResults = mongoTemplate.aggregate(
aggregation, "movieCollection", BasicDBObject.class);
return groupResults.getMappedResults();
}
$unwind of mongodb aggregation can be used for this.
db.Collection.aggregate([{
{$unwind : 'movies'},
{$match :{'movies.name' : 'fana'}}
}])
You can try the above query to get required output.
Above approaches provides you a solution using aggregation and basic query. But if you dont want to use BasicObject below code will perfectly work:
Query query = new Query()
query.fields().elemMatch("movies", Criteria.where("name").is("Fana"));
List<Movies> movies = mongoTemplate.find(query, Movies.class);
The drawback of this query is that it may return duplicate results present in different documents, since more than 1 document may match this criteria. So you can add _id in the criteria like below:
Criteria criteria = Criteria.where('_id').is(movieId)
Query query = new Query().addCriteria(criteria)
query.fields().elemMatch("movies", Criteria.where("name").is("Fana"));
query.fields().exclude('_id')
List<Movies> movies = mongoTemplate.find(query, Movies.class);
I am excluding "_id" of the document in the response.

Build dynamic queries with Spring Data MongoDB Criteria

I would like to run a bulk delete operation on a list of documents in MongoDB that have been selected by the user in the UI so I need to dynamically build a query that looks like the following (the or clause expands for every document selected):
{
$and: [
{
"contentType": "application/vnd.sometype"
},
{
$or: [
{
"metadata.name": "someName",
"metadata.version": "someVersion"
},
{
"metadata.name": "someOtherName",
"metadata.version": "someOtherVersion"
}
]
}
]
},
Fields: null,
Sort: null
Just now I'm using string concatenation to achieve this.
Is it possible to build this query with the Spring Data MongoDB Criteria Builder (org.springframework.data.mongodb.core.query.Criteria)?
Doesn't this work for you?
Criteria criteria = Criteria.where("contentType").is("application/vnd.sometype");
List<Criteria> docCriterias = new ArrayList<Criteria>(docs.size());
for (Document doc: docs) {
docCriterias.add(Criteria.where("metadata.name").is(doc.getName())
.and("metadata.version").is(doc.getVersion()));
}
criteria = criteria.orOperator(docCriterias.toArray(new Criteria[docs.size()]));
?
Here we need to build new query and embed the criteria to the built new query. And also, we have to create a list of criteria using some criteria for embed to the query. Here my example is providing a list of metadata and we don't know the name of parameter which will send for us. So, The solution is as given follow.
List<Criteria> criterias = new ArrayList<>();
for (MetaData metaData : newDoc.getMetaData()) {
Criteria dynamicCriteria = Criteria.where("metaData.key").is(metaData.getKey()).andOperator(Criteria.where("metaData.value").is(metaData.getValue()));
criterias.add(dynamicCriteria);
}
Criteria criteria = new Criteria().andOperator(criterias.toArray(new Criteria[criterias.size()]));
Query searchQuery = new Query(criteria);
List<Document> documents = mongoTemplate.find(searchQuery, Document.class);

Resources