как получить точные результаты поиска с несколькими строками запроса в спящем поиске - PullRequest
0 голосов
/ 29 августа 2018

Мы используем поисковую форму hibernate 5.9.2 и хотели бы получить точные результаты поиска, такие как:

Если пользователь начинает с

John -> all data with John should display
John Murphy -> all data with John murphy should display
John murphy Columbia -> Only data with John murphy Columbia should display 
John murphy Columbia SC -> Only data with John murphy Columbia should display  
John murphy Columbia SC 29201 -> Only data with John murphy Columbia SC 29201

29201 -> Only data with 29201 as zipcode should be displayed.
and so on...

В основном мы пытаемся выполнить поиск по точным записям из нескольких полей индекса.

У нас есть объект, содержащий эти данные в таких полях, как Имя, Адрес1, Адрес2, Город, Почтовый индекс, Штат.

Мы пробовали bool() (с must / must) запросами, но поскольку мы не уверены, какие данные пользователь будет вводить первыми, это может быть почтовый индекс, штат, город в любом месте текстового поиска.

Пожалуйста, поделитесь своими знаниями / логикой в ​​отношении анализаторов / стратегий, которые мы можем использовать для достижения этой цели с помощью hibernate search / lucene.

Ниже приведена структура индекса:

> {
>         "_index" : "client_master_index_0300",
>         "_type" : "com.csc.pt.svc.data.to.Basclt0300TO",
>         "_id" : "518,1",
>         "_score" : 4.0615783,
>         "_source" : {
>           "id" : "518,1",
>           "cltseqnum" : 518,
>           "addrseqnum" : "1",
>           "addrln1" : "Dba",
>           "addrln2" : "Betsy Evans",
>           "city" : "SDA",
>           "state" : "SC",
>           "zipcode" : "89756-4531",
>           "country" : "USA",
>           "basclt0100to" : {
>             "cltseqnum" : 518,
>             "clientname" : "Betsy Evans",
>             "longname" : "Betsy Evans",
>             "id" : "518"
>           },
>           "basclt0900to" : {
>             "cltseqnum" : 518,
>             "id" : "518"
>           }
>         }
>       }

Ниже ввод

Акаш Агравал 29021

ответ содержит все записи, соответствующие akash, agrwal, 29,2, 1, 01 и т. Д ...

То, чего мы пытаемся достичь, - это точный результат поиска, в отношении указанного выше поискового ввода результаты должны содержать только данные с Akash Agrawal 29201, а не другие данные.

Мы в основном ищем имя basclt0100to.longname, addrln1, addrln2, город, штат, почтовый индекс, страну.

Определение индекса ниже

> {
>   "client_master_index_0300" : {
>     "aliases" : { },
>     "mappings" : {
>       "com.csc.pt.svc.data.to.Basclt0300TO" : {
>         "dynamic" : "strict",
>         "properties" : {
>           "addrln1" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrln2" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrln3" : {
>             "type" : "text",
>             "store" : true
>           },
>           "addrseqnum" : {
>             "type" : "text",
>             "store" : true
>           },
>           "basclt0100to" : {
>             "properties" : {
>               "clientname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "cltseqnum" : {
>                 "type" : "long",
>                 "store" : true
>               },
>               "firstname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "id" : {
>                 "type" : "keyword",
>                 "store" : true,
>                 "norms" : true
>               },
>               "longname" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "midname" : {
>                 "type" : "text",
>                 "store" : true
>               }
>             }
>           },
>           "basclt0900to" : {
>             "properties" : {
>               "cltseqnum" : {
>                 "type" : "long",
>                 "store" : true
>               },
>               "email1" : {
>                 "type" : "text",
>                 "store" : true
>               },
>               "id" : {
>                 "type" : "keyword",
>                 "store" : true,
>                 "norms" : true
>               }
>             }
>           },
>           "city" : {
>             "type" : "text",
>             "store" : true
>           },
>           "cltseqnum" : {
>             "type" : "long",
>             "store" : true
>           },
>           "country" : {
>             "type" : "text",
>             "store" : true
>           },
>           "id" : {
>             "type" : "keyword",
>             "store" : true
>           },
>           "state" : {
>             "type" : "text",
>             "store" : true
>           },
>           "zipcode" : {
>             "type" : "text",
>             "store" : true
>           }
>         }
>       }
>     },
>     "settings" : {
>       "index" : {
>         "creation_date" : "1535607176216",
>         "number_of_shards" : "5",
>         "number_of_replicas" : "1",
>         "uuid" : "x4R71LNCTBSyO9Taf8siOw",
>         "version" : {
>           "created" : "6030299"
>         },
>         "provided_name" : "client_master_index_0300"
>       }
>     }
>   }
> }

До сих пор я пытался использовать edgengraanalyzer, стандартный анализатор запросов lucene. Я пробовал с помощью запроса Bool (), запроса по ключевому слову, фразы, пробовал все, что доступно в документации.

Но я уверен, что мне не хватает стратегии / логики, которую мы должны использовать.

Ниже приведен текущий запрос, который я использую и который дает результаты прикрепленного снимка

 Query finalQuery = queryBuilder.simpleQueryString()
            .onFields("basclt0100to.longname", "addrln1", "addrln2" 
                ,"city","state","zipcode", "country")
            .withAndAsDefaultOperator()
            .matching(lowerCasedSearchTerm)
            .createQuery();

        FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);
        fullTextQuery.setMaxResults(this.data.getPageSize()).setFirstResult(this.data.getPageSize());

        List<String> projectedFields = new ArrayList<String>();
        for (String fieldName : projections)
                projectedFields.add(fieldName);

        @SuppressWarnings("unchecked")
        List<Cltj001ElasticSearchResponseTO> results = fullTextQuery.
        setProjection(projectedFields.toArray(new String[projectedFields.size()]))
        .setResultTransformer( new BasicTransformerAdapter() {
            @Override
            public Cltj001ElasticSearchResponseTO transformTuple(Object[] tuple, String[] aliases) {
                return   new Cltj001ElasticSearchResponseTO((String) tuple[0], (long) tuple[1],
                            (String) tuple[2], (String) tuple[3], (String) tuple[4],
                            (String) tuple[5],(String) tuple[6], (String) tuple[7], (String) tuple[8]);

            }
        })
        .getResultList();
        resultsClt0300MasterIndexList = results;

искал: akash 29201 & искал: akash 1 main

Здесь вы видите, что у нас есть все данные, содержащие akash, sh, 29, 292, 29201.

Ожидаемые результаты:

Акаш Агравал - 29201 Akash Agrawal - 1 главная улица, SC, 29201

В основном только точные данные, содержащие / соответствующие входной строке.

Использованные анализаторы: Указатель времени

    @AnalyzerDef(name = "autocompleteEdgeAnalyzer",

//Split input into tokens according to tokenizer
                tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
                         filters = {
                                   @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                                    @TokenFilterDef(factory = StopFilterFactory.class),
                                    @TokenFilterDef(
                                            factory = EdgeNGramFilterFactory.class, // Generate prefix tokens
                                            params = {
                                                    @Parameter(name = "minGramSize", value = "3"),
                                                    @Parameter(name = "maxGramSize", value = "3")
                                            }
                                    )
                            })

Переопределение времени запроса с помощью:

    @AnalyzerDef(name = "withoutEdgeAnalyzerFactory",

// Split input into tokens according to tokenizer
                tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
                         filters = {
                                    @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
                                    @TokenFilterDef(factory = LowerCaseFilterFactory.class),


                            }
                /*filters = {
                        // Normalize token text to lowercase, as the user is unlikely to
                        // care about casing when searching for matches
                        @TokenFilterDef(factory = PatternReplaceFilterFactory.class, params = {
                                @Parameter(name = "pattern", value = "([^a-zA-Z0-9\\.])"),
                                @Parameter(name = "replacement", value = " "),
                                @Parameter(name = "replace", value = "all") }),
                        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                        @TokenFilterDef(factory = StopFilterFactory.class) }*/)

Надеюсь, эти данные помогут.

1 Ответ

0 голосов
/ 30 августа 2018

Самое простое решение, требующее лишь небольшого изменения кода, заключается в использовании Occur.MUST вместо Occur.SHOULD в вашем логическом запросе. Тогда вы получите только документы, соответствующие каждому ключевому слову, вместо документов, соответствующих хотя бы одному ключевому слову, как в настоящее время.

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


Во-первых, вам не нужно разбивать входную строку самостоятельно; это работа Люсена (и Elasticsearch), во время так называемого «анализа текста». Перед началом использования Hibernate Search вы действительно должны понимать анализ текста.

Анализ текста, короче говоря, процесс превращения одной строки в «токены» (слова), которые можно использовать в полнотекстовом индексе.

Анализ текста выполняется в двух случаях (я упрощаю, но это более или менее так и происходит):

  • при индексации документов содержимое каждого поля анализируется, и Elasticsearch сохраняет результаты анализа (список токенов) в индексе.
  • при запросе строка запроса анализируется, и полнотекстовый движок будет искать каждый полученный токен в индексе.

Анализ текста состоит из трех этапов:

  • Фильтрация символов, которую я не буду подробно описывать, потому что она обычно пропускается, поэтому вам, вероятно, она не понадобится.
  • Токенизация, которая разбивает одну строку на несколько частей, называемых «токенами». Короче говоря, он извлекает слова из строки.
  • Фильтрация токенов, которая применяет преобразования к токенам, такие как превращение их в строчные, замена диакритических знаков на более простые эквиваленты ("é" => "e", "à" => "a", ...), расщепление токены разворачиваются в ngrams ("word" => ["w", "wo", "wor", "word"]) и т. д.

Цель этого текстового анализа - учесть более тонкие совпадения, чем просто "равные строки". В частности, он позволяет находить слова в документе, выполнять поиск без учета регистра, но также и гораздо более тонкий поиск, такой как «слова, начинающиеся с заданной строки» (с использованием EdgeNgramTokenFilter) или совпадения, казалось бы, несвязанных слов, таких как « Wi-Fi "и" Wi-Fi ".

Как будет вести себя поиск, зависит от того, какой анализатор вы использовали во время индексации и во время запроса. Как правило, один и тот же анализ применяется во время индексации и времени запроса, но в некоторых очень специфических, расширенных случаях использования (например, при использовании EdgeNGramTokenFilter) вам потребуется использовать немного другой анализатор при запросах.

Как вы можете видеть, Lucene / Elasticsearch уже делает то, что вы хотите, то есть разбивает входную строку на несколько «слов». Кроме того, если вы используете правильный анализатор, вам не нужно вводить строчную строку самостоятельно, так как Token Filter позаботится об этом.

Это все, что вам нужно понять, прежде чем начать использовать Hibernate Search.

Теперь об особенностях: проблема в том, что когда вы просто используете запрос .keyword(), строка действительно будет разбита на несколько слов, но Hibernate Search будет искать документы, которые соответствуют любому из этих слова. Это не то, что вам нужно: вы хотите искать документы, которые соответствуют всем этих слов.

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

Например:

Query finalQuery = queryBuilder.simpleQueryString()
         .onFields("basclt0100to.longname", "addrln1", "addrln2" 
             ,"city","state","zipcode", "country")
         .withAndAsDefaultOperator()
         .matching(searchTerms)
         .createQuery();

FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(finalQuery, Basclt0300TO.class);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...