Как создать собственный анализатор, чтобы игнорировать акценты и стоп-слова pt-br, используя API-интерфейс эластичного поиска? - PullRequest
0 голосов
/ 11 марта 2020

Прежде всего, учтите, что я использую класс "News" (Noticia, на португальском языке), который имеет строковое поле с именем "Content" (Conteudo на португальском языке)

public class Noticia
{
    public string Conteudo { get; set; } 
}

Я пытаюсь создайте индекс, настроенный на игнорирование акцентов и стоп-слов pt-br, а также на анализ до 40-ти символов в выделенном запросе.

Я могу создать такой индекс, используя этот код:

var createIndexResponse = client.Indices.Create(indexName, c => c
    .Settings(s => s
        .Setting("highlight.max_analyzed_offset" , 40000000)
        .Analysis(analysis => analysis
            .TokenFilters(tokenfilters => tokenfilters
                .AsciiFolding("folding-accent", ft => ft
                )
                .Stop("stoping-br", st => st
                    .StopWords("_brazilian_")
                )
            )
            .Analyzers(analyzers => analyzers
                .Custom("folding-analyzer", cc => cc
                    .Tokenizer("standard")
                    .Filters("folding-accent", "stoping-br")
                )
            )
        )
    )
    .Map<Noticia>(mm => mm
        .AutoMap()
        .Properties(p => p
            .Text(t => t
                .Name(n => n.Conteudo)
                .Analyzer("folding-analyzer")
            )
        )
    )
);

Если я тестирую этот анализатор с помощью Kibana Dev Tools, я получаю желаемый результат: без акцентов и стоп-слов!

POST intranet/_analyze
{
  "analyzer": "folding-analyzer",
  "text": "Férias de todos os funcionários"
}

Результат:

{
  "tokens" : [
    {
      "token" : "Ferias",
      "start_offset" : 0,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "funcionarios",
      "start_offset" : 19,
      "end_offset" : 31,
      "type" : "<ALPHANUM>",
      "position" : 4
    }
  ]
}

Те же (хорошие) результаты возвращаются, когда я использую NEST для анализа запроса с помощью моего анализатора свертывания (возвращаются токены "Ferias" e "funcionar ios")

var analyzeResponse = client.Indices.Analyze(a => a
.Index(indexName)
.Analyzer("folding-analyzer")
.Text("Férias de todos os funcionários")
);

Однако, если я выполняю поиск с использованием клиента NEST ElasticSearch. NET, такие термины, как «Férias» (с акцентом) и «Ferias» (без акцента), обрабатываются как разные.

Моя цель - выполнить запрос, который возвращает все результаты, независимо от того, является ли слово Férias или Ferias

То есть упрощенный код (C# гнездо) I я использую для запросаasticsearch:

var searchResponse = ElasticClient.Search<Noticia>(s => s
    .Index(indexName)
    .Query(q => q
    .MultiMatch(m => m
                .Fields(f => f
                    .Field(p => p.Titulo,4)
                    .Field(p => p.Conteudo,2)
                )
                .Query(termo)
            )
    )
);

, и это расширенный вызов API, связанный с searchResponse

Successful (200) low level call on POST: /intranet/_search?pretty=true&error_trace=true&typed_keys=true
# Audit trail of this API call:
 - [1] HealthyResponse: Node: ###NODE ADDRESS### Took: 00:00:00.3880295
# Request:
{"query":{"multi_match":{"fields":["categoria^1","titulo^4","ementa^3","conteudo^2","attachments.attachment.content^1"],"query":"Ferias"}},"size":100}
# Response:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 13.788051,
    "hits" : [
      {
        "_index" : "intranet",
        "_type" : "_doc",
        "_id" : "4934",
        "_score" : 13.788051,
        "_source" : {
          "conteudo" : "blablabla ferias blablabla",
          "attachments" : [ ],
          "categoria" : "Novidades da Biblioteca - DBD",
          "publicadaEm" : "2008-10-14T00:00:00",
          "titulo" : "INFORMATIVO DE DIREITO ADMINISTRATIVO E LRF - JUL/2008",
          "ementa" : "blablabla",
          "matriculaAutor" : 900794,
          "atualizadaEm" : "2009-02-03T13:44:00",
          "id" : 4934,
          "indexacaoAtiva" : true,
          "status" : "Disponível"
        }
      }
    ]
  }
}

Я также пытался использовать несколько полей и суффикс в запросе, но безуспешно

.Map<Noticia>(mm => mm
    .AutoMap()
    .Properties(p => p
        .Text(t => t
        .Name(n => n.Conteudo)
        .Analyzer("folding-analyzer")
        .Fields(f => f
            .Text(ss => ss
                .Name("folding")
                .Analyzer("folding-analyzer")
                )
        )

(...)

var searchResponse = ElasticClient.Search<Noticia>(s => s
    .Index(indexName)   
    .Query(q => q
    .MultiMatch(m => m
        .Fields(f => f
        .Field(p => p.Titulo,4)
        .Field(p => p.Conteudo.Suffix("folding"),2)       
                )
                .Query(termo)
            )
    )
);

Любая подсказка, что я делаю неправильно или что я могу сделать, чтобы достичь своей цели?

Заранее большое спасибо!

1 Ответ

0 голосов
/ 03 апреля 2020

Через несколько дней я узнал, что я делаю неправильно, и все было связано с отображением.

Вот шаги, которые я предпринял, чтобы подойти к проблеме и решить ее в конце

1 - в первую очередь я открыл консоль kibana и обнаружил, что только мое последнее поле отображенных полей было назначено для моего пользовательского анализатора (сворачивание-анализатор)

Для проверки каждого из ваших полей вы можете использовать API GET FIELD MAPPING и команду в инструментах разработчика, например:

GET /<index>/_mapping/field/<field>

, тогда вы сможете увидеть, назначен ли ваш анализатор для вашего поля или нет

2 - После этого я обнаружил, что последнее поле было единственным, назначенным для моего пользовательского анализатора, и причина была в том, что я испортил беглое отображение двумя способами:

  • Прежде всего, Мне пришлось правильно связать свои свойства текста
  • Во-вторых, я пытался отобразить другой класс POCO в другом предложении Map <>, когда я должен был использовать предложение Object <>

правильное отображение, которое сработало для меня, было примерно таким:

.Map<Noticia>(mm => mm
        .AutoMap()
        .Properties(p => p
            .Text(t => t
                .Name(n => n.Field1)
                .Analyzer("folding-analyzer")
            )
            .Text(t => t
                .Name(n => n.Field2)
                .Analyzer("folding-analyzer")
            )
            .Object<NoticiaArquivo>(o => o
                .Name(n => n.Arquivos)
                .Properties(eps => eps
                    .Text(s => s
                        .Name(e => e.NAField1)
                        .Analyzer("folding-analyzer")
                    )
                    .Text(s => s
                        .Name(e => e.NAField2)
                        .Analyzer("folding-analyzer")
                    )
                )
            )
        )
    )

Наконец, важно поделиться тем, что когда вы назначаете анализатор с помощью предложения .Analyzer ("analiserName"), вы говорите elasti c поиск, который вы хотите использовать анализатор аргументов как для индексации, так и для поиска

Если вы хотите использовать анализатор только при поиске, а не во время индексации, вы должны использовать .SearchAnalyzer ("analiserName ") оговорка

...