[转]Lucene 全文检索实践

本文介绍了使用Lucene进行全文检索的实践过程,包括索引创建、搜索程序编写、中文支持及高亮显示等功能的实现。

 

[转]Lucene 全文检索实践

作者:baggio785
作者Blog:http://blog.youkuaiyun.com/baggio785/
本文来源:http://www.lucene.com.cn/sj.htm

Lucene

Lucene Apache Jakarta 的一个子项目,是一个全文检索的搜索引擎库。其提供了简单实用的 API,通过这些 API,可以自行编写对文件(TEXTXMLHTML等)、目录、数据库的全文检索程序。

Features
* Very fast indexing, minimal RAM required
* Index compression to 30% of original text
* Indexes text and HTML, document classes available for XML, PDF and RTF
* Search supports phrase and Boolean queries, plus, minus and quote marks, and parentheses
* Allows single and multiple character wildcards anywhere in the search words, fuzzy search, proximity
* Will search for punctuation such as + or ?
* Field searches for title, author, etc., and date-range searching
* Supports most European languages
* Option to store and display full text of indexed documents
* Search results in relevance order
* APIs for file format conversion, languages and user interfaces


实践任务:
1)
编写 Java 程序 MyIndexer.java,使用 JDBC 取出 MySQL 数据表内容(以某一论坛数据做测试),然后通过 org.apache.lucene.index.IndexWriter 创建索引。
2)
编写 Java 程序 MySearcher.java,通过 org.apache.lucene.search.IndexSearcher 等查询索引。
3)
实现支持中文查询及检索关键字高亮显示。
4)
通过 PHP / Java Integration 实现对 MySearch.java 的调用。
5)
实现对 PHP 手册(简体中文) 的全文检索。

 

 

Lucene 全文检索实践(2

Java 的程序基本编写完成,实现了对中文的支持。下一步是将其放到 WEB 上运行,首先想到的是使用 JSP,安装了Apache Tomcat/4.1.24,默认的发布端口是 8080。现在面临的一个问题是:Apache httpd 的端口是 80,并且我的机器对外只能通过 80 端口进行访问,如果将 Tomcat 的发布端口改成 80 的话,httpd 就没法对外了,而其上的 PHP 程序也将无法在 80 端口运行。

对于这个问题,我想到两种方案:
1
、使用 PHP 直接调用 Java。需要做的工作是使用 --with-java 重新编译 PHP
2
、使用 mod_jk 做桥接的方式,将 servlet 引擎结合到 httpd 中。需要做的工作是编译 jakarta-tomcat-connectors-jk-1.2.5-src,生成 mod_jk.so httpd 使用,然后按照 Howto 文档 进行 Tomcathttpd 的配置。

对于第一个方案的尝试:使用 PHP 直接调用 Java

环境
* PHP 4.3.6 prefix=/usr
* Apache 1.3.27 prefix=/usr/local/apache
* j2sdk1.4.1_01 prefix=/usr/local/jdk

配置步骤
1)
安装 JDK,这个就不多说了, GOOGLE 可以搜索出这方面的大量文章

2)
重新编译 PHP,我的 PHP 版本是 4.3.6

cd php-4.3.6

./configure --with-java=/usr/local/jdk

make

make install


完成之后,会在 PHP lib 下(我的是在 /usr/lib/php)有个 php_java.jar,同时在扩展动态库存放的目录下(我的是在 /usr/lib/php/20020429)有个 java.so 文件。到这一步需要注意一个问题,有些 PHP 版本生成的是 libphp_java.so 文件,extension 的加载只认 libphp_java.so,直接加载 java.so 可能会出现如下错误:

PHP Fatal error:  Unable to load Java Library /usr/local/jdk/jre/lib/i386/libjava.so, error: libjvm.so:

 cannot open shared object file: No such file or directory in /home/nio/public_html/java.php on line 2


所以如果生成的是 java.so,需要创建一个符号连接:

ln -s java.so libphp_java.so



3)
修改 Apache Service 启动文件(我的这个文件为 /etc/init.d/httpd),在这个文件中加入:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/jdk/jre/lib/i386/server:/usr/local/jdk/jre/lib/i386


正如你所看到的,我的 JDK 装在 /usr/local/jdk 目录下,如果你的不是在此目录,请做相应改动(下同)。

4)
修改 PHP 配置文件 php.ini,找到 [Java] 部分进行修改:

[Java]

java.class.path = /usr/lib/php/php_java.jar

java.home = /usr/local/jdk

;java.library =

;java.library.path =

extension_dir=/usr/lib/php/20020429/

extension=java.so


我将 java.library java.library.path 都注释掉了,PHP 会自动认为 java.library=/usr/local/jdk/jre/lib/i386/libjava.so

5)
重新启动 Apache httpd 服务:

service httpd restart



测试
测试脚本 java.php
源代码:

getProperty('java.version').'
';

print 'Java vendor=' . $system->getProperty('java.vendor').'
';

print 'OS=' .   $system->getProperty('os.name') . ' ' .

                $system->getProperty('os.version') . ' on ' .

                $system->getProperty('os.arch') . '
';

?>



总结
安装配置还算简单,但是在 PHP 运行 Java 的速度感觉较慢,所以下定决心开始实践第二个方案。(待续)

Lucene 全文检索实践(3

今天总算有些空闲时间,正好说说第二种方案:使用 mod_jk 做桥接的方式,将 servlet 引擎结合到 httpd 中。

环境
* PHP 4.3.6 prefix=/usr
* Apache 1.3.27 prefix=/usr/local/apache
* j2sdk1.4.1_01 prefix=/usr/local/jdk
* jakarta-tomcat-4.1.24 prefix=/usr/local/tomcat
*
另外需要下载 jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz

配置步骤
1)
安装 JDK Tomcat,这些安装步骤就不多说了。

2)
编译 jakarta-tomcat-connectors-jk-1.2.5-src,生成 mod_jk.so,并将其复制到 apache modules 存放目录:

tar xzf jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz

cd jakarta-tomcat-connectors-jk-1.2.5-src/jk/native

./configure --with-apxs=/usr/local/apache/bin/apxs

make

cp apache-1.3/mod_jk.so /usr/local/apache/libexec



3)
编辑 Apache 配置文件 /usr/local/apache/conf/httpd.conf,加入:

LoadModule jk_module libexec/mod_jk.so

AddModule mod_jk.c


这个 LoadModule 语句最好放在其他 LoadModule 语句后边。
同时在配置文件后边加入:

# workers.properties 文件所在路径,后边将对此文件进行讲解

JkWorkersFile /usr/local/apache/conf/workers.properties

# jk 的日志文件存放路径

JkLogFile /usr/local/apache/log/mod_jk.log

# 设置 jk 的日志级别 [debug/error/info]

JkLogLevel info

# 选择日志时间格式

JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

# JkOptions 选项设置

JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat 设置日志的请求格式

JkRequestLogFormat "%w %V %T"

# 映射 /examples/* worker1worker1 workers.properties 文件中定义

JkMount /examples/* worker1



4)
/usr/local/apache/conf/ 目录下创建 workers.properties 文件,其内容如下:

# 定义使用 ajp13 worker1

worker.list=worker1

 

# 设置 worker1 的属性(ajp13

worker.worker1.type=ajp13

worker.worker1.host=localhost

worker.worker1.port=8009

worker.worker1.lbfactor=50

worker.worker1.cachesize=10

worker.worker1.cache_timeout=600

worker.worker1.socket_keepalive=1

worker.worker1.socket_timeout=300



5)
好了,启动 Tomcat,重启一下 Apache HTTPD Server,访问:http://localhost/examples/index.jsp,看看结果如何,和 http://localhost:8080/examples/index.jsp 是一样的。

提示:如果不想让别人通过 8080 端口访问到你的 Tomcat,可以将 /usr/lcoal/tomcat/conf/server.xml 配置文件中的如下代码加上注释:


然后重新启动 Tomcat 即可。

总结
此方案安装配置稍微复杂些,但执行效率要比第一种方案要好很多。所以决定使用这种方案来完成我的 Lucene 全文检索实践任务。

 

Tomcat Service 脚本

Linux (我用的是 Redhat)中,如果经常需要启动/关闭 Tomcat 的话,还是创建一个 daemon 来得比较方便,创建步骤如下:

1)
/etc/init.d/ 目录下创建文件 tomcat,代码如下:

# chkconfig: 345 91 10

# description: Tomcat daemon.

#

 

# 包含函数库

. /etc/rc.d/init.d/functions

 

# 获取网络配置

. /etc/sysconfig/network

 

# 检测 NETWORKING 是否为 "yes"

[ "${NETWORKING}" = "no" ] && exit 0

 

# 设置变量

# $TOMCAT 指向 Tomcat 的安装目录

TOMCAT=/usr/local/tomcat

 

# $STARTUP 指向 Tomcat 的启动脚本

STARTUP=$TOMCAT/bin/startup.sh

 

# $SHUTDOWN 指向 Tomcat 的关闭脚本

SHUTDOWN=$TOMCAT/bin/shutdown.sh

 

# 设置 JAVA_HOME 环境变量,指向 JDK 安装目录

export JAVA_HOME=/usr/local/jdk

 

# 启动服务函数

start() {

         echo -n $"Starting Tomcat service: "

         $STARTUP

         RETVAL=$?

         echo

}

 

# 关闭服务函数

stop() {

         action $"Stopping Tomcat service: " $SHUTDOWN

         RETVAL=$?

         echo

}

 

# 根据参数选择调用

case "$1" in

  start)

         start

         ;;

  stop)

         stop

         ;;

  restart)

         stop

         start

         ;;

  *)

         echo $"Usage: $0 start|stop|restart"

         exit 1

esac

 

exit 0


2)
修改 tomcat 文件的属性

chown a+x tomcat


3)
生成 service

chkconfig --add tomcat


好了,现在可以通过 service tomcat start 命令启动 Tomcat 了,关闭及重启服务的命令也类似,只是将 start 换成 stop restart

Lucene 全文检索实践(4

在几天的研究中,了解了 Lucene 全文检索的一些原理,同时进行了实践,编写了一个论坛的全文检索创建索引程序及用于搜索的 JSP 程序,另外还写了一个 PHP 手册(简体中文)的全文检索,可以进行多关键字搜索。基本完成了最初定下的实践任务。

Lucene 全文检索实践(5

对于 Lucene 的初步研究已经过去一段时间,自己感觉还不是很深入,但由于时间的关系,一直也没再拿起。应网友的要求,将自己实践中写的一些代码贴出来,希望能对大家有用。程序没有做进一步的优化,只是很简单的实现功能而已,仅供参考。

在实践中,我以将 PHP 中文手册中的 HTML 文件生成索引,然后通过一个 JSP 对其进行全文检索。
生成索引的 Java 代码:

/**

 * PHPDocIndexer.java

 * 用于对 PHPDoc HTML 页面生成索引文件。

 */

import java.io.File;

import java.io.FileReader;

import java.io.BufferedReader;

import java.io.IOException;

import java.util.Date;

import java.text.DateFormat;

import java.lang.*;

 

import org.apache.lucene.analysis.cjk.CJKAnalyzer;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.document.Document;

import org.apache.lucene.document.Field;

import org.apache.lucene.document.DateField;

 

class PHPDocIndexer

{

    public static void main(String[] args) throws ClassNotFoundException, IOException

    {

        try {

            Date start = new Date();

 

            IndexWriter writer = new IndexWriter("/home/nio/indexes-phpdoc", new CJKAnalyzer(), true); //索引保存目录,必须存在

            indexDocs(writer, new File("/home/nio/phpdoc-zh")); //HTML 文件保存目录

           

            System.out.println("Optimizing ....");

            writer.optimize();

            writer.close();

           

            Date end = new Date();

          

            System.out.print("Total time: ");

            System.out.println(end.getTime() - start.getTime());

        } catch (Exception e) {

            System.out.println("Class " + e.getClass() + " throws error!/n  errmsg: " + e.getMessage());

        }   //end try

    }   //end main

   

    public static void indexDocs(IndexWriter writer, File file) throws Exception

    {

        if (file.isDirectory()) {

            String[] files = file.list();

            for (int i = 0; i < files.length; i++) {

                indexDocs(writer, new File(file, files[i]));

            }   //end for

        } else if (file.getPath().endsWith(".html")) {  //只对 HTML 文件做索引          

            System.out.print("Add file:" + file + " ....");

            // Add html file ....

            Document doc = new Document();

            doc.add(Field.UnIndexed("file", file.getName()));    //索引文件名

            doc.add(Field.UnIndexed("modified", DateFormat.getDateTimeInstance().format(new Date(file.lastModified()))));   //索引最后修改时间

 

            String title = "";

            String content = "";

            String status = "start";

            

            FileReader fReader = new FileReader(file);

            BufferedReader bReader = new BufferedReader(fReader);

            String line = bReader.readLine();

 

            while (line != null) {               

                content += line;

                //截取 HTML 标题 </span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                if ("start" == status && line.equalsIgnoreCase("><TITLE")) {</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                    status = "match";</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                } else if ("match" == status) {</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                    title = line.substring(1, line.length() - 7);</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                    doc.add(Field.Text("title", title));    //</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">索引标题</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                    status = "end";</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                }   //end if</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">                line = bReader.readLine();</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            }   //end while</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            bReader.close();</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            fReader.close();</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            doc.add(Field.Text("content", content.replaceAll("<[^<>]+>", "")));  //</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">索引内容</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            writer.addDocument(doc);</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">            System.out.println(" [OK]");</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">        }   //end if</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">    }</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"> </span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">}   //end class</span></p></div><p class="MsoNormal" style="LAYOUT-GRID: 15.6pt none; LINE-HEIGHT: 130%; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; LINE-HEIGHT: 130%; FONT-FAMILY: Verdana"><br /><br /></span><span style="FONT-SIZE: 9pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">索引生成完之后,就需要一个检索页面,下边是搜索页面(</span><span lang="EN-US" style="FONT-SIZE: 9pt; LINE-HEIGHT: 130%; FONT-FAMILY: Verdana">search.jsp</span><span style="FONT-SIZE: 9pt; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">)的代码:</span></p><div class="Section1" style="BORDER-RIGHT: #dddddd 1pt solid; PADDING-RIGHT: 2pt; LAYOUT-GRID: 15.6pt none; BORDER-TOP: #dddddd 1pt solid; PADDING-LEFT: 3pt; BACKGROUND: #eeeeee; PADDING-BOTTOM: 2pt; BORDER-LEFT: #dddddd 1pt solid; PADDING-TOP: 2pt; BORDER-BOTTOM: #dddddd 1pt solid"><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"><%@ page language="java" import="javax.servlet.*, javax.servlet.http.*, java.io.*, java.util.Date, java.util.ArrayList, java.util.regex.*, org.apache.lucene.analysis.*, org.apache.lucene.document.*, org.apache.lucene.index.*, org.apache.lucene.search.*, org.apache.lucene.queryParser.*, org.apache.lucene.analysis.Token, org.apache.lucene.analysis.TokenStream, org.apache.lucene.analysis.cjk.CJKAnalyzer, org.apache.lucene.analysis.cjk.CJKTokenizer, com.chedong.weblucene.search.WebLuceneHighlighter" %></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"><%@ page contentType="text/html;charset=GB2312" %></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"</span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">    "http://www.w3.org/TR/html4/strict.dtd"></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"><html></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'"><head></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">    <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /></span></p><p class="MsoNormal" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; BACKGROUND: #eeeeee; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; LINE-HEIGHT: 130%; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none; TEXT-ALIGN: left" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">    <title>PHPDoc - PHP </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 宋体">简体中文手册全文检索</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 130%; FONT-FAMILY: 'Courier New'">

   

   

   

 

<%

String keyVal = null;

String pageVal = null;

int offset = 0;

int curPage = 0;

int pages;

final int ROWS = 50;

 

//获取 GET 参数

try {

    byte[] keyValByte = request.getParameter("key").getBytes("ISO8859_1");  //查找关键字

    keyVal = new String(keyValByte);

    pageVal = request.getParameter("page");    //页码

} catch (Exception e) {

    //do nothing;

}

if (keyVal == null)

    keyVal = new String("");

%>

   

       

       

          " />

        提示:可使用多个关键字(使用空格隔开)提高搜索的准确率。

   

   

<%

if (keyVal != null && keyVal.length() > 0) {

    try {

        curPage = Integer.parseInt(pageVal);    //将当前页转换成整数

    } catch (Exception e) {

        //do nothing;

    }   //end try

    try {

        Date startTime = new Date();

        keyVal = keyVal.toLowerCase().replaceAll("(or|and)", "").trim().replaceAll("//s+", " AND ");

        Searcher searcher = new IndexSearcher("/home/nio/indexes-phpdoc"); //索引目录

        Analyzer analyzer = new CJKAnalyzer();

        String[] fields = {"title", "content"};        

        Query query = MultiFieldQueryParser.parse(keyVal, fields, analyzer);

        Hits hits = searcher.search(query);

 

        StringReader in = new StringReader(keyVal);

        TokenStream tokenStream = analyzer.tokenStream("", in);

        ArrayList al = new ArrayList();

        for (Token token = tokenStream.next(); token != null; token = tokenStream.next()) {

            al.add(token.termText());

        }   //end for

       

        //总页数

        pages = (new Integer(hits.length()).doubleValue() % ROWS != 0) ? (hits.length() / ROWS) + 1 : (hits.length() / ROWS);

 

        //当前页码

        if (curPage < 1)

            curPage = 1;

        else if (curPage > pages)

            curPage = pages;

       

        //起始、终止下标

        offset = (curPage - 1) * ROWS;

        int end = Math.min(hits.length(), offset + ROWS);

       

        //循环输出查询结果

        WebLuceneHighlighter hl = new WebLuceneHighlighter(al);

        for (int i = offset; i < end; i++) {

            Document doc = hits.doc(i);

            %>

           

           

<%=hl.highLight(doc.get("content").replaceAll("/n", "  "), 100)%> ……

           

                /~nio/phpdoc-zh/<%=doc.get("file")%>

                 - 

                <%=doc.get("modified")%>

           

           

            <%

        }   //end for

        searcher.close();

        Date endTime = new Date();

        %>

           

                检索总共耗时 <%=((endTime.getTime() - startTime.getTime()) / 1000.0)%> 秒,约有 <%=hits.length()%> 项符合条件的记录,共 <%=pages%>

        <%

        if (curPage > 1 && pages > 1) {

        %>

             | 上一页

        <%

        }   //end if

        if (curPage < pages && pages > 1) {

        %>

             | 下一页

        <%

        }   //end if

    } catch (Exception e) {

    %>

       

    <%

    }   //end if

}   //end if

%>   



在线示例:PHP 手册(简体中文)

 

 

全文检索实践( 1


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值