Question

I'm a complete beginner to elasticsearch and I have been trying to use elasticsearch's completion suggester using Nest for auto-complete on a property.

Here is my mapping (as mentioned here: ):

  var createResult = client.CreateIndex(indexName, index => index                
            .AddMapping<Contact>(tmd => tmd
                .Properties(props => props
                    .Completion(s =>
                        s.Name(p => p.CompanyName.Suffix("completion"))
                        .IndexAnalyzer("standard")
                        .SearchAnalyzer("standard")
                        .MaxInputLength(20)
                        .Payloads()
                        .PreservePositionIncrements()
                        .PreserveSeparators())                        
                )
            )
        );
var resultPerson = client.IndexMany(documents.OfType<Person>(), new SimpleBulkParameters { Refresh = true });
var resultCompany = client.IndexMany(documents.OfType<Company>(), new SimpleBulkParameters { Refresh = true });

And while indexing I'm just making use of IndexMany method and passing the IEnumberable<Contact> (Contact has a property by name CompanyName, Contact is an abstract class, both Person and Company are concrete implementations of it). The search throws an exception saying ElasticSearchException[Field [companyName] is not a completion suggest field]. And the query looks like below:

SearchDescriptor<Contact> descriptor = new SearchDescriptor<Contact>();
descriptor = descriptor.SuggestCompletion("suggest", c => c.OnField(f => f.CompanyName).Text(q));

var result = getElasticClientInstance("contacts").Search<Contact>(body => descriptor);

string qe = result.ConnectionStatus.ToString();

What am I doing wrong here, I looked into Nest's tests on SuggestCompletion but not much help, meaning the test only depict on how to get suggestions but not on how to set index mappings for SuggestCompletion.

I also tried setting up edgeNgram tokenizer as mentioned in this post but, couldn't proceed there as well.

Any direction or an example on how to proceed would greatly help.

Was it helpful?

Solution

UPDATE

You are try to create a property with the name "companyName.completion" but at that position its not valid and it will use the last token "completion". So its actually mapping a field called completion.... try changing the call to: .Name(p => p.CompanyName)

Other observations

You specify a mapping for the Contact but while indexing you use the Person and Company types.

In elasticsearch terms you mapped:

/index/contact/

but your documents are going into:

/index/person/ and /index/company

NEST won't automatically map all implementation of a specific class and elasticsearch has no way of knowing the three are related.

I would refactor the mapping to a method and call it for all the types involved.

 var createResult = client.CreateIndex(indexName, index => index                
      .AddMapping<Contact>(tmd => MapContactCompletionFields(tmd))
      .AddMapping<Person>(tmd => MapContactCompletionFields(tmd))
      .AddMapping<Company>(tmd => MapContactCompletionFields(tmd))   
  );

 private RootObjectMappingDescriptor<TContact>  MapContactCompletionFields<TContact>(
      RootObjectMappingDescriptor<TContact> tmd)
      where TContact : Contact
 {
      return  tmd.Properties(props => props
           .Completion(s => s
                .Name(p => p.CompanyName.Suffix("completion"))
                .IndexAnalyzer("standard")
                .SearchAnalyzer("standard")
                .MaxInputLength(20)
                .Payloads()
                .PreservePositionIncrements()
                .PreserveSeparators()
           )                        
       );
 } 

That method returns the descriptor so you can further chain on it.

Then when you do a search for contacts:

var result = getElasticClientInstance("contacts").Search<Contact>(
    body => descriptor
        .Types(typeof(Person), typeof(Company))
);

That types hint will cause the search to looking /index/person and /index/company and will know how to give you back a covariant list of documents.

So you can do result.Documents.OfType<Person>() after the previous call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top