简体   繁体   中英

Elasticsearch - Autocomplete with NEST

I'm trying to do autocomplete with the NEST client.

The code looks as follows:

Poco (condensed):

public class Course
{
    [ElasticProperty(Name="id")]
    public int ID { get; set; }
    public string Name { get; set; }   
    [ElasticProperty(Type = FieldType.Completion)]
    public CompletionField Suggest { get; set; }

    public Course(sm.Models.Course c)
    {
        if (c != null)
        {
            this.ID = c.ID;
            this.Name = c.Name;
            this.Suggest = new CompletionField
            {
                Input = new List<string>(this.Name.Split(' ')) { this.Name },
                Output = this.Name,
                Payload = new
                {
                    id = this.Name
                },
                Weight = 1
            };
        }
    }
}   

Indexing:

Client.CreateIndex("myindex", c => c
            .NumberOfReplicas(1)
            .NumberOfShards(5)
            .Settings(s => s
                .Add("merge.policy.merge_factor", "10")
                .Add("search.slowlog.threshold.fetch.warn", "1s")
            )
            .AddMapping<Course>(m => m.MapFromAttributes()
                .Properties(props => props
                    .Completion(s=>s
                        .Name(p=>p.Suggest)
                        .IndexAnalyzer("simple")
                        .SearchAnalyzer("simple")
                        .MaxInputLength(20)
                        .Payloads()
                        .PreservePositionIncrements()
                        .PreserveSeparators()
                    )
                )                
            ));

My suggest query:

GET _suggest
{
  "course-suggest": {
   "text": "Nothilfe",
   "completion": {
     "field": "suggest",
     "size": 10
   }
  }
}

Which results in this error:

    "failures": [
         {
            "shard": 1,
            "index": "myindex",
            "status": "INTERNAL_SERVER_ERROR",
            "reason": {
               "type": "exception",
               "reason": "failed to execute suggest",
               "caused_by": {
                  "type": "exception",
                  "reason": "Field [suggest] is not a completion suggest field"
               }
            }
         }

Why is my suggest field not recognized as a completion field?

GET _mapping/course

"suggest": {
  "properties": {
     "input": {
        "type": "string"
     },
     "output": {
        "type": "string"
     },
     "payload": {
        "properties": {
           "id": {
              "type": "string"
           }
        }
     },
     "weight": {
        "type": "long"
     }
  }

There are a few possible reasons why you might be receiving this error, but the most obvious one is that your mapping for type course in index myindex is out of date in relation to the query you're sending through.

You can easily check the the mapping for the type with

curl -XGET "http://localhost:9200/myindex/course/_mapping"

It should look like

{
   "myindex": {
      "mappings": {
         "course": {
            "properties": {
               "id": {
                  "type": "integer"
               },
               "name": {
                  "type": "string"
               },
               "suggest": {
                  "type": "completion",
                  "analyzer": "simple",
                  "payloads": true,
                  "preserve_separators": true,
                  "preserve_position_increments": true,
                  "max_input_length": 20
               }
            }
         }
      }
   }
}

If it doesn't you'll need to fix this, either by deleting the type in the index and recreating (or deleting the index altogether and recreating), or by creating a new index with the correct mapping and copying documents over from the old index if you want to *try* to keep any data you already have (this may or may not be possible here).

The following will create the index as expected and perform the suggest query you're trying to run. Note that I have scoped the query to run only against the index myindex . The types

public class Course
{
    [ElasticProperty(Name = "id")]
    public int ID { get; set; }

    public string Name { get; set; }

    [ElasticProperty(Type = FieldType.Completion)]
    public CompletionField Suggest { get; set; }

    public Course(Course c)
    {
        if (c != null)
        {
            this.ID = c.ID;
            this.Name = c.Name;
            this.Suggest = new CompletionField
            {
                Input = new List<string>(this.Name.Split(' ')) { this.Name },
                Output = this.Name,
                Payload = new
                {
                    id = this.Name
                },
                Weight = 1
            };
        }
    }
}

// I'm guessing CompletionField looks something like this?
public class CompletionField
{
    public List<string> Input { get; set; }
    public string Output { get; set; }
    public object Payload { get; set; }
    public double Weight { get; set; }
}

and the creation of the index and query

void Main()
{
    var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
        .ExposeRawResponse(true)
        .SetConnectionStatusHandler(response =>
        {
            // log out the requests
            if (response.Request != null)
            {
                Console.WriteLine("{0} {1} \n{2}\n", response.RequestMethod.ToUpperInvariant(), response.RequestUrl,
                    Encoding.UTF8.GetString(response.Request));
            }
            else
            {
                Console.WriteLine("{0} {1}\n", response.RequestMethod.ToUpperInvariant(), response.RequestUrl);
            }

            if (response.ResponseRaw != null)
            {
                Console.WriteLine("{0}\n{1}\n\n{2}\n", response.HttpStatusCode, Encoding.UTF8.GetString(response.ResponseRaw), new String('-', 30));
            }
            else
            {
                Console.WriteLine("{0}\n\n{1}\n", response.HttpStatusCode, new String('-', 30));
            }
        });

    var client = new ElasticClient(settings);

    var indexResponse = client.CreateIndex("myindex", c => c
        .NumberOfReplicas(1)
        .NumberOfShards(5)
        .Settings(s => s
            .Add("merge.policy.merge_factor", "10")
            .Add("search.slowlog.threshold.fetch.warn", "1s")
        )
        .AddMapping<Course>(m => m.MapFromAttributes()
                .Properties(props => props
                    .Completion(s => s
                        .Name(p => p.Suggest)
                        .IndexAnalyzer("simple")
                        .SearchAnalyzer("simple")
                        .MaxInputLength(20)
                        .Payloads()
                        .PreservePositionIncrements()
                        .PreserveSeparators()
                    )
                )
        ));

    // give Elasticsearch some time to initialize the index
    Thread.Sleep(TimeSpan.FromSeconds(5));

    var suggestResponse = client.Suggest<Course>(s => s
        .Index("myindex")
        .Completion("course-suggest", c => c
            .Text("Nothilfe")
            .OnField("suggest")
            .Size(10)
        )
    );

    // do something with suggestResponse
}

This logs out the following to the console

POST http://localhost:9200/myindex 
{
  "settings": {
    "index": {
      "number_of_replicas": 1,
      "number_of_shards": 5,
      "merge.policy.merge_factor": "10",
      "search.slowlog.threshold.fetch.warn": "1s"
    }
  },
  "mappings": {
    "course": {
      "properties": {
        "id": {
          "type": "integer"
        },
        "name": {
          "type": "string"
        },
        "suggest": {
          "type": "completion",
          "search_analyzer": "simple",
          "index_analyzer": "simple",
          "payloads": true,
          "preserve_separators": true,
          "preserve_position_increments": true,
          "max_input_len": 20
        }
      }
    }
  }
}

200
{"acknowledged":true}

------------------------------

POST http://localhost:9200/myindex/_suggest 
{
  "course-suggest": {
    "text": "Nothilfe",
    "completion": {
      "field": "suggest",
      "size": 10
    }
  }
}

200
{"_shards":{"total":5,"successful":5,"failed":0}}

------------------------------

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM