2017-02-21 31 views
0

Мы использовали Lucene.NET в течение нескольких лет для поиска текста, извлеченного из файлов, на основе пользовательского ввода условий поиска. Тем не менее, мы недавно столкнулись с проблемой, о которой сообщается клиентом, где поиск терминов с несколькими косыми чертами не возвращает совпадений.Запрос Lucene.NET с несколькими косыми чертами

Примером может служить индексное значение SB/ABC/1234-123, а пользователь вводит SB/* для соответствия всем документам с этим префиксом. Однако на основе этого запроса результаты не возвращаются. Нечетная часть заключается в том, что поиск ABC/* действительно возвращает документ со значением SB/ABC/1234-123, полностью игнорируя компонент SB/.

Изначально проблема была сообщили с комбинацией прямого слэша и шаблона (SB/* не возвращать спички для SB/1234-123), но это было адресовано, используя QueryParser с KeywordAnalyzer в дополнение к предыдущему QueryParser с только StandardAnalyzer.

Здесь используется текущий код (упрощенный для ключевых элементов, которые могут воспроизвести проблему).

var reader = IndexReader.Open(FSDirectory.Open(new DirectoryInfo(indexPath)), true); 
var searcher = new IndexSearcher(reader); 
var mainQuery = new BooleanQuery(); 

// The analyzer and parser for searching the index fields with full stop-words and tokenizers 
var fieldAnalyzer = new StandardAnalyzer(LuceneVersion); 
var fieldParser = new MultiFieldQueryParser(LuceneVersion, reader.GetFieldNames(IndexReader.FieldOption.ALL).ToArray(), fieldAnalyzer); 

// The analyzer and parser for searching the index fields using no stop words or tokenizers 
var fieldKeywordAnalyzer = new KeywordAnalyzer(); 
var fieldKeywordParser = new MultiFieldQueryParser(LuceneVersion, reader.GetFieldNames(IndexReader.FieldOption.ALL).ToArray(), fieldKeywordAnalyzer); 

// Build and append the Standard and Keyword query clauses together for the whole field value query to pick up all relevant results 
var fieldQuery = fieldParser.Parse(textCriteria); 
var fieldKeywordQuery = fieldKeywordParser.Parse(textCriteria); 

var fieldBooleanQuery = new BooleanQuery 
{ 
    {fieldQuery, Occur.SHOULD}, 
    {fieldKeywordQuery, Occur.SHOULD} 
}; 

mainQuery.Add(fieldBooleanQuery, Occur.MUST); 

var hits = searcher.Search(mainQuery, reader.NumDocs()); 

Фактическая разобранный запрос в пределах mainQuery в момент вызова searcher.Search+((Title:sb/abc/*) (Title:sb/abc/*)). В этом случае оба предложения BooleanQuery оказываются одинаковыми. Инструмент Люк обычно используется для работы с индексами Lucene кажется, думает, что это недопустимый синтаксис при использовании KeywordAnalyzer (игнорируя токенизированный аспект на данный момент):

Cannot parse '+((Title:sb/abc/*) (Title:sb/abc/*))': '*' or '?' not allowed as first character in WildcardQuery.

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

Наше текущее требование состоит в том, что оно должно поддерживать поиск токенизированных/стоп-слов (для текстовых фраз и т. Д.), А также точные соответствия (мы храним много номеров счетов и т. Д., Которые не должны быть обозначены) на тех же самых полях одновременно и обе управляющие символы. Запрос SB/* является примером поиска подстановочных знаков по сценарию с точным соответствием.

Надеюсь, это имеет смысл. При необходимости я могу добавить дополнительное разъяснение.

Редактировать: Наши данные организованы в ряд столбцов, которые могут хранить любое текстовое значение. Пример: некоторые клиенты помещают значение, которое является уникальным значением/идентификатором (номер счета и т. Д.) В поле 1 для doctype A. Тот же клиент может использовать поле 1 в виде текстового блока (полное имя и т. Д.) Для doctype B. Doctypes классификации документов, которые описывают на высоком уровне, что конкретный документ должен представлять, например, счет-фактура, в PO и т.д. Пример данных:

DocId DocType Field1 Field2 Field3, etc 
1234 A  SB/2567 John Doe 
5678 B  Jane Doe 90210 
3456 A  ABC/5678 Bobby Lee 
+0

StandardAnalyzer обрабатывает ваш пример номер счета SB/ABC/1234-123 как две лексемы: [сб] [а/1234-123]. Вот почему «ABC/*» соответствует. –

+0

Вот почему мы также добавили KeywordAnalyzer, чтобы он просматривал как токенированные (стандартные) значения, так и не-токенизированные (ключевое слово). В этом сценарии, похоже, игнорируется KeywordAnalyzer. – Kettch19

+0

@ DanielBrixen Извините, не пометил вас в своем ответе, и он не позволит мне его редактировать. – Kettch19

ответ

0

как @Daniel Бриксен отметил, StandardAnalyzer не делать то, что вы надеетесь, что это так.

Вы должны рассмотреть возможность прокатки собственного анализатора. Here's a .net fiddle, который демонстрирует SlashAnalyzer (который, вероятно, ведет себя так, как вы хотите), а также демонстрирует, что производит StandardAnalyzer.

Вот что вы можете сказать о коре/анализаторе/токенизаторе.

public sealed class SlashAnalyzer : Analyzer 
{ 
    public override TokenStream TokenStream(System.String fieldName, System.IO.TextReader reader) 
    { 
     return new LowerCaseFilter(new SlashTokenizer(reader)); 
    } 

    public override TokenStream ReusableTokenStream(System.String fieldName, System.IO.TextReader reader) 
    { 
     Tokenizer tokenizer = (Tokenizer)PreviousTokenStream; 
     if (tokenizer == null) 
     { 
      tokenizer = new SlashTokenizer(reader); 
      PreviousTokenStream = tokenizer; 
     } 
     else 
      tokenizer.Reset(reader); 
     return tokenizer; 
    } 
} 

public class SlashTokenizer : CharTokenizer 
{ 
    public SlashTokenizer(System.IO.TextReader @in) : base(@in) 
    { 
    } 

    public SlashTokenizer(AttributeSource source, System.IO.TextReader @in) : base(source, @in) 
    { 
    } 

    public SlashTokenizer(AttributeFactory factory, System.IO.TextReader @in) : base(factory, @in) 
    { 
    } 

    protected override bool IsTokenChar(char c) 
    { 
     return c != '/'; 
    } 
} 
+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^