Zend Search Lucene не возвращает ожидаемых результатов - PullRequest
0 голосов
/ 13 января 2012

Я создал простой индекс, используя Zend_Search_Lucene для поиска по списку названий компаний, так как я хочу иметь возможность предложить более интеллектуальный поиск, чем простой MySQL «LIKE% query%». Я использовал приведенный ниже код, где «companyname» - это название компании, а «document_id» - уникальный идентификатор для каждого документа (я знаю, что Lucene назначает его внутренне, но я понимаю, что это может измениться, тогда как мой идентификатор документа будет быть статичным).

$index = Zend_Search_Lucene::create('test-index');

$document = new Zend_Search_Lucene_Document();
$document->addField(Zend_Search_Lucene_Field::UnIndexed('document_id', 1));
$document->addField(Zend_Search_Lucene_Field::Text('companyname', 'XYZ Holdings'));
$index->addDocument($document);

$document = new Zend_Search_Lucene_Document();
$document->addField(Zend_Search_Lucene_Field::UnIndexed('document_id', 2));
$document->addField(Zend_Search_Lucene_Field::Text('companyname', 'X.Y.Z. (Holdings) Ltd'));
$index->addDocument($document);

$document = new Zend_Search_Lucene_Document();
$document->addField(Zend_Search_Lucene_Field::UnIndexed('document_id', 3));
$document->addField(Zend_Search_Lucene_Field::Text('companyname', 'X Y Z Ltd'));
$index->addDocument($document);

$index->commit();

Однако, когда я запускаю следующий код, чтобы найти все компании с вариантами «XYZ» в названии:

$index = Zend_Search_Lucene::open('test-index');
$hits = $index->find('companyname:XYZ');
foreach ($hits as $hit)
{
  print "ID: " . $hit->document_id . "\n";
  print "Score: " . $hit->score . "\n";
  print "Company: " . $hit->companyname . "\n";
}

Я получаю следующее:

ID: 1
Score: 1
Company: XYZ Holdings

Я ожидал, что XYZ совпадет со всеми документами, так как смысл этого поиска состоит в том, чтобы подобрать компании, которые имеют одно и то же имя, но слегка отличающиеся пунктуацией, что не может быть учтено в простом предложении LIKE. Есть ли причина, по которой Lucene не соответствует всем документам, и могу ли я что-то сделать, чтобы это исправить?

У меня возникает такая же проблема, если я ищу 'companyname: "x.y.z holding"' - это не соответствует ничего, кроме 'companyname: "x.y.z holdings"'. Я ожидаю, что Lucene определит, что «владение» и «владение» достаточно близко, чтобы считаться совпадением.

Я вполне уверен, что все документы проиндексированы, потому что, если я ищу 'X.Y.Z', я получаю совпадения для документов 2 и 3.

Редактировать: Забыл упомянуть версию PHP (5.3.5-1ubuntu7.4 с Suhosin-Patch) и версию Zend Framework (1.11.10-0ubuntu1).

Ответы [ 2 ]

1 голос
/ 17 января 2012

Вы можете решить эту проблему, предварительно обработав свой контент перед его индексацией. Lucene будет работать с токенами, и вам нужно обращаться с ними как с отдельными единицами. В прошлом я делал что-то похожее, чтобы соответствовать номерам версий, чтобы при поиске 2.0 также получался, например, 2.0.3, но не 1.2.0.

Функция toCanonical () здесь не идеальна. Я рекомендую вам написать свой собственный и создать тестовый набор, чтобы убедиться, что он преобразует текст так, как вы ожидаете. Что он делает, так это строит более длинную строку, группируя вещи, которые выглядят как аббревиатуры. Вы также можете позвонить по поисковому запросу.

Вам нужно искать имя компании_каноническое вместо названия компании.

Может быть более чистый способ сделать это в качестве фильтра в Zend Lucene. Вы также можете использовать стеммер для обработки множественных форм и тому подобное. Там уже есть реализация портера-стеммера. http://codefury.net/2008/06/a-stemming-analyzer-for-zends-php-lucene/

function toCanonical($text)
{
    $out = $text . ' ';
    $step = $text;

    $pattern = '/([A-Z])[\s\.-]([A-Z])([^a-z])/';
    while (preg_match($pattern, $step)) {
        $step = preg_replace($pattern, '$1$2$3', $step);
        $out .= $step . ' ';
    }

    return $out;
}

function createDocument($id, $companyName)
{
    $canonicalName = toCanonical($companyName);

    $document = new Zend_Search_Lucene_Document();
    $document->addField(Zend_Search_Lucene_Field::UnIndexed('document_id', $id));
    $document->addField(Zend_Search_Lucene_Field::Text('companyname', $companyName));
    $document->addField(Zend_Search_Lucene_Field::UnStored('companyname_canonical', $canonicalName));

}

$index->addDocument(createDocument(1, 'XYZ Holdings'));
$index->addDocument(createDocument(1, 'X.Y.Z. (Holding) Company'));
0 голосов
/ 18 января 2012

при индексации «XYZ Holdings» (скажем, вы используете стандартный анализатор), тогда будет два токена «xyz» и «holdings»

В случае «XYZ (Holdings) Ltd» & будетbe "x", "y", "z", "holdings" и "ltd"

В случае "XYZ Ltd" токенами будут "x", "y", "z" и "ltd"

Когда вы вводите название компании:" XYZ "или название компании:" XYZ ", оба варианта 2 и 3 совпадают.Lucene никак не может знать, что XYZ в случае 1 также является аббревиатурой.

Я думаю, вам следует написать свой собственный токенизатор для генерации одинаковых токенов для "XYZ", "XYZ" и "XYZ", но это можетмешать другим заглавным словам, которые не являются аббревиатурами

...