Dr. Roggia
Dr. Roggia

Reputation: 1125

Elasticsearch query with NEST don't work

I'm using Microsoft SQL Server Management Studio and ElasticSearch 2.3.4 with ElasticSearch-jdbc-2.3.4.1, and i linked ES with my mssql server. Everything works fine, but when i make a query using NEST on my MVC program the result is empty. When i put an empty string inside my search attribute i get the elements, but when i try to fill it with some filter i get an empty result. Can someone help me out please? Thanks in advance.

C#:

const string ESServer = "http://localhost:9200";
ConnectionSettings settings = new ConnectionSettings(new Uri(ESServer));
settings.DefaultIndex("tiky");
settings.MapDefaultTypeNames(map => map.Add(typeof(DAL.Faq), "faq"));
ElasticClient client = new ElasticClient(settings);

var response = client.Search<DAL.Faq>(s => s.Query(q => q.Term(x => x.Question, search)));

var result = response.Documents.ToList();

DAL: DAL

Postman: PostMan

PS: i followed this guide to create it

EDIT:

Index mapping: Index Mapping

Upvotes: 0

Views: 1658

Answers (1)

Russ Cam
Russ Cam

Reputation: 125488

There's a couple of things that I can see that may help here:

  1. By default, NEST camel cases POCO property names when serializing them as part of the query JSON in the request, so x => x.Question will serialize to "question". Looking at your mapping however, field names in Elasticsearch are Pascal cased, so what the client is doing will not match what's in Elasticsearch.

You can change how NEST serializes POCO property names by using .DefaultFieldNameInferrer(Func<string, string>) on ConnectionSettings

const string ESServer = "http://localhost:9200";
ConnectionSettings settings = new ConnectionSettings(new Uri(ESServer))
    .DefaultIndex("tiky");
    .MapDefaultTypeNames(map => map.Add(typeof(DAL.Faq), "faq"))
    // pass POCO property names through verbatim
    .DefaultFieldNameInferrer(s => s);

ElasticClient client = new ElasticClient(settings);
  1. As Rob mentioned in the comments, a term query does not analyze the query input. When executing a term query against a field that is analyzed at index time then, in order to get matches, the query text that you pass to the term query would need to take the analysis that is applied at index time into account. For example,

    • Question is analyzed with the Standard Analyzer
    • A Question value of "What's the Question?" will be analyzed and indexed as tokens "what's", "the" and "question"
    • A term query would need to have a query input of "what's", "the" or "question" to be a match

A match query, unlike a term query, does analyze the query input, so the output of the search analysis will be used to find matches. In conjunction with Pascal casing highlighted in 1., you should now get documents returned.

You can also have the best of both worlds in Elasticsearch i.e. analyze input at index time for full-text search functionality as well as index input without analysis to get exact matches. This is done with multi-fields and here is an example of creating a mapping that indexes Question properties as both analyzed and not analyzed

public class Faq
{
    public string Question { get; set; }
}

var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var defaultIndex = "default-index";
var connectionSettings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex)
        .DefaultFieldNameInferrer(s => s);

var client = new ElasticClient(connectionSettings);

if (client.IndexExists(defaultIndex).Exists)
    client.DeleteIndex(defaultIndex);

client.CreateIndex(defaultIndex, c => c
    .Mappings(m => m
        .Map<Faq>(mm => mm
            // let NEST infer mapping from the POCO
            .AutoMap()
            // override any inferred mappings explicitly
            .Properties(p => p
                .String(s => s
                    .Name(n => n.Question)
                    .Fields(f => f
                        .String(ss => ss
                            .Name("raw")
                            .NotAnalyzed()
                        )
                    )
                )
            )
        )
    )
);   

The mapping for this looks like

{
  "mappings": {
    "faq": {
      "properties": {
        "Question": {
          "type": "string",
          "fields": {
            "raw": {
              "type": "string",
              "index": "not_analyzed"
            }
          }
        }
      }
    }
  }
}

The "raw" sub field under the "Question" field will index the value of the Question property without any analysis i.e. verbatim. This sub field can now be used in a term query to find exact matches

client.Search<Faq>(s => s
    .Query(q => q
        .Term(f => f.Question.Suffix("raw"), "What's the Question?")
    )
);

which find matches for the previous example.

Upvotes: 2

Related Questions