Статьи

Полезные данные аккуратны, но где полный пример для Solr?

Когда я обсуждаю полезные данные в Solr, я немного расстраиваюсь из-за отсутствия примера, который я мог бы найти, который дал мне все части в одном месте. Поэтому я решил создать его для Solr 4.0+ (на самом деле, 4.8.1 на момент написания этой статьи, но это должно относиться ко всей строке кода 4x). Есть много полезных фрагментов, наш собственный Грант Ингерсолл показал, как использовать полезные нагрузки в 2009 году   на уровне Lucene.

С тех пор полезные нагрузки были добавлены в Solr… вроде. Существует DelimitedPayloadTokenFilterFactory, которую можно использовать при построении цепочки анализа в schema.xml, которая будет принимать маркеры полезных данных с разделителями и сохранять полезные данные вместе с термином. Это fieldType и field даже в стандартном выпуске.

Вопрос, однако, в том, как вы  используете полезные нагрузки при запросе в Solr? В этом посте представлен полный пример.

Сначала краткий обзор

Полезные данные — это способ связать числовое значение с термином. Поэтому, когда термин в запросе совпадает с термином в документе, у вас также есть числовое значение, доступное для использования при оценке. Существует множество вариантов использования полезных нагрузок, вот некоторые из них:

  1. Части речи. Допустим, вы хотите весить существительные тяжелее, чем прилагательные. Оставляя в стороне проблему распознавания существительных и прилагательных … скажем так, вы можете. Вы можете связать вес, который вы можете затем включить в оценку, используя больший вес для существительных, которые соответствуют терминам в поиске.
  2. Эвристически обнаруженные корреляции, также известные как «секретный соус». Вы проанализировали шаблоны использования и обнаружили, что если в начальной поисковой фразе содержится слово «рыбалка», существует высокая вероятность того, что пользователь купит приманку, а не искатель глубины. Во время приема, когда вы найдете слово «рыбалка» в описании чего-то, что вы классифицировали как «приманка», вы добавляете некоторый вес этому термину.
  3. Всякий раз, когда вы можете соотнести какое-либо поведение с определенными терминами, вы можете взвесить эти термины более весомо в расчетах оценки документов Solr. Этот вид обработки может быть очень сложным в вычислительном отношении и, следовательно, не производительным во время поиска. Если вы можете переложить эту обработку на индексирование времени с помощью добавления полезных нагрузок, вы можете использовать результаты вычисления этих корреляций и по-прежнему выполнять эффективный поиск.

Схема шагов

Помните, я оставляю в стороне,
как вы делаете корреляции здесь.

  1. Добавьте полезную нагрузку к термину в документе.
  2. Измените файл schema.xml, чтобы позволить вам использовать эту полезную нагрузку.
  3. Измените свой solrconfig.xml, чтобы распознать новый анализатор запросов, который вы собираетесь написать.
  4. Напишите новый класс подобия для поля Payloaded.
  5. Используйте новый анализатор запросов в запросах.

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

Добавьте оплаченный термин в документ.

На самом деле это самая простая часть, просто используйте разделитель трубы. Ваш термин выглядит как «рыбалка | 5,0».

Измените свой файл schema.xml.

Ваш файл schema.xml будет иметь два изменения. Схема по умолчанию поставляется с типом поля «полезная нагрузка». Это будет хорошо работать для _ingesting_ данных, но нам нужно сделать одно изменение, чтобы использовать это при оценке ; добавить новое пользовательское сходство с типом поля. Ваш <fieldType> будет выглядеть так:

<fieldtype name="payloads" stored="false" indexed="true" class="solr.TextField" >
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.DelimitedPayloadTokenFilterFactory" encoder="float"/>
</analyzer>
<similarity class="payloadexample.PayloadSimilarityFactory" />
</fieldtype>

Класс подобия будет неким пользовательским кодом, который вы увидите позже. Его работа состоит в том, чтобы взять полезную нагрузку и использовать ее для влияния на оценку документа. Особенность Solr 4.x заключается в том, что вы можете определять пользовательские сходства для отдельных полей, что мы и сделали здесь.

Есть еще одно изменение, которое нужно внести в схему. Внизу внизу вы можете увидеть комментарий о пользовательских сходствах. Добавьте эту строку:

<similarity class="solr.SchemaSimilarityFactory"/>

Это то, что позволит найти ваше сходство в <fieldType>.

Измените свой файл solrconfig.xml.

Все идет нормально. Но как вы на самом деле используете это? Оказывается, что если вы не создадите свой собственный анализатор, по умолчанию оценка полезной нагрузки будет просто возвращать 1.0f. Поэтому вам нужно создать парсер, который будет фактически использовать значение. Вам понадобятся два изменения; определите путь к lib для вашего jar и определите новый анализатор запросов. Это выглядит так:

<lib dir="path_to_jar_file_containing_custom_code" regex=".*\.jar">

and then:  

<queryParser name="myqp" class="payloadexample.PayloadQParserPlugin" />

Напишите новый класс подобия и анализатор запросов для поля Payloaded.

Хорошо, вот код Это самая длинная часть поста, так что терпите меня. Или пропустите до конца и скопируйте / вставьте это позже. В моем примере кода есть два файла, которые я поместил в одну и ту же банку. Сначала PayloadQParserPlugin (см. Изменения в solrconfig.xml).

package payloadexample;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.payloads.AveragePayloadFunction;
import org.apache.lucene.search.payloads.PayloadTermQuery;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.parser.QueryParser;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QParserPlugin;
import org.apache.solr.search.QueryParsing;
import org.apache.solr.search.SyntaxError;

// Just the factory class that doesn't do very much in this 
// case but is necessary for registration in solrconfig.xml. public class PayloadQParserPlugin extends QParserPlugin { @Override public void init(NamedList args) { // Might want to do something here if you want to preserve information for subsequent calls! } @Override public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { return new PayloadQParser(qstr, localParams, params, req); } } // The actual parser. Note that it relies heavily on the superclass class PayloadQParser extends QParser { PayloadQueryParser pqParser; public PayloadQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); } // This is kind of tricky. The deal here is that you do NOT
// want to get into all the process of parsing parentheses, // operators like AND/OR/NOT/+/- etc, it's difficult. So we'll
// let the default parsing do all this for us. // Eventually the complex logic will resolve to asking for
// fielded query, which we define in the PayloadQueryParser // below. @Override public Query parse() throws SyntaxError { String qstr = getString(); if (qstr == null || qstr.length() == 0) return null; String defaultField = getParam(CommonParams.DF); if (defaultField == null) { defaultField = getReq().getSchema().getDefaultSearchFieldName(); } pqParser = new PayloadQueryParser(this, defaultField); pqParser.setDefaultOperator (QueryParsing.getQueryParserDefaultOperator(getReq().getSchema(), getParam(QueryParsing.OP))); return pqParser.parse(qstr); } @Override public String[] getDefaultHighlightFields() { return pqParser == null ? new String[]{} : new String[] {pqParser.getDefaultField()}; } } // Here's the tricky bit. You let the methods defined in the
// superclass do the heavy lifting, parsing all the // parentheses/AND/OR/NOT/+/- whatever. Then, eventually, when
// all that's resolved down to a field and a term, and // BOOM, you're here at the simple "getFieldQuery" call. // NOTE: this is not suitable for phrase queries, the limitation
// here is that we're only evaluating payloads for // queries that can resolve to combinations of single word
// fielded queries. class PayloadQueryParser extends QueryParser { PayloadQueryParser(QParser parser, String defaultField) { super(parser.getReq().getCore().getSolrConfig().luceneMatchVersion, defaultField, parser); } @Override protected Query getFieldQuery(String field, String queryText, boolean quoted) throws SyntaxError { SchemaField sf = this.schema.getFieldOrNull(field); // Note that this will work for any field defined with the // <fieldType> of "payloads", not just the field "payloads". // One could easily parameterize this in the config files to // avoid hard-coding the values. if (sf != null && sf.getType().getTypeName().equalsIgnoreCase("payloads")) { return new PayloadTermQuery(new Term(field, queryText), new AveragePayloadFunction(), true); } return super.getFieldQuery(field, queryText, quoted); } }

Что с AveragePayloadFunction ()? Хорошо, представьте, что у вас есть несколько терминов в одном документе, каждый из которых имеет разную полезную нагрузку. Эта функция будет «делать правильные вещи», если среднее из этих значений «правильное». Существуют некоторые предопределенные функции полезной нагрузки (все производные от PayloadFunction), которые «делают правильно» с полезными нагрузками в других случаях, например, min, max, которые вы также можете использовать. Или вы можете написать свой собственный, если ваши потребности отличаются.

Теперь PayloadS SimilarityFactory (см. Изменения в schema.xml).

package payloadexample;

import org.apache.lucene.analysis.payloads.PayloadHelper;
import org.apache.lucene.search.similarities.DefaultSimilarity;
import org.apache.lucene.search.similarities.Similarity;
import org.apache.lucene.util.BytesRef;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.schema.SimilarityFactory;

public class PayloadSimilarityFactory extends SimilarityFactory {
  @Override
  public void init(SolrParams params) {
    super.init(params);
  }

  @Override
  public Similarity getSimilarity() {
    return new PayloadSimilarity();
  }
}

class PayloadSimilarity extends DefaultSimilarity {

  //Here's where we actually decode the payload and return it.
  @Override
  public float scorePayload(int doc, int start, int end, BytesRef payload) {
    if (payload == null) return 1.0F;
    return PayloadHelper.decodeFloat(payload.bytes, payload.offset);
  }
}

Помните, компьютеры глупы. Где-то вы должны сказать компьютеру, чтобы он взял полезные данные из термина и использовал их. Это все, что вы здесь делаете. По умолчанию ScorePayload возвращает только 1,0, поэтому, если вы не сделаете этот шаг, вам будет интересно, почему ваши полезные данные не имеют никакого эффекта.

Используйте новый анализатор запросов в запросах.

Сначала краткий обзор того, что мы уже сделали:

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

Однако в этот момент, как и в остальной части Solr, на самом деле 
использование этой информации является вопросом наличия парсера запросов, который фактически вызывает ее. Парсер запросов полезной нагрузки, как и любой другой парсер запросов, edismax, standard, term, фраза, raw, nested и т. Д. (См.
Документацию CWiki ). Это все еще нужно призвать.

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

http://localhost:8983/solr/collection1/query?defType=myqp&q=payloads:(electronics memory)

Вывод

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

  • Он плохо обрабатывает фразы. Это создаст «TermQuery» из всей фразы, а это не то, что вам нужно.
  • Это требует некоторого вложения кода, было бы лучше иметь встроенную поддержку в Solr.
  • Он не применяется ко всем различным анализаторам запросов, он ограничен только что определенным qparser.
    • Я поболтал с Крисом Хостеттером (он мой консультант по всем вопросам, связанным с синтаксическим анализатором запросов), и у этого подхода есть одна заслуга:

    • ..вы можете использовать этот qparser с любым типом поля — пользовательским, который добавляет полезные нагрузки, PreAnalyzedField, TextField, используя DelimitedPayloadTokenFilterFactory, что угодно …

    • Другой подход заключается в создании пользовательского FieldType. Это преимущество (опять же цитирование)

    • Если вы используете собственный подход FieldType, то вы автоматически получаете запросы на основе полезной нагрузки от большинства существующих анализаторов запросов — но вы должны решить (и в результате: ограничить), когда / как / почему полезные нагрузки добавляются к вашим терминам в Логика FieldType

Так что у каждого есть свои достоинства, это
просто вопрос их реализации в Solr. Siiighhh. Наряду с другими 50 вещами, которые я хотел бы сделать.