欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

Drupal专业开发指南 第12章 搜索和索引内容(1)

MySQL 和PostgreSQL都有内置的全文搜索能力。你可以很容易的使用这些数据库特定解决方案来建立一个搜索引擎,但你失去了对搜索机制的控制,同时也不能 让你的搜索系统与你的应用完全匹配。而且有时候数据库认为优先级比较高的词语,而实际上在你的应用中则被认为是“噪音”。

由于数据库全文搜索不能很好的满足应用需求,Drupal社区建立一个定制的搜索引擎,来实现针对Drupal的索引和页面等级算法。结果就建立了与Drupal其他框架部分相一致的搜索引擎,它具有标准的配置和用户接口----不用担心底层使用了什么数据库。

 

在本章我们讨论了如何使用模块编写搜索API的钩子函数和构建定制的搜索表单。我们还将学习一下Drupal是如何解析和索引内容的,还有就是如何编写索引器的钩子函数。

 

提示 Drupal能够理解复杂的查询语句,比如包含布尔操作and/or,精确短语,或者甚至可以排除每个词语。这些情况的一个实际例子如下所示:

Beatles OR John Lennon "Penny Lane" -insect

 

构建一个定制的搜索页面

Drupal可对节点和用户名进行搜索。即使你开发了一个你自己定制的节点类型,Drupal也可以对其节点视图中显示的内容进行索引。例如,假定你有一个recipe(处方)节点类型,它包含两个字段ingredients(药物成分)和instructions(用法说明);你创建一个新的处方节点,其节点ID为22。当有匿名用户访问http://example.com/?q=node/22时就可以看到这些节点字段,搜索模块将在它下次访http://example.com/cron.php时(一般通过一个cron周期性运行它),将会对处方节点的内容和其它元数据进行索引。

 

Drupal默认提供了节点搜索和用户搜索。开始你可能会觉得节点搜索和用户搜索的底层机制是一样的,事实上它们使用了两种独立的方式来实现搜索功能。对于节点搜索,每次所搜都没有直接对node表进行查询,它首先使用一个索引器把内容预处理为一种结构化的格式,在节点搜索时,将会对结构化的索引数据进行查询,这样就会产生更快更准确的结果。我们在下面的部分将会详细的介绍索引器。

 

用户搜索一点也不复杂,这是因为用户名只是数据库中的一个单独字段,搜索语句只需要对该字段进行检查就可以了。而且,用户名中不允许包含HTML,所以你不需要使用HTML索引器。相反,你只需要几行代码直接对user表进行查询就可以了。

 

让我们看一个例子。假定我们的站点使用了path.module, 我们需要对数千个URL别名进行管理,这使得现有的URL别名管理页面显得非常笨拙。我们将编写一个搜索接口来快速的寻找我们想找的东西。。幸运的是你可 以使用搜索API提供的默认搜索表单(如图12-1所示)。如果这个接口能够满足你的需求,那么你只需要编写为搜索请求查找采集数(hits)的逻辑就可以了。这一搜索逻辑一般是一个数据库查询语句。

 

图12-1 搜索API的搜索默认用户接口

 

它看起来很简单,默认内容搜索表单事实上很强大,它可以对你站点上的节点内容的所有可视的元素进行查询,这是由于索引器的缘故。也就是说,通过该接口,可以对节点的标题、主体、其它定制属性、评论、分类词语进行查询。高级搜索特性,如图12-2所示,是过滤搜索结果的另一种方式。

很有可能你想扩展默认搜索表单以添加额外的搜索字段,你已在第10章学到了如何去扩展。明确的,你可以使用hook_form_alter()来添加和删除表单字段。由于在本章中我们的主题是搜索API,我们将在这里使用默认的搜索表单。图12-3展示了我们需要为我们的路径别名搜索模块实现的搜索API钩子函数的概貌。

 

图12-2 有默认搜索表单提供的高级搜索选项

 

 

 

图12-3 为创建一个定制搜索页面,搜索API钩子函数的执行周期

 

 

注意 在测试这些例子以前,你需要重新构建你的搜索索引数据。你可以这样做:导航到Administer ➤ Site configuration ➤ Search settings,点击“Re-index site”按钮,接着访问http://example.com/cron.php

 

在sites/all/modules/custom下面创建一个名为pathfinder的新文件夹,在新目录中创建列表 12-1 和 12-2所示的文件。

列表12-1. pathfinder.info

; $Id$

name = Pathfinder

description = Gives administrators the ability to search URL aliases.

version = "$Name$"

列表12-2. pathfinder.module

<?php

// $Id$

/**

* @file

* Search interface for URL aliases.

*/

不要在你的文本编辑器中关闭pathfinder.module;你将继续使用它。继续,在Administer ➤ Site building ➤ Modules中启用该模块。接下来要实现的函数是hook_search($op, $keys)。该钩子函数根据操作($op)参数的不同返回不同的信息。

 

 

/**

* Implementation of hook_search().

*/

function pathfinder_search($op = 'search', $keys = NULL) {

switch ($op) {

case 'name':

if (user_access('administer url aliases')) {

return t('URL aliases');

}

case 'search':

if (user_access('administer url aliases')) {

$found = array();

// Replace wildcards with MySQL/PostgreSQL wildcards.

$keys = preg_replace('!/*+!', '%', $keys);

$sql = "SELECT * FROM {url_alias} WHERE LOWER(dst) LIKE LOWER('%%%s%%')";

$result = pager_query($sql, 50, 0, NULL, $keys);

while ($path = db_fetch_object($result)) {

$found[] = array('title' => $path->dst,

'link' => url("admin/path/edit/$path->pid"));

}

return $found;

}

}

}

 

当搜索API调用hook_search('name')时,它将寻找显示在通用搜索页面中的菜单标签(tab)上的名字(参看图12-4)。在这里,我们返回的是“URL aliases”。通过返回菜单标签的名字,搜索API 将为菜单标签的链接创建一个新的搜索表单。正如前面提到的,如果你需要扩展搜索接口的话,你可以使用hook_form_alter()(高级搜索选项就 是通过这种方式添加到节点搜索表单上的---参看node.module里面的node_form_alter())

 

 

图12-4 通过从hook_search()中返回菜单标签的名字,这样就可以访问搜索表单了

hook_search('search') 是hook_search()中干重活的部分。当提交搜索表单时,调hook_search('search'),它的任务是搜集并返回搜索结果。在其那 面的代码中,我们使用表单中提交的搜索词语对url_alias表进行查询。我们接着将查询的结果搜集到一个数组中并将其返回。继续,测试一下你新的搜索 引擎!一定要启用search.module 和 path.module,创建一些URL别名,然后导航到http://example.com/?q=search/pathfinder并搜索一个已存在的别名。

 

 

注意 由于搜索API将提交搜索表单的POST请求转化为了GET请求,所以用户可以将搜索结果页面添加到收藏夹中。例如,一个对节点的“surfing”字段进行搜索产生的搜索结果页面的URL为http://example.com/?q=search/node/surfing,该URL可添加到收藏夹中。

 

让我们转移到搜索结果页面的外观上。如果默认的搜索结果页面与你期望的存在差距的话,你可以覆盖它。在我们这里,我们不想把它仅仅展示为一列匹配的别名,让我们使用一个可排序的表格,每行匹配别名后面带有一个独立的“edit”链接。通过对hook_search('search')的返回值进行一些修改并实现hook_search_page(),从而完成这一工作。

/**

* Implementation of hook_search().

*/

function pathfinder_search($op = 'search', $keys = NULL) {

switch ($op) {

case 'name':

if (user_access('administer url aliases')) {

return t('URL aliases');

}

case 'search':

if (user_access('administer url aliases')) {

$header = array(

array('data' => t('Alias'), 'field' => 'dst'),

t('Operations'),

);

// Return to this page after an 'edit' operation.

$destination = drupal_get_destination();

// Replace wildcards with MySQL/PostgreSQL wildcards.

$keys = preg_replace('!/*+!', '%', $keys);

$sql = "SELECT * FROM {url_alias} WHERE LOWER(dst) LIKE LOWER('%%%s%%')" .

tablesort_sql($header);

$result = pager_query($sql, 50, 0, NULL, $keys);

while ($path = db_fetch_object($result)) {

$rows[] = array(l($path->dst, $path->dst), l(t('edit'),

"admin/build/path/edit/$path->pid", array(), $destination));

}

if (!$rows) {

$rows[] = array(array('data' => t('No URL aliases found.'),

'colspan' => '2'));

}

return $rows;

}

}

}

/**

* Implementation of hook_search_page().

*/

function pathfinder_search_page($rows) {

$header = array(

array('data' => t('Alias'), 'field' => 'dst'), ('Operations'));

$output = theme('table', $header, $rows);

$output .= theme('pager', NULL, 50, 0);

return $output;

}

 

 

在前面的代码中,我们使用drupal_get_destination() 来取回我们所在页面的当前位置,如果我们点击了链接并编辑一个别名,提交编辑表单以后我们将自动返回到这一搜索结果页面。由于返回的路径信息作为编辑链接 的一部份传递给了别名编辑表单,所以编辑表单知道要返回到哪个页面。你将在URL中看到一个名为destination的额外GET参数,该参数包含的就是保存表单后所要返回的URL。

为了对结果表格进行排序,我们将tablesort_sql()函数追加到搜索查询字符串上,从而保证向查询语句后追加正确的SQL ORDER BY语句。最后,pathfinder_search_page()是钩子hook_search_page()的一个实现,它允许我们控制搜索结果页面的输出。图12-5展示了最终的搜索结果页面。

 

图12-5 URL别名搜索的最终搜索页面

普通分类: