0

I'm trying to retrieve documents that have a phrase in them, not necessarily at the start of the word, over multiple document fields.

Such as "ell" should match a document field "hello". And do this on two fields.

I initially went with MultiMatch due to this SO answer. Here was my implementation:

QueryContainer &= Query<VeganItemEstablishmentSearchDto>.MultiMatch(c => c
    .Fields(f => f.Field(p => p.VeganItem.Name).Field(v => v.VeganItem.CompanyName))
    .Query(query)
    .MaxExpansions(2)
    .Slop(2)
    .Name("named_query")
);

But I found that it would only match "hello" if my search phrase started with the start of the word e.g. it would not match "ello".

So I then changed to QueryString due to this SO answer. My implementation was:

QueryContainer &= Query<VeganItemEstablishmentSearchDto>.QueryString(c => c
    .Fields(f => f.Field(p => p.VeganItem.Name).Field(v => v.VeganItem.CompanyName))
    .Query(query)
    .FuzzyMaxExpansions(2)
    .Name("named_query")
);

But I found that was even worse. It didn't search multiple fields, only p.VeganItem.Name and still "ello" was not matching "hello".

How do I use Nest to search for a term that can be in the middle of a word and over multiple document fields?

BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287

3 Answers3

2

Wildcard queries are expensive, if you want to customize and allow how many middle characters you want to search, you can do it using the n-gram tokenizer, that would be less expensive and will provide more customisation/flexibility to you.

I've also written a blog post on implementing the autocomplete and its various trade-offs with performance and functional requirements.

Amit
  • 30,756
  • 6
  • 57
  • 88
0

You will need to use wild card query for this scenario, for more information about wild cards query check here, and for nest WildQueries check here.

To do wild card query in Nest you can do like this:

new QueryContainer[]
 {
     Query<VeganItemEstablishmentSearchDto>.Wildcard(w => w
     .Field(v => v.VeganItem.CompanyName))
     .Value(query)),
     Query<VeganItemEstablishmentSearchDto>.Wildcard(w => w
     .Field(p => p.VeganItem.Name))
     .Value(query)
 }

Your should add asterisk (*) in the beginning and end of your query.

Please keep in your mind that wildCard queries are expensive and you might want to achieve these by having different Analyzer in your mapping.

Kaveh
  • 1,158
  • 6
  • 16
0

QueryString from this SO answer is what worked for me for multiple fields and the middle of a word. I have not tried Amit's answer yet. I will in the future. This is the quick solution for a beginner:

QueryContainer &= Query<VeganItemEstablishmentSearchDto>
.QueryString(c => c
    .Name("named_query")
    .Boost(1.1)
    .Fields(f => f.Field(p => p.VeganItem.Name).Field(v => v.VeganItem.CompanyName))
    .Query($"*{query}*")
    .Rewrite(MultiTermQueryRewrite.TopTermsBoost(10))
);

This also works:

QueryContainer = QueryContainer | Query<VeganItemEstablishmentSearchDto>
.MatchPhrase(c => c
    .Boost(1.1)
    .Field(f => f.VeganItem.Name)
    .Query(query)
    .Slop(1)
);

QueryContainer = QueryContainer | Query<VeganItemEstablishmentSearchDto>
.MatchPhrase(c => c
    .Boost(1.1)
    .Field(f => f.VeganItem.CompanyName)
    .Query(query)
    .Slop(1)
);


var terms = query.ToLower().Split(' ');
foreach (var term in terms)
{ 
    QueryContainer = QueryContainer | Query<VeganItemEstablishmentSearchDto>
        .Wildcard(c => c
            .Value($"*{term}*")
            .Field(f => f.VeganItem.CompanyName)
            .Rewrite(MultiTermQueryRewrite.TopTermsBoost(10))
        );

    QueryContainer = QueryContainer | Query<VeganItemEstablishmentSearchDto>
        .Wildcard(c => c
            .Value($"*{term}*")
            .Field(f => f.VeganItem.Name)
            .Rewrite(MultiTermQueryRewrite.TopTermsBoost(10))
        );
} 
BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287
  • 1
    Glad you found a solution, just beaware that query string isn't suitable for search boxes as it fails on bad syntax, read more on the https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html – Amit May 04 '22 at 05:48