I assume you expand the synonyms. You can use scripting to count the matching positions.
Elasticsearch Google Group with a solution by Vineeth Mohan
I adapted his script as a native script, that returns a number between 0 and 1 for the ratio of matched positions in the field. I tweaked it a bit to match only one position per query
You need a field that contains the number of positions, for example by using token_count which actually counts the number of positions
@Override
public Object run()
{
IndexField indexField = this.indexLookup().get(field);
Long numberOfPositions = ((ScriptDocValues.Longs) doc().get(positionsField)).getValue();
ArrayList<Integer> positions = new ArrayList<Integer>();
for (String term : terms)
{
Iterator<TermPosition> termPos = indexField.get(term, IndexLookup.FLAG_POSITIONS | IndexLookup.FLAG_CACHE)
.iterator();
while (termPos.hasNext())
{
int position = termPos.next().position;
if (positions.contains(position))
{
continue;
}
positions.add(position);
// if the term matches multiple positions, only a new position should count
break;
}
}
return positions.size() * 1.0 / numberOfPositions;
}
You can than use it in your query as a function_score script.
{
"function_score": {
"query": {
"match": {
"message": "I like elastic things since elasticsearch is super cool"
}
},
"script_score": {
"params": {
"terms": [
"I",
"like",
"elastic",
"things",
"since",
"elasticsearch",
"is",
"super",
"cool"
],
"field": "message",
"positions_field": "message.pos_count"
},
"lang": "native",
"script": "matched_positions_ratio"
},
"boost_mode": "replace"
}
}
You may then set "min_score" to 1 and only get documents that match all positions in the given field.
I hope this solution is what you need.