2012-01-13 80 views
0

我已经创建了一个简单的索引,使用Zend_Search_Lucene搜索公司名称列表,因为我希望能够提供比简单的MySQL'LIKE%查询% ”。我使用了下面的代码,其中'companyname'是公司名称,'document_id'是每个文档的唯一ID(我知道Lucene在内部分配一个ID,但我知道可以更改,而我的文档ID将会静态)。Zend搜索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持有'' - 这与'companyname:'x.y.z持有''不匹配,我会得到同样的问题。我期望Lucene能够解决'持有'和'持股'足够接近被视为匹配。

我确信所有的文件进行索引,因为如果我搜索“XYZ”我得到的文件2和3

编辑匹配:忘了提的PHP版本(5.3.5-1ubuntu7.4与Suhosin-Patch)和Zend Framework版本(1.11.10-0ubuntu1)。

回答

1

您可以在编制索引之前通过预处理您的内容来解决问题。 Lucene将使用令牌,并且需要将它们视为单个单元。我在过去做了类似的事情来匹配版本号,所以搜索2.0也会提供2.0.3,但不是1.2.0。

toCanonical()函数并不完美。我建议你自己编写一个测试套件,以确保它能够按照您的预期转换文本。它所做的是通过将看起来像缩写词的东西分组来构建更长的字符串。您也可以在搜索查询中调用它。

您需要搜索companyname_canonical而不是公司名称。

在Zend Lucene中,可能有一种更简洁的方式来做它的过滤器。你可能也想用一个词干来处理复数形式等等。已经编写了一个porter stemmer的实现。 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

谢谢,这听起来像其他大写单词干扰Lucene没有提供我想要的东西,因为我认为它会阻止你,否则我只是重新发明轮子,我可以保证我会错过一些东西。 – pwaring 2012-01-17 09:21:48

+0

该词干可作为第三方插件使用。但是,我不认为你所要求的适合正常的干扰规则。 Java实现有一个更大的生态系统可供选择。 – 2012-01-17 12:56:10

0

当指数“XYZ控股”(说你正在使用standardAnalyzer),然后会有两个记号“某某”和“增持”

在“XYZ(集团)有限公司”案如果“XYZ有限公司”的标记为“x”,“y”,“z”和“z”,则“x”,“y”,“z”,“holdings”和“ ltd“

当您发出公司名称:”XYZ“或公司名称:”XYZ“时,情况2和情况3都匹配。 lucene没有办法知道情况1中的XYZ也是首字母缩写词。

我想你应该写自己的标记生成器生成的“XYZ”,“某某”和“XYZ”相同的标记,但是这可能与不是缩略词

+0

“没有办法lucene可以知道情况1中的XYZ也是首字母缩写词” 我认为这是问题 - 我希望Lucene知道三个或更多个大写字母后跟一个空格可能是首字母缩写词(如PDF,HTML等)。我真的不知道写我自己的记号器。 – pwaring 2012-01-21 21:31:49

+0

您可以使用StandardAnalyzer(因为它删除了首字母缩写词)。我不知道在zend lucene中是否有与此相同的东西 – naresh 2012-01-22 07:29:20