Elastic Search для поиска слов, начинающихся с фразы

Я пытаюсь создать функцию поиска для своего веб-сайта с помощью Elastic Search и NEST. Вы можете увидеть мой код ниже, и я получаю результаты, если ищу полные (и почти полные) слова. То есть, если я ищу «пахта» или «пахта», я получаю совпадение с моим документом, содержащим слово «пахта».

Однако то, что я пытаюсь сделать, это то, что если я ищу «масло», у меня должен быть результат со всеми тремя документами, в которых есть слова, начинающиеся с «масла». Я думал, что это было решено с помощью FuzzyLikeThis?

Может ли кто-нибудь увидеть, что я делаю неправильно, и указать мне правильное направление?

Я создал консольное приложение и полный код, который вы можете увидеть здесь:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nest;
using Newtonsoft.Json;

namespace ElasticSearchTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var indexSettings = new IndexSettings();
            indexSettings.Analysis.Analyzers["text-en"] = new SnowballAnalyzer { Language = "English" };

            ElasticClient.CreateIndex("elastictesting", indexSettings);

            var testItem1 = new TestItem {
                Id = 1,
                Name = "Buttermilk"
            };
            ElasticClient.Index(testItem1, "elastictesting", "TestItem", testItem1.Id);

            var testItem2 = new TestItem {
                Id = 2,
                Name = "Buttercream"
            };
            ElasticClient.Index(testItem2, "elastictesting", "TestItem", testItem2.Id);

            var testItem3 = new TestItem {
                Id = 3,
                Name = "Butternut"
            };
            ElasticClient.Index(testItem3, "elastictesting", "TestItem", testItem3.Id);

            Console.WriteLine("Write search phrase:");
            var searchPhrase = Console.ReadLine();
            var searchResults = Search(searchPhrase);

            Console.WriteLine("Number of search results: " + searchResults.Count());
            foreach (var item in searchResults) {
                Console.WriteLine(item.Name);
            }

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }

        private static List<TestItem> Search(string searchPhrase)
        {
            var query = BuildQuery(searchPhrase);

            var result = ElasticClient
                .Search(query)
                .Documents
                .Select(d => d)
                .Distinct()
                .ToList();

            return result;
        }

        public static ElasticClient ElasticClient
        {
            get
            {
                var localhost = new Uri("http://localhost:9200");
                var setting = new ConnectionSettings(localhost);
                setting.SetDefaultIndex("elastictesting");
                return new ElasticClient(setting);
            }
        }

        private static SearchDescriptor<TestItem> BuildQuery(string searchPhrase)
        {
            var querifiedKeywords = string.Join(" AND ", searchPhrase.Split(' '));

            var filters = new BaseFilter[1];

            filters[0] = Filter<TestItem>.Bool(b => b.Should(m => m.Query(q =>
                q.FuzzyLikeThis(flt =>
                    flt.OnFields(new[] {
                        "name"
                    }).LikeText(querifiedKeywords)
                    .PrefixLength(2)
                    .MaxQueryTerms(1)
                    .Boost(2))
                )));

            var searchDescriptor = new SearchDescriptor<TestItem>()
                .Filter(f => f.Bool(b => b.Must(filters)))
                .Index("elastictesting")
                .Type("TestItem")
                .Size(500);

            var jsons = JsonConvert.SerializeObject(searchDescriptor, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
            return searchDescriptor;
        }
    }

    class TestItem {
        public int Id { get; set; }
        [ElasticProperty(Analyzer = "text-en", Index = FieldIndexOption.analyzed)]
        public string Name { get; set; }
    }
}

Отредактировано 01.04.2014 11:18

Что ж, в итоге я использовал MultiMatch и QueryString, так что теперь мой код выглядит так. Надеюсь, это поможет кому-нибудь в будущем. Кроме того, я добавил свойство Description в свой TestItem, чтобы проиллюстрировать множественное совпадение.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nest;
using Newtonsoft.Json;

namespace ElasticSearchTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var indexSettings = new IndexSettings();

            ElasticClient.CreateIndex("elastictesting", indexSettings);

            var testItem1 = new TestItem {
                Id = 1,
                Name = "Buttermilk",
                Description = "butter with milk"
            };
            ElasticClient.Index(testItem1, "elastictesting", "TestItem", testItem1.Id);

            var testItem2 = new TestItem {
                Id = 2,
                Name = "Buttercream",
                Description = "Butter with cream"
            };
            ElasticClient.Index(testItem2, "elastictesting", "TestItem", testItem2.Id);

            var testItem3 = new TestItem {
                Id = 3,
                Name = "Butternut",
                Description = "Butter with nut"
            };
            ElasticClient.Index(testItem3, "elastictesting", "TestItem", testItem3.Id);

            Console.WriteLine("Write search phrase:");
            var searchPhrase = Console.ReadLine();
            var searchResults = Search(searchPhrase);

            Console.WriteLine("Number of search results: " + searchResults.Count());
            foreach (var item in searchResults) {
                Console.WriteLine(item.Name);
                Console.WriteLine(item.Description);
            }

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }

        private static List<TestItem> Search(string searchPhrase)
        {
            var query = BuildQuery(searchPhrase);

            var result = ElasticClient
                .Search(query)
                .Documents
                .Select(d => d)
                .Distinct()
                .ToList();

            return result;
        }

        public static ElasticClient ElasticClient
        {
            get
            {
                var localhost = new Uri("http://localhost:9200");
                var setting = new ConnectionSettings(localhost);
                setting.SetDefaultIndex("elastictesting");
                return new ElasticClient(setting);
            }
        }

        private static SearchDescriptor<TestItem> BuildQuery(string searchPhrase)
        {
            var searchDescriptor = new SearchDescriptor<TestItem>()
                .Query(q => q
                    .MultiMatch(m =>
                    m.OnFields(new[] {
                        "name",
                        "description"
                    }).QueryString(searchPhrase).Type(TextQueryType.PHRASE_PREFIX)
                    )
                )
                .Index("elastictesting")
                .Type("TestItem")
                .Size(500);

            var jsons = JsonConvert.SerializeObject(searchDescriptor, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

            return searchDescriptor;
        }
    }

    class TestItem {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

person Kidde82    schedule 01.04.2014    source источник
comment
Я не очень понимаю, чего вы пытаетесь достичь. Вы хотите найти «масло» и получить результаты, содержащие «пахту»?   -  person Geert-Jan    schedule 01.04.2014
comment
Когда я ищу масло, я хочу получить все слова, которые начинаются с масла. Так что в моем примере кода он должен вернуть все три, потому что они начинаются с масла.   -  person Kidde82    schedule 01.04.2014


Ответы (2)


Вместо использования FuzzyLikequery.. используйте префиксный запрос, это быстрее и точнее..! для получения дополнительной информации см. < /а>

curl -XPOST "http://localhost:9200/try/indextype/_search" -d'
{
"query": {
    "prefix": {
       "field": {
          "value": "Butter"
       }
    }
}
}'

создайте вышеуказанный запрос в NEST и попробуйте еще раз ..!

person BlackPOP    schedule 01.04.2014

Это не имеет ничего общего с FuzzyLikeThis.

Вы можете использовать префиксный запрос, как это было предложено @BlackPOP из коробки. Вы также можете выбрать использование EdgeNGrams, это будет токенизировать ваш ввод во время индексации. В результате производительность выше по сравнению с prefixquery, что компенсируется увеличением размера индекса.

Следует иметь в виду, что prefixquery работает только с неанализируемыми полями, поэтому, если вы хотите выполнять какой-либо анализ во время индексации, вам, вероятно, лучше использовать EdgeNGrams.

Пожалуйста, прочитайте об анализаторах и т. д., если вы не знаете, что это такое. Некоторые ссылки:

См. Как я могу выполнить поиск по префиксу в ElasticSearch в дополнение к общей строке запроса? для аналогичного вопроса.

person Geert-Jan    schedule 01.04.2014
comment
Спасибо, что разъяснили и указали мне правильное направление. Я посмотрю! - person Kidde82; 02.04.2014