当前位置:编程学习 > JAVA >>

大牛请教下这种搜索是用什么技术做的?

如下图 --------------------编程问答-------------------- 是类似仿淘宝搜索么、?

如果是的话其实就是对数据库的查询和显示前台  而查询语句是拼凑出来的
还有个隐藏表单空间   自身页面跳转
js+jsp+struts+jdbc就可以

而点击之后的隐藏效果是  body的onload事件加载一个初始化的js控制div显示与隐藏      --------------------编程问答-------------------- Stirng s = "select * from 表名 where 1=1 ";
if(条件1){
    s += " AND '字段1' = '值1' ";
}
if(条件2){
    s+= " AND '字段2' = '值2' ";
}
多条件查询大概就是这样  剩下的东西,比如选择吉林,就会出现长春 四平 这些相关联的显示效果,基本都是JAVASCRIPT实现的了 --------------------编程问答-------------------- 一般有两种形式  一种是直接用  SQL拼接的形式 如楼上 适合小型 数据量比较小项目 

还有一种是走搜索引擎的  如LUCENE 上面的搜索条件 一个类型当成一个维度 适用中小型以上 数据量比较大的项目 你可以看看 基于lucene的一些扩展框架 如 solr、compass 可以参考  --------------------编程问答-------------------- --------------------编程问答-------------------- 使用lusene来做的。
脱机数据包, 数据字典形式
请笑纳, 希望能给你带来思路

1、Lucene概述
        Lucene是一个用Java写的全文检索引擎工具包,可以方便地嵌入到各种应用中实现针对应用的全文索引/检索功能,Lucene有两个主要的服务,索引和检索,两者任务是相互独立的。这使得开发人员可以根据需要对它们进行扩展。Lucene提供了丰富的API,可以与存储在索引中的信息方便的交互。需要说明的是它并不是一个完整的全文检索应用,而是为应用程序提供索引和搜索功能。即若想让Lucene真正起作用,还需在其基础上做一些必要的二次开发。
         Lucene源码中共包括7个子包,每个包完成特定的功能,
1.1 Lucene的索引
        Lucene索引信息存储有三种可选方式:内存(RAM),文件系统(FS)和数据库(DB)。RAM存储适用于较小的检索系统,文件系统存储可用于中型检索,数据库存储则适用于对检索性能要求更高的系统。下面对其索引存储逻辑进行分析。
        在Lucene中,索引(index)由段(segment)组成,段(segment)由记录(document)组成,记录(document)由域 (field)组成,域(field)由字符串(term)组成。例如,一个document可以与一个物理文件对应,将其中文件名、文件内容、文件创建时间等信息提取为数据源放入document中,也可将多个物理文件的数据源同时放入一个document。数据源是由一个被称为field的类表示。这个field类主要用来标识当前数据源各种属性,主要是以下三种标识:
        可分词性:该数据源是否需要经过分词。可分词时,内容被分成多个可被索引的词;不可分词时,整个字段内容作为一个词。
        可存储性:字段内容直接按照词存放,而不是以倒排形式存放。
         可索引性:该数据源的数据是否要在用户检索时被检索,需要检索时,字段内容以倒排形式存放,即记录字段每个词在某一文档中出现的频率。
         从代码方面上看,在Lucene的索引部分,invert-Document()方法是最重要的,由它调用分析器的接口,分析统计词条的位置与频率信息。作用就是建立倒排索引,直观的讲,就像一本书在最后给出在书中出现的名词列表,同时对应给出该名词出现在第几章和第几页。在查找的时候,可以直接定位到具体页码,而不用从目录开始逐个查找。
         Lucene在维护和扩展索引的时候不断创建新的索引文件,最终将这些新的小索引文件并入大索引中。使程序员可以根据不同的要求自行调整批次大小、周期长短。这点对于Lucene的检索效率也相当的重要。建立索引,是需要占用内存资源的,当有新的记录加入索引时,并不直接写入硬盘而是先放在内存中,所以最直接提高检索速度的方法就是提高内存存放索引的缓冲区的大小。具体缓冲区的大小设置需要根据实际情况而定。
        1.2 Lucene和其它全文检索的区别

        传统的查找技术是通过逐次匹配内存中的文本实现的,即顺序查找。对文档集合中的信息进行少量预处理或不做处理,这种方法只适合文档较少的情况,虽然结构简单,易实现,但检索速度比较慢,尤其在处理海量数据和模糊查询时有着明显不足。当信息量在TB级别时,查找的速度是无法忍受的。Lucene通过特殊的索引结构实现了传统检索不擅长的全文索引机制,这也是Lucene快速发展的原因之一。
2、应用实例
         2.1 基于Lucene的全文检索系统
        主要通过实现一个简单的手机产品的搜索引擎系统来说明基于Java的开源框架Lucene和 Heritrix在全文搜索系统中的应用。用户可以通过该系统在网站上搜索他们所需要的详细手机信息,并能从搜索结果中打开一个有关详细信息的页面进行浏览。这些手机商品信息由各大IT网站提供的。
       (1)系统架构设计
         在该系统中,首先通过网络爬虫Heritrix将这些商品信息抓取下来,然后组织整理,构建产品数据库,同时为各种内容(产品图片,详细资料)利用 Lucene建立索引,以提供给用户检索。最后通过Spring等web框架设计web用户接口。
(2)系统实现
        在选择抓取的网站时要注意以下几点:
         1)网站能够被本程序中的爬虫(Heritrix)抓取,因为有的网站使用了反爬虫技术,防止未经授权的爬虫对其网页进行抓取。
         2)网站的信息不是由Javascript动态生成的。在web2.0时代的今天,很多网站采用了Ajax技术,利用Javascript来生成内容,这种动态的内容需要在浏览器中运行生成,是爬虫无法获取到的,因此Ajax技术是对搜索引擎不友好的技术,在选择网站的时,不应当选择这类网站。
         3)网站的页面结构不要当经常变化,最好是用固定的模板生成的,这样有利于在分析网页时候,使用较简单的方式来解析数据。
        4)在选择网站时候,也应该尽量选择那些访问量大,产品数据比较全的站点,这样抓取的内容也完整些。
         综上几点,在该系统中选择了太平洋电脑网的手机信息页面作为此次抓取的站点。系统设计步骤简要说明如下:
         首先对目标页面的结构进行分析,获取爬虫程序的抓取清单,利用爬虫程序对页面抓取完毕后,使用HTMLParser工具来解析网页,获取手机产品名,型号,产品属性信息,图片地址等内容生成产品信息文件,同时对所有手机产品构建一个产品信息的词库,这个词库包含所抓取的所有产品的品牌和型号,便于用户输入关键字时候能够检索到它们。当词库建立好后,需要把产品信息插入数据库中,并建立Lucene的索引。
2.2基于Lucene.net网络爬虫的设计与实现
         仲恺农业工程学院刘磊安等人基于Lucene.NET平台开发了网络爬虫,可以对特定的网页进行抓取和分析,提取网页中有用信息,并对抓取的数据进行索引,存储到服务器硬盘,同时过滤掉无用信息。系统界面友好,准确高效。
        (1)系统逻辑结构
         该系统采用分层设计,结构清晰,可扩展性强。系统主要分为:BLL业务逻辑层,DAL数据访问层,Model数据库实体层,Common层。其中BLL业务逻辑层主要实现数据抓取,接受用户搜索,页面分析等逻辑功能。DAL数据访问层主要负责处理所有涉及数据库的操作。Model数据库实体层主要为一些数据库实体层,每个数据库表对应一个实体类。Common层主要包含一些常用类,如:线程管理,Html页面分析,URL处理等类。
        (2)数据库设计
        该系统所有数据按照Lucene.net特有索引文件格式存储到硬盘中。由于计算机存在少量也能够与数据处理,数据需要长期保留在外村上反复进行查询、修改、插入和删除等操作。因此只将少量数据需要存储到数据库中,该系统采用Microsoft SQL Server 2005做数据库。WaitForIndex等待索引内容表,该表主要存储爬虫程序停止后,在内存中未索引数据存进数据库中。
         (3)系统实现
         该系统采用四层结构,分为界面层(包括:web界面,WinForm界面)、BLL业务逻辑层、DAL数据访问层、Common常用类、Model数据库实体层。BLL业务逻辑层,主要处理业务逻辑,其中包括一些逻辑类、爬虫类、索引类、搜索类。DAL数据访问层通过使用.net中的反射机制实现ORM对象关系模型,底层对数据库访问基类为BaseDAL类,该类使用.net中的泛型,类似于C++中的模板机制,从而使得该类可以接受任意类型的数据库实体类。该项目中对应Model层中的实体,每个实体对应一个数据库访问类,如Model层中有:FinishedURL类,则DAL层有 FinishedURLDAL类与之对应,该类继承BaseDAL类。Common层主要为一些常用类,该系统中主要用到HTMLHelper类和 ThreadManager类。HTMLHelper主要用于分析HTML页面信息,获取URL列表,页面Title、内容等。 ThreadManager类为多线程管理类,根据系统设置进行线程的启动,停止操作。
Jive是一个比较丰富的知识宝藏,从中可以学习到很多新的实战技巧和具体功能实现方式。前面基本介绍了Jive中的一些主要架构技术,通过这些技术可以基本上掌握Jive论坛系统。

Jive中还有很多非常实用的组件技术和工具库,分析学习可重用技术,可以在自己具体的项目重复使用,大大提高了新系统的开发速度和效率。
5.1  Jive的树形结构

Jive的管理功能中提供了将Jive数据库数据导出到XML文件的管理工具,在这个工具功能实现中,使用了树形结构的遍历技术。

Jive将ForumThread中的第一个ForumMessage作为root ForumMessage,以这个ForumMessage为根节点,每个ForumThread中包含了一套树形结构。

TreeWalker是树形结构的一个抽象接口,代码如下:

public interface TreeWalker {

    //根节点

    public ForumMessage getRoot();

    //获得父节点

    public ForumMessage getParent(ForumMessage child)

            throws ForumMessageNotFoundException;

    //获得子节点

    public ForumMessage getChild(ForumMessage parent, int index)

            throws ForumMessageNotFoundException;

    //获得所有子节点

    public Iterator children(ForumMessage parent);

    //获得所有的子节点,包括子节点的子节点…

    public Iterator recursiveChildren(ForumMessage parent);

    //获得一个节点的深度,相对根节点而言

    public int getMessageDepth(ForumMessage message);

   

    public int getChildCount(ForumMessage parent);

    public int getRecursiveChildCount(ForumMessage parent);



    /**

     * 返回相对父节点的子节点索引。例如

     * <pre>

     *   4

     *   |-- 2

     *   |-- |-- 1

     *   |-- |-- 6

     *   |-- |-- 8

     *   |-- 5

     * </pre>

     * getIndexOfChild(4, 2) 将返回0

     * getIndexOfChild(4, 5) 将返回1

     * getIndexOfChild(2, 1) 将返回0

     * getIndexOfChild(2, 6) 将返回1

     * getIndexOfChild(2, 8) 将返回2

     */

    public int getIndexOfChild(ForumMessage parent, ForumMessage child);

    //一个节点是否是叶,叶相对枝来说,叶没有子节点了

    public boolean isLeaf(ForumMessage node);

}

DbTreeWalker 是TreeWalker的一个实现,它是将一个ForumThread下所有帖子的ID从数据库中装入LongTree中。一句LongTree的树形结构遍历核心技术实现ForumThread中的帖子结构的遍历。

LongTree类似之前的Cache类,封装了树形结构遍历的核心算法,在LongTree中建立了3个数组long [] keys、char [] leftChildren和char [] rightSiblings。

一个节点有两个特性:它有子节点;它有兄弟节点。leftChildren保存的是这个节点的子节点的索引;而rightSiblings保存的是这个节点兄弟节点的索引。例如:

  1000

   |-- 3000

   |-- |--4000

   |-- |--6000

   |-- |--7000

   |-- 5000

1000是个根节点,1000下有两个子节点3000和5000,而3000则有3个子节点4000、6000和7000,3000还有一个兄弟节点5000,使用上述3个数组是这样保持信息的:

keys[0] = 1000

keys[1] = 3000

keys[2] = 4000

keys[3] = 5000

keys[4] = 6000

keys[5] = 7000

keys数组中保存的是各个节点的数值,而leftChildren和rightSiblings数组保存的是keys数组的index,即0、1、2、3、4等数字。

1000节点有两个子节点,那么其对应的leftChildren和rightSiblings分别是:

leftChildren[0] = 1

leftChildren[0]中的索引0表示当前索引,keus[0]是1000,说明现在节点是1000;1也表示keys数组的索引,keys[1]的值是3000,所以上一句表示1000的子节点是3000。

1000节点没有兄弟节点:

rightSiblings[0] = -1

再看看3000节点,其keys的索引Index是1,其子节点是4000、6000和7000,取最近一个4000的索引index放入数组:

leftChildren[1] = 2

这表示1000节点的子节点是4000,那么另外一个6000节点如何表示?这是以4000节点的兄弟节点表现出来的。4000节点的keys的索引index是2,通过下列表示:

rightSiblings[2] = 4

其中,4表示6000在keys中的索引Index。同样,第3个子节点7000表示如下:

rightSiblings[4] = 5

这样,3000节点有3个子节点4000、6000和7000(4000、6000和7000是兄弟节点)通过上述简单两句就表现出来了。

总结一个父子关系树的表示方法:在父节点中,使用leftChildren保存最靠近父节点的一个子节点(父节点的第一个儿子,叫长子)的索引,其他子节点则是通过rightSiblings表明与长子是兄弟关系。

看看LongTress的初始化构造方法,keys数组的值保存的是ForumMessage的ID,如下:

public LongTree(long rootKey, int size) {

    keys = new long[size+1];                        //初始化

    leftChildren = new char[size+1];               //初始化

    rightSiblings = new char[size+1];              //初始化



    // 在keys[1]中保存的是rootMessage 的ID

    keys[1] = rootKey;

    leftChildren[1] = 0;                                 //无子节点

    rightSiblings[1] = 0;                                 //无兄弟姐妹

}
当加入一个节点时,其方法如下:

public void addChild(long parentKey, long newKey) {

    // 根据parentKey找出其对应的keys索引index

    char parentIndex = findKey(parentKey, (char)1);

    if (parentIndex == 0) {

            throw new IllegalArgumentException("Parent key " + parentKey +

                    " not found when adding child " + newKey + ".");

    }



    // 为newKey创建节点

    keys[nextIndex] = newKey;

    leftChildren[nextIndex] = 0;

    rightSiblings[nextIndex] = 0;



    //将新建节点标志为父节点的子节点

    if (leftChildren[parentIndex] == 0) {

        // 如果父节点原来没有子节点,那就将新建节点作为其子节点

        leftChildren[parentIndex] = nextIndex;

    }else {

        // 如果父节点有子节点,寻找最后一个子节点

        long siblingIndex = leftChildren[parentIndex];

       //在siblingIndex中循环查找,直至值为0

        while (rightSiblings[new Long(siblingIndex).intValue()] != 0) {

             siblingIndex = rightSiblings[new Long(siblingIndex).intValue()];

        }

        // 将新建节点作为最后一个字节点加入

        rightSiblings[new Long(siblingIndex).intValue()] = nextIndex;

    }

    // 最后,自动增加nextIndex 以便下一个等待插入

    nextIndex++;

}

Jive将数据导出到XML文件时,就是根据某个ForumMessage的ID,通过TreeWalker找出它的所有子节点ForumMessage的ID,然后将其内容导出。
5.2  XML和JDOM

XML 称为可扩充标记语言,是类似HTML定义文档标记语言的一个框架。XML以结构严谨著称,因此用来保存数据是非常适合的,这样在数据库之外,又多了一个持久化保存数据的方式。

在Java中,XML更多时是作为配置文件数据存储体形式出现,在之前一般是使用properties来保存系统的配置文件,如下:

cache.maxsize=1024

cache.minsize=2

这两句分别设置cache的最大值和最小值,那么在Java中通过下列语句读取:

Properties p = new Properties();

InputStream fin = new FileInputStream("Config.properties");

p.load(fin);

String maxSize = p.getProperty("cache.maxsize ");

String minSize = p.getProperty("cache.minsize ");

这样就可以获得配置文件中的两个值。

这种配置文件使用方法简单直接,但是只适合配置文件不很复杂的情况。在复杂的配置情况下,properties就不是很合适,例如设置系统的可选属性,一个系统安装在不同应用场合,客户的要求总有些不一样,有些功能是可选的,那么需要在配置文件中配置一些可选的功能,以Tomcat的server.xml为例,如下:

<Context path="/register" docBase="D:/javasource/SimpleRegister/defaultroot" debug="1"

                 reloadable="true" crossContext="true">



</Context>

<Context path="/examples" docBase="examples" debug="0"

                 reloadable="true" crossContext="true">

          <Logger className="org.apache.catalina.logger.FileLogger"

                     prefix="localhost_examples_log." suffix=".txt"

              timestamp="true"/>



</Context>
--------------------编程问答--------------------
在一个配置中有很多Context,每个Contexr都包含Logger等具体配置,XML格式本身是一种树形结构的数据格式。在实际应用中,很多复杂的表示都可以使用树形结构来分解代表。因此,使用XML来表示这种树形结构的数据无疑是非常合适的。

在Jive中,jive_config.xml是Jive系统的配置文件。这个配置文件是在Jive系统安装时,按照用户的选择动态生成的,其中包含数据库连接参数、界面显示颜色、电子邮件配置以及缓冲配置、搜索配置和文件或图片上传配置。

分析读取XML数据有很多工具,如DOM(http://www.worg/DOM/)和SAX(http://www.saxproject.org/)。这两种是标准的XML分析器,可以使用任何语言来实现,DOM分析XML数据时,是将整个文档一下子读入内存,如果文档很大,性能就发生影响,而SAX则是动态地对每一行分析,无需全部读入,因此在分析大文档时速度比较快。

但是这两种分析方法都是围绕XML树形结构展开的,在编制这两种分析器时,会涉及到大量XML概念的API,需要一定的XML基础和知识,使用起来有一定难度。

JDOM(http://www.jdom.org)封装了DOM/SAX的具体使用技术,以非常符合Java编程方式的形式来分析XML,因此使用起来非常方便。

在分析速度方面,JDOM比DOM要快,比SAX慢一点。但用在分析配置文件上,速度不是主要的,因为可以使用lazy initialization。这类似缓存机制,在第一次读取后就保存在内存中,以后每次直接从内存中获取。

在Jive中,JDOM操作基本是由JiveGlobals完成的。

public class JiveGlobals {

 private static final String JIVE_CONFIG_FILENAME = "jive_config.xml";

private static XMLProperties properties = null;



...

//从配置文件获取配置

 public static String getJiveProperty(String name) {

  loadProperties();

  return properties.getProperty(name);

 }

  //用JDOM载入配置文件

 private synchronized static void loadProperties() {

  if (properties == null) {

   properties = new XMLProperties(jiveHome + File.separator +

           JIVE_CONFIG_FILENAME);

  }

 }
//将配置保存到配置文件中

public static void setJiveProperty(String name, String value) {

   loadProperties();

   properties.setProperty(name, value);

}

}

从上面代码看出,对XML文件读写非常方便,使用properties.getProperty(name)就可以获得name的配置值,而properties.setProperty(name, value)一句就可以将name和其值value保存到XML文件中,非常类似Hashtable的读取和存入。

XMLProperties是JDOM的一个属性文件辅助包,它主要是对属性名进行分解和合成,例如XML如下:

<jive>

 <email>

  <fromName>Jive_Administrator</fromName>

  <fromEmail>webmaster@example.com</fromEmail>

  <subject>Your thread was updated!</subject>

  <body>Hello {name}! The thread {threadName} was updated!</body>

 </email>

<jive>

jive/email/fromName的值是Jive_Administrator,那么如何读取Jive_Administrator?使用properties.getProperty("email.fromName")就可以。注意到,这里Key的名字组合是 email.fromName,这种特定的写法就是XMLProperties可以支持的,在对XML文件保存细节中,由XMLProperties将这种属性名称写法具体转换成XML文档操作。具体内部代码如下:

  public String getProperty(String name) {

        if (propertyCache.containsKey(name)) {  //从缓存中获取

            return (String)propertyCache.get(name);

        }

         //将email.fromName转变为String数组

         //例如propName[0] = jive; propName[1] = email …

        String[] propName = parsePropertyName(name);

        // 通过propName数组循环,遍历XML的树形结构层次,寻找出对应的属性值

        Element element = doc.getRootElement();

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

            element = element.getChild(propName);

            if (element == null) {

                return null;

            }

        }

        // 寻找到element后,获得其内容

        String value = element.getText();

        if ("".equals(value)) {            return null;        }

        else {

            // 保存到缓存中

            value = value.trim();

            propertyCache.put(name, value);

            return value;

        }

    }

以上只是分析了JDOM的XMLProperties包是如何做属性配置提取的,正是因为JDOM内部做了很多基础支持性的细节工作,才使得使用JDOM变得非常方便。

总结使用JDOM对配置文件读写操作语法如下:

·          获得配置(查询):getProperty(name)。

·          新增和修改:properties.setProperty(name, value)。

·          删除:properties.deleteProperty(name)。

name的格式是xxx.xxx.xxx,例如:

<jive>

   …

<upload>

        <dir>/home/jdon/jive/upload/</dir>

        <relurl>upload/</relurl>

    </upload>

    …

</jive>

要获得/home/jdon/jive/upload/,name的格式是upload.dir;要获得upload/,name的格式是upload.relurl。

注意,如果要在系统中支持上述功能,必须下载JDOM包,还要有DataFormatFilter. java、DataUnformatFilter.java、XMLFilterBase.java和XMLProperties.java支持。这几个类不包含在JDOM标准包中,作为一个应用包含在其Sample中。当然也可以直接从Jive中复制出来使用。
5.3  全文检索和Lucene

Jive中支持全文检索,这个功能主要核心依赖另外一个开放源代码项目Lucene(http://jakarta.apache.org/lucene/docs/index.html)。Jakarta Lucene是一个高性能全文搜索引擎,可以跨平台应用于任何搜索应用。

使用Lucene作为搜索引擎,应用系统需要做两件事情:

(1)建立索引文件。将Jive数据库中的数据内容建立索引文件,这是通过SearchManager来完成。SearchManager代码如下:

public interface SearchManager {

    public boolean isSearchEnabled();

    public void setSearchEnabled(boolean searchEnabled);

    /**

    //如果SearchManage正在工作,返回真

    public boolean isBusy();

    //返回索引完成率

    public int getPercentComplete();

    //是否自动建立索引

    //通过TaskEngine.scheduleTask方法实现定期自动索引

    public boolean isAutoIndexEnabled();

    public void setAutoIndexEnabled(boolean value);

    //自动索引间隔的分钟数

    public int getAutoIndexInterval();

    public void setAutoIndexInterval(int minutes);

    //获得上次建立索引的时间

    public Date getLastIndexedDate();

//在实时建立索引时,将当前帖子加入索引

    public void addToIndex(ForumMessage message);

    public void removeFromIndex(ForumMessage message);

    //手动更新自上次建立索引后的新内容

    public void updateIndex();

    //手动重新建立全部的索引

    public void rebuildIndex();

    //优化

    public void optimize();

}

·          SearchManager定义了建立索引的一些属性,建立索引有两种方式:当有新帖子加入时,通过调用indexMessage()方法实时索引;或者通过TaskEngine.scheduleTask方法每隔一定时间建立索引。

·          DbSearchManager作为SearchManager的子类实现,又是一个线程类,它是建立索引的主要功能类。在DbSearchManager中主要使用了Lucene的IndexWriter、 Analyzer、 Document和 Field等功能类来建立索引。

·          IndexWriter用户建立新的索引,当然也可以将文档加入已经存在的索引。

在文本被索引之前,它必须通过一个分析器Analyzer。分析器Analyzer 负责从文本中分离出索引关键字。Lucene有几种不同类型的分析器:

·          SimpleAnalyzer是将英文转换为小写字母,按空格和标点符号切分出英文单词,

如I am Java这一句,使用SimpleAnalyzer切词就会切分出下列词语:

token1=I

token2=am

token3=Java

·          StandardAnalyzer是对英文进行了较为复杂的处理。除了按词语建立索引关键字(token)外,还能够为特殊名称、邮件地址、缩写格式等建立索引单元,而且对“and”、“ the”等词语做了过滤。

·          ChineseAnalyzer是专门用来分析中文的索引的。关于中文分析器,有很多尝试,如车东的http://sourceforge.net/projects/weblucene/;zhoujun的http://www. jdon.com/jive/thread.jsp? forum=61&thread=8400等,该问题将在后面章节继续讨论。

一个索引是由一系列Document组成,每个Document是由一个或多个Field组成,每个Field都有一个名字和值,可以把Document作为关系数据库中一条记录,而Field则是记录中某列字段。一般建立索引如下:

//指定将在哪个目录建立索引

String indexDir = "/home/jdon/jive/WEB-INF/jiveHome";

//指定将要建立索引的文本

String text = "welcom here, I am Java,";     

Analyzer analyzer = new StandardAnalyzer();   //使用StandardAnalyzer

//建立一个IndexWriter

IndexWriter writer = new IndexWriter(indexDir, analyzer, true);

//建立Document

Document document  = new Document();

//进行切词、索引

document.add(Field.Text("fieldname", text));

//加入索引中

writer.addDocument(document);

writer.close();

其中,Field根据具体要求有不同用法,Lucene提供4种类型的Field: Keyword、 UnIndexed、 UnStored和 Text。

·          Keyword 不实现切词,逐字地保存在索引中,这种类型适合一些如URL、日期、个人姓名、社会安全号码、电话号码等需要原封不动保留的词语。

·          UnIndexed既不实现切词也不索引,但是其值是一个词一个词地保存在索引中,这不适合很大很长的词语,适合于显示一些不经过直接搜索的结果值。

·          UnStored与UnIndexed正好相反,将被切词和索引,但是不保存在索引中,这适合巨大文本,如帖子内容、页面内容等。

·          Text是实现切词、索引,并且保存在索引中。

在Jive中,索引的建立以DbSearchManager中加入帖子索引方法为例:

protected final void addMessageToIndex(long messageID, long userID,

            long threadID, long forumID, String subject, String body,

            java.util.Date creationDate, IndexWriter writer) throws IOException

{

    //建立一个  Document

    Document doc = new Document();

    doc.add(Field.Keyword("messageID",Long.toString(messageID)));

    doc.add(new Field("userID", Long.toString(userID), false, true, false));

   doc.add(new Field("threadID", Long.toString(threadID), false, true, false));

    doc.add(new Field("forumID", Long.toString(forumID), false, true, false));

    doc.add(Field.UnStored("subject", subject));

    doc.add(Field.UnStored("body", body));

    doc.add(new Field("creationDate", DateField.dateToString(creationDate),

                false, true, false));

    //将该Document加入当前索引中

    writer.addDocument(doc);

} --------------------编程问答-------------------- 在DbSearchManager中同时也实现了自动建立索引的过程,通过在构造方法中生成TimeTask实例:

timerTask = TaskEngine.scheduleTask(

                    this,autoIndexInterval*JiveGlobals.MINUTE,

                    autoIndexInterval*JiveGlobals.MINUTE);

因为DbSearchManager是线程类,它在run方法中实现索引任务自动运行:

TaskEngine.addTask(new IndexTask(false));

(2)建立完成后,就可以直接搜索特定的词语了。搜索语句一般代码如下:

Searcher searcher = new IndexSearcher((indexDir);  //创建一个搜索器

//使用和索引同样的语言分析器

Query query = QueryParser.parse(queryString, "body", new StandardAnalyzer());

//搜索结果使用Hits存储

Hits hits = searcher.search(query);

//通过hits得到相应字段的数据和查询的匹配度

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

      System.out.println(hits.doc(i).get("fieldname "));

};

Jive实现搜索就复杂得多,它为搜索专门建立了一个Query接口:

public interface Query {

    //需要搜索的字符串

    public String getQueryString();

    public void setQueryString(String queryString);



    public Date getBeforeDate();

    public void setBeforeDate(Date beforeDate);



    public Date getAfterDate();

    public void setAfterDate(Date afterDate);



    public User getFilteredUser();

    public void filterOnUser(User user);



    public ForumThread getFilteredThread();

    public void filterOnThread(ForumThread thread);



    public int resultCount();

    public Iterator results();

    public Iterator results(int startIndex, int numResults);

}

Query接口中主要定义了和搜索相关的一些参数,可以根据具体要求定制,直接使用Query就可以达到搜索的目的,如需要搜索Java is cool,那么使用下列代码:

ForumFactory forumFactory = ForumFactory.getInstance();

Query query = forumFactory.createQuery(forums);

query.setQueryString("Jive is cool");

Iterator iter = query.results();

while (iter.hasNext()) {

     ForumMessage message = (ForumMessage)iter.nextElement();

     //输出结果

}

追查代码会发现,上面forumFactory.createQuery(forums)方法实际内容是new DbQuery(forums, this)。DbQuery作为Query的一个子类,它的搜索语句通过executeQuery()方法中下列语句实现:

private void executeQuery() {

    try {

       Searcher searcher = getSearcher();  //创建一个搜索器

       …

       //使用分析器获得Query对象

       org.apache.lucene.search.Query bodyQuery =

                QueryParser.parse(queryString, "body", DbSearchManager.analyzer);

      org.apache.lucene.search.Query subjectQuery =

                QueryParser.parse(queryString, "subject", DbSearchManager.analyzer);

        //将两个Query对象加入BooleanQuery

        BooleanQuery comboQuery = new BooleanQuery();

        comboQuery.add(subjectQuery,false,false);

        comboQuery.add(bodyQuery,false,false);

        //Jive自己的搜索结果过滤器

        MultiFilter multiFilter = new MultiFilter(3);

        int filterCount = 0;

   

        if (factory.getForumCount() != forums.length) {

             //将其他论坛内容搜索结果过滤掉

            String[] forumIDs = new String[forums.length];

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

                forumIDs[i] = Long.toString(forums[i].getID());

             }

             multiFilter.add(new FieldFilter("forumID", forumIDs));

             filterCount++;

        }



         //日期过滤器  如只查询某日期以后的内容

        if (beforeDate != null || afterDate != null) {

            if (beforeDate != null && afterDate != null) {

                multiFilter.add(new DateFilter("creationDate", beforeDate, afterDate));

                filterCount++;

             }else if (beforeDate == null) {

                multiFilter.add(DateFilter.After("creationDate", afterDate));

                 filterCount++;

             }else {

                 multiFilter.add(DateFilter.Before("creationDate", beforeDate));

                 filterCount++;

             }

        }

        // 过滤用户

        if (user != null) {

             String userID = Long.toString(user.getID());

             multiFilter.add(new FieldFilter("userID", userID));

             filterCount++;

        }

        // 主题过滤

        if (thread != null) {

             String threadID = Long.toString(thread.getID());

             multiFilter.add(new FieldFilter("threadID", threadID));

             filterCount++;

        }

        if (filterCount > 0) {//实现搜索

             hits = searcher.search(comboQuery, multiFilter);

        } else {

             hits = searcher.search(comboQuery);

        }

        //搜索结果不要超过最大大小

        int numResults = hits.length() < MAX_RESULTS_SIZE ?

                    hits.length() : MAX_RESULTS_SIZE;

        long [] messages = new long[numResults];

        for (int i=0; i<numResults; i++) {

           messages[i]= Long.parseLong( ((Document)hits.doc(i)).get("messageID") );

       }

        results = messages;

      } catch (Exception e) {

        e.printStackTrace();

        results = new long[0];

      }

}

Jive的搜索使用了过滤器,以便过滤掉不想出现的结果,然后还对搜索结果进行了限制转换,这些在实际使用中都是必需的。 --------------------编程问答-------------------- 都是牛人!!!打这么多、、up!! --------------------编程问答-------------------- ...没敢看!迅速拖到底部。
我的想法是代码量不大,就用隐藏标签。每选择一个比如是省,就进入一个事件,在事件里面根据选择的哪个省再显示下面的地区。。。就是把相应的地区标签显示属性修改成可以显示~~~~
补充:Java ,  Java EE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,