Поиск в Azure — частичное совпадение фраз

#azure #azure-cognitive-search

#azure #azure-когнитивный поиск

Вопрос:

Я пытаюсь улучшить ранжирование результатов, которые возвращаются из поискового индекса Azure. Индекс поиска в основном содержит список названий групп и участников.

Для нас важно точное совпадение, но также и частичное совпадение, как и неполное слово в запросе.

Если я использую пример попытки найти группу под названием Black Flag . В области пользовательского ввода я дошел до ввода black fl .

В настоящее время я структурирую запрос следующим образом: "black fl"|black fl* (точное совпадение по целой фразе и частичное совпадение по fl).

Это возвращает следующие результаты в следующем порядке:

  • Флуоресцирующий черный
  • Флоренс Блэк
  • Черный флаг

На данный момент выполняется поиск по единственному текстовому полю с помощью Standard - Lucene анализатора.

Я просмотрел Scoring Profiles , но они, похоже, не имеют отношения к такому небольшому набору данных с точки зрения доступных полей.

Я также исследовал полный поиск lucene, добавив такие вещи, как ^ 10 к слову black, чтобы сделать его более важным, и многими способами изменил свою строку запроса, все из которых, похоже, не дают желаемого эффекта.

Я ожидал бы, что это Black Flag будет соответствовать лучше, поскольку порядок слов более правильный, чем у результатов, которые находятся над ним.

Есть ли способ изменить метод подсчета очков, чтобы справиться с этим? Теперь я представляю, что имею дело с пользовательским анализатором (https://learn.microsoft.com/en-gb/azure/search/index-add-custom-analyzers) но не совсем уверен, с чего начать и как бы я хотел, чтобы вел себя анализатор.

Мы были бы весьма признательны за любые мысли или примеры о том, как наилучшим образом справиться с этим сценарием.

РЕДАКТИРОВАТЬ — Дополнительная информация Текущее решение состоит из следующего, но оно предполагает необходимость манипулировать результатами, получаемыми из поискового индекса.

  1. Индекс создается следующим образом:
 {
    "fields": [
        {"name": "id", "type": "Edm.String", "key": true, "filterable": false, "searchable": false, "sortable": false, "facetable": false},
        {"name": "entityId", "type": "Edm.Int64", "filterable": false, "searchable": false, "sortable": false, "facetable": false},
        {"name": "entityType", "type": "Edm.Int32", "sortable": false, "facetable": false},
        {"name": "sortableName", "type": "Edm.String", "filterable": false, "facetable": false, "searchable": false},
        {"name": "name", "type": "Edm.String", "filterable": false, "retrievable": false, "sortable": false, "facetable": false, "analyzer":"keyword_analyzer"},
        {"name": "town", "type": "Edm.String", "filterable": false, "retrievable": false, "sortable": false, "facetable": false, "analyzer":"keyword_analyzer"},
        {"name": "tags", "type": "Collection(Edm.String)", "filterable": false, "retrievable": false, "sortable": false, "facetable": false, "analyzer":"keyword_analyzer"}
    ],
    "defaultScoringProfile": "default_score",
    "scoringProfiles": [
        {
            "name": "default_score",
            "text":{
                "weights": {
                    "name": 3.5,
                    "tags": 2,
                    "town": 1
                }
            }
        }
    ],

    "analyzers":[
        {
            "name": "keyword_analyzer",
            "@odata.type":"#Microsoft.Azure.Search.CustomAnalyzer",
            "charFilters":[
                "map_dash",
                "map_space"
            ],
            "tokenizer":"keyword_tokenizer",
            "tokenFilters":[
                "asciifolding",
                "lowercase",
                "trim",
                "delimiter_filter"
            ]
        }
    ],
    "charFilters":[        
        {
           "name":"map_dash",
           "@odata.type":"#Microsoft.Azure.Search.MappingCharFilter",
           "mappings":["-=>_"]
        },
        {
           "name":"map_space",
           "@odata.type":"#Microsoft.Azure.Search.MappingCharFilter",
           "mappings":["\u0020=>_"]
        }
     ],
    "tokenizers":[
        {
            "name": "keyword_tokenizer",
            "@odata.type":"#Microsoft.Azure.Search.KeywordTokenizerV2"
        }
    ],
    "tokenFilters":[
        {
            "name": "stopwords_filter",
            "@odata.type":"#Microsoft.Azure.Search.StopwordsTokenFilter",
            "removeTrailing": false
        },      
        {
            "name": "delimiter_filter",
            "@odata.type":"#Microsoft.Azure.Search.WordDelimiterTokenFilter",
            "generateWordParts": true,
            "generateNumberParts": true,
            "splitOnCaseChange": false,
            "preserveOriginal": true,
            "splitOnNumerics": false
        }
    ]
}
  
  1. Before uploading data to the index we need to normalize it — Black Flag becomes black flag . We also have to remove any preceeding words of the so this means that The Killers becomes killers — also any non standard characters are replaced to remove accents etc.

  2. When performing a search, in code we need to now remove any preceeding the if it exists, and perform the same normalization — I can accept doing this.

  3. Затем мы создаем запрос, который изменяется в зависимости от того, сколько слов содержится в исходном запросе.

                 List<string> splitQ = queryPhrase.SplitToList(" ");
                if (splitQ.Count > 0)
                {
                    if (splitQ.Count == 1)
                    {
                        search.Append($"("{splitQ[0]}" || {this.EscapeSpecialCharacters(splitQ[0])}*)");
                    }
                    else
                    {
                        for (int i = 0; i < splitQ.Count; i  )
                        {
                            if (i == splitQ.Count - 1)
                            {


                                search.Append($" {this.EscapeSpecialCharacters(splitQ[i])}*");
                            }
                            else
                                search.Append($" "{splitQ[i]}"");
                        }

                        search.Insert(0, $"("{queryPhrase}"||(");
                        search.Append("))");
                    }
                }
  

Одно слово black будет означать, что основной запрос: ("black" || black*)

Однако, как только появляются дополнительные слова, это должно измениться. black fl становится: ("black fl"||( "black" fl*))

Поиск по трем словам будет: ("one two three"||( "one" "two" three*))

В дополнение к этому мы добавляем любые параметры фильтра.

  1. Поиск отправляется в индекс с типом запроса, установленным в full

Вышесказанное максимально приблизило нас к получению достойных и точных результатов. Однако при подсчете результатов все перепутано.

Обработка результатов…Во-первых, теперь мы нормализуем оценку, выдаваемую поисковым индексом Azure. В зависимости от поискового запроса оценки варьируются в широких пределах, поэтому мы нормализуем их в процентах на основе максимального значения показателя.

Теперь нам нужно применить наше собственное enhancer значение для оценки на основе поля tag или name . Точное совпадение с запросом дает улучшающее значение 5, а startswith запрос получает и улучшающее значение 3.

Затем мы предоставляем оценку, которая использует улучшение для повышения позиции результатов в рейтинге.

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

Комментарии:

1. Решением этой проблемы должно быть увеличение количества терминов. Не могли бы вы, пожалуйста, предоставить нам полный запрос? Также убедитесь, что вы задаете QueryType=full в своем запросе.

2. @MatthewGotteiner извините, что долго отвечал, это выпало из поля моего зрения. Я добавил наше текущее решение выше, надеюсь, оно имеет смысл.