Ответ 1
Прошло некоторое время с тех пор, как я работал напрямую с lucene, но то, что вы хотите, должно быть, изначально довольно простым. Базовое поведение запроса lucene очень похоже на запрос соответствия (query_string точно эквивалентен lucene, но совпадение очень близко). Я собрал небольшой пример, который работает только с lucene (7.2.1), если вы хотите попробовать. Основной код выглядит следующим образом:
public static void main(String[] args) throws Exception {
// Create the in memory lucence index
RAMDirectory ramDir = new RAMDirectory();
// Create the analyzer (has default stop words)
Analyzer analyzer = new StandardAnalyzer();
// Create a set of documents to work with
createDocs(ramDir, analyzer);
// Query the set of documents
queryDocs(ramDir, analyzer);
}
private static void createDocs(RAMDirectory ramDir, Analyzer analyzer)
throws IOException {
// Setup the configuration for the index
IndexWriterConfig config = new IndexWriterConfig(analyzer);
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
// IndexWriter creates and maintains the index
IndexWriter writer = new IndexWriter(ramDir, config);
// Create the documents
indexDoc(writer, "document-1", "hello planet mercury");
indexDoc(writer, "document-2", "hi PLANET venus");
indexDoc(writer, "document-3", "howdy Planet Earth");
indexDoc(writer, "document-4", "hey planet MARS");
indexDoc(writer, "document-5", "ayee Planet jupiter");
// Close down the writer
writer.close();
}
private static void indexDoc(IndexWriter writer, String name, String content)
throws IOException {
Document document = new Document();
document.add(new TextField("name", name, Field.Store.YES));
document.add(new TextField("body", content, Field.Store.YES));
writer.addDocument(document);
}
private static void queryDocs(RAMDirectory ramDir, Analyzer analyzer)
throws IOException, ParseException {
// IndexReader maintains access to the index
IndexReader reader = DirectoryReader.open(ramDir);
// IndexSearcher handles searching of an IndexReader
IndexSearcher searcher = new IndexSearcher(reader);
// Setup a query
QueryParser parser = new QueryParser("body", analyzer);
Query query = parser.parse("hey earth");
// Search the index
TopDocs foundDocs = searcher.search(query, 10);
System.out.println("Total Hits: " + foundDocs.totalHits);
for (ScoreDoc scoreDoc : foundDocs.scoreDocs) {
// Get the doc from the index by id
Document document = searcher.doc(scoreDoc.doc);
System.out.println("Name: " + document.get("name")
+ " - Body: " + document.get("body")
+ " - Score: " + scoreDoc.score);
}
// Close down the reader
reader.close();
}
Важные части распространения этого собирается быть анализатор и понимание Lucene синтаксиса анализатор запросов.
Analyzer
используется как индексированием, так и запросами, чтобы указать, как разбирать текст, чтобы они могли думать о тексте одинаково. Он устанавливает, как tokenize (что нужно разделить, будь то toLower() и т.д.). StandardAnalyzer
разбивается на пробелы и несколько других (у меня нет этой возможности), а также выглядит применительно к Lower().
QueryParser
собирается выполнить некоторые из этих работ для вас. Если вы видите выше в моем примере. Я делаю две вещи, я говорю синтаксическому анализатору, что такое поле по умолчанию, и передаю строку hey earth
. Парсер собирается превратить это в запрос, который выглядит как body:hey body:earth
. Это будет искать документы, которые имеют либо hey
либо earth
в body
. Два документа будут найдены.
Если мы пройдем hey AND earth
запрос будет разобран, чтобы выглядеть как +body:hey +body:earth
которого требуется, чтобы документы имели оба термина. Нулевые документы будут найдены.
Чтобы применить нечеткие опции, вы добавляете ~
к условиям, которые вы хотите быть нечеткими. Так что если запрос hey~ earth
он применит нечеткость к hey
и запрос будет выглядеть как body:hey~2 body:earth
. Три документа будут найдены.
Вы можете напрямую писать запросы, и синтаксический анализатор все еще обрабатывает все. Так что если вы передаете его hey name:\"document-1\"
(это лексемы расщепляется на -
) будет создать запрос как body:hey name:"document 1"
. Два документа будут возвращены, поскольку он ищет фразу document 1
(так как она все еще токенизирует на -
). Где, если бы я сделал hey name:document-1
он пишет body:hey (name:document name:1)
который возвращает все документы, поскольку все они имеют document
в качестве термина. Здесь есть некоторые нюансы.
Я попытаюсь немного подробнее рассказать о том, как они похожи. Ссылка на запрос соответствия. Elastic говорит, что основное различие будет: "Он не поддерживает префиксы имени поля, подстановочные знаки или другие" расширенные "функции". Вероятно, они выйдут из другого направления.
И запрос соответствия, и запрос lucene, при работе с анализируемым полем, будут брать строку запроса и применять к ней анализатор (tokenize it, toLower и т.д.). Поэтому они оба превратят HEY Earth
в запрос, который ищет термины hey
или earth
.
Запрос соответствия может установить operator
, предоставив "operator": "and"
. Это изменит наш запрос, чтобы искать hey
и earth
. Аналогия в lucene заключается в том, чтобы сделать что-то вроде parser.setDefaultOperator(QueryParser.Operator.AND);
Следующее - нечеткость. Оба работают с одинаковыми настройками. Я считаю, что эластичная "fuzziness": "AUTO"
эквивалентна lucene auto при применении ~
к запросу (хотя, я думаю, вам нужно добавить каждый термин, который немного громоздкий).
Запрос с нулевыми терминами представляется эластичной конструкцией. Если вам нужен параметр ALL, вам придется реплицировать совпадение со всем запросом, если анализатор запросов удалил все токены из запроса.
Обрезание часто выглядит связанным с CommonTermsQuery. Я не использовал это, поэтому у вас может быть какое-то копание, если вы хотите его использовать.
У Lucene есть фильтр синонимов, который будет применен к анализатору, но вам, возможно, потребуется построить карту самостоятельно.
Различия, которые вы можете найти, вероятно, будут забиты. Когда я бегу, они спрашивают, зачем hey earth
против люцина. Он получает документы-3 и документ-4, которые возвращаются со счетом 1.3862944
. Когда я запускаю запрос в форме:
curl -XPOST http://localhost:9200/index/_search?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
Я получаю те же документы, но со счетом 1.219939
. Вы можете запустить объяснение для обоих из них. В lucene путем печати каждого документа с помощью
System.out.println(searcher.explain(query, scoreDoc.doc));
И в эластичном состоянии, запрашивая каждый документ, как
curl -XPOST http://localhost:9200/index/docs/3/_explain?pretty -d '{
"query" : {
"match" : {
"body" : "hey earth"
}
}
}'
Я получаю некоторые отличия, но я не могу точно объяснить их. Я действительно получаю значение для документа 1.3862944
но fieldLength
отличается и влияет на вес.