小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Jive的其他組件技術(shù)

 大千 2009-04-16

5  Jive的其他組件技術(shù)

Jive是一個(gè)比較豐富的知識(shí)寶藏,從中可以學(xué)習(xí)到很多新的實(shí)戰(zhàn)技巧和具體功能實(shí)現(xiàn)方式。前面基本介紹了Jive中的一些主要架構(gòu)技術(shù),通過這些技術(shù)可以基本上掌握Jive論壇系統(tǒng)。

Jive中還有很多非常實(shí)用的組件技術(shù)和工具庫,分析學(xué)習(xí)可重用技術(shù),可以在自己具體的項(xiàng)目重復(fù)使用,大大提高了新系統(tǒng)的開發(fā)速度和效率。

Jive的管理功能中提供了將Jive數(shù)據(jù)庫數(shù)據(jù)導(dǎo)出到XML文件的管理工具,在這個(gè)工具功能實(shí)現(xiàn)中,使用了樹形結(jié)構(gòu)的遍歷技術(shù)。

JiveForumThread中的第一個(gè)ForumMessage作為root ForumMessage,以這個(gè)ForumMessage為根節(jié)點(diǎn),每個(gè)ForumThread中包含了一套樹形結(jié)構(gòu)。

TreeWalker是樹形結(jié)構(gòu)的一個(gè)抽象接口,代碼如下:

public interface TreeWalker {

    //根節(jié)點(diǎn)

    public ForumMessage getRoot();

    //獲得父節(jié)點(diǎn)

    public ForumMessage getParent(ForumMessage child)

            throws ForumMessageNotFoundException;

    //獲得子節(jié)點(diǎn)

    public ForumMessage getChild(ForumMessage parent, int index)

            throws ForumMessageNotFoundException;

    //獲得所有子節(jié)點(diǎn)

    public Iterator children(ForumMessage parent);

    //獲得所有的子節(jié)點(diǎn),包括子節(jié)點(diǎn)的子節(jié)點(diǎn)

    public Iterator recursiveChildren(ForumMessage parent);

    //獲得一個(gè)節(jié)點(diǎn)的深度,相對(duì)根節(jié)點(diǎn)而言

    public int getMessageDepth(ForumMessage message);

   

    public int getChildCount(ForumMessage parent);

    public int getRecursiveChildCount(ForumMessage parent);

 

    /**

     * 返回相對(duì)父節(jié)點(diǎn)的子節(jié)點(diǎn)索引。例如

     * <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);

    //一個(gè)節(jié)點(diǎn)是否是葉,葉相對(duì)枝來說,葉沒有子節(jié)點(diǎn)了

    public boolean isLeaf(ForumMessage node);

}

DbTreeWalker TreeWalker的一個(gè)實(shí)現(xiàn),它是將一個(gè)ForumThread下所有帖子的ID從數(shù)據(jù)庫中裝入LongTree中。一句LongTree的樹形結(jié)構(gòu)遍歷核心技術(shù)實(shí)現(xiàn)ForumThread中的帖子結(jié)構(gòu)的遍歷。

LongTree類似之前的Cache類,封裝了樹形結(jié)構(gòu)遍歷的核心算法,在LongTree中建立了3個(gè)數(shù)組long [] keys、char [] leftChildrenchar [] rightSiblings。

一個(gè)節(jié)點(diǎn)有兩個(gè)特性:它有子節(jié)點(diǎn);它有兄弟節(jié)點(diǎn)。leftChildren保存的是這個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)的索引;而rightSiblings保存的是這個(gè)節(jié)點(diǎn)兄弟節(jié)點(diǎn)的索引。例如:

  1000

   |-- 3000

   |-- |--4000

   |-- |--6000

   |-- |--7000

   |-- 5000

1000是個(gè)根節(jié)點(diǎn),1000下有兩個(gè)子節(jié)點(diǎn)30005000,而3000則有3個(gè)子節(jié)點(diǎn)4000、60007000,3000還有一個(gè)兄弟節(jié)點(diǎn)5000,使用上述3個(gè)數(shù)組是這樣保持信息的:

keys[0] = 1000

keys[1] = 3000

keys[2] = 4000

keys[3] = 5000

keys[4] = 6000

keys[5] = 7000

keys數(shù)組中保存的是各個(gè)節(jié)點(diǎn)的數(shù)值,而leftChildrenrightSiblings數(shù)組保存的是keys數(shù)組的index,即01、23、4等數(shù)字。

1000節(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn),那么其對(duì)應(yīng)的leftChildrenrightSiblings分別是:

leftChildren[0] = 1

leftChildren[0]中的索引0表示當(dāng)前索引,keus[0]1000,說明現(xiàn)在節(jié)點(diǎn)是1000;1也表示keys數(shù)組的索引,keys[1]的值是3000,所以上一句表示1000的子節(jié)點(diǎn)是3000

1000節(jié)點(diǎn)沒有兄弟節(jié)點(diǎn):

rightSiblings[0] = -1

再看看3000節(jié)點(diǎn),其keys的索引Index1,其子節(jié)點(diǎn)是4000、60007000,取最近一個(gè)4000的索引index放入數(shù)組:

leftChildren[1] = 2

這表示1000節(jié)點(diǎn)的子節(jié)點(diǎn)是4000,那么另外一個(gè)6000節(jié)點(diǎn)如何表示?這是以4000節(jié)點(diǎn)的兄弟節(jié)點(diǎn)表現(xiàn)出來的。4000節(jié)點(diǎn)的keys的索引index2,通過下列表示:

rightSiblings[2] = 4

其中,4表示6000keys中的索引Index。同樣,第3個(gè)子節(jié)點(diǎn)7000表示如下:

rightSiblings[4] = 5

這樣,3000節(jié)點(diǎn)有3個(gè)子節(jié)點(diǎn)4000、600070004000、60007000是兄弟節(jié)點(diǎn))通過上述簡單兩句就表現(xiàn)出來了。

總結(jié)一個(gè)父子關(guān)系樹的表示方法:在父節(jié)點(diǎn)中,使用leftChildren保存最靠近父節(jié)點(diǎn)的一個(gè)子節(jié)點(diǎn)(父節(jié)點(diǎn)的第一個(gè)兒子,叫長子)的索引,其他子節(jié)點(diǎn)則是通過rightSiblings表明與長子是兄弟關(guān)系。

看看LongTress的初始化構(gòu)造方法,keys數(shù)組的值保存的是ForumMessageID,如下:

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;                                 //無子節(jié)點(diǎn)

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

}

當(dāng)加入一個(gè)節(jié)點(diǎn)時(shí),其方法如下:

public void addChild(long parentKey, long newKey) {

    // 根據(jù)parentKey找出其對(duì)應(yīng)的keys索引index

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

    if (parentIndex == 0) {

            throw new IllegalArgumentException("Parent key " + parentKey +

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

    }

 

    // newKey創(chuàng)建節(jié)點(diǎn)

    keys[nextIndex] = newKey;

    leftChildren[nextIndex] = 0;

    rightSiblings[nextIndex] = 0;

 

    //將新建節(jié)點(diǎn)標(biāo)志為父節(jié)點(diǎn)的子節(jié)點(diǎn)

    if (leftChildren[parentIndex] == 0) {

        // 如果父節(jié)點(diǎn)原來沒有子節(jié)點(diǎn),那就將新建節(jié)點(diǎn)作為其子節(jié)點(diǎn)

        leftChildren[parentIndex] = nextIndex;

    }else {

        // 如果父節(jié)點(diǎn)有子節(jié)點(diǎn),尋找最后一個(gè)子節(jié)點(diǎn)

        long siblingIndex = leftChildren[parentIndex];

       //siblingIndex中循環(huán)查找,直至值為0

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

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

        }

        // 將新建節(jié)點(diǎn)作為最后一個(gè)字節(jié)點(diǎn)加入

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

    }

    // 最后,自動(dòng)增加nextIndex 以便下一個(gè)等待插入

    nextIndex++;

}

Jive將數(shù)據(jù)導(dǎo)出到XML文件時(shí),就是根據(jù)某個(gè)ForumMessageID,通過TreeWalker找出它的所有子節(jié)點(diǎn)ForumMessageID,然后將其內(nèi)容導(dǎo)出。

5.2  XMLJDOM

XML 稱為可擴(kuò)充標(biāo)記語言,是類似HTML定義文檔標(biāo)記語言的一個(gè)框架。XML以結(jié)構(gòu)嚴(yán)謹(jǐn)著稱,因此用來保存數(shù)據(jù)是非常適合的,這樣在數(shù)據(jù)庫之外,又多了一個(gè)持久化保存數(shù)據(jù)的方式。

Java中,XML更多時(shí)是作為配置文件數(shù)據(jù)存儲(chǔ)體形式出現(xiàn),在之前一般是使用properties來保存系統(tǒng)的配置文件,如下:

cache.maxsize=1024

cache.minsize=2

這兩句分別設(shè)置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 ");

這樣就可以獲得配置文件中的兩個(gè)值。

這種配置文件使用方法簡單直接,但是只適合配置文件不很復(fù)雜的情況。在復(fù)雜的配置情況下,properties就不是很合適,例如設(shè)置系統(tǒng)的可選屬性,一個(gè)系統(tǒng)安裝在不同應(yīng)用場合,客戶的要求總有些不一樣,有些功能是可選的,那么需要在配置文件中配置一些可選的功能,以Tomcatserver.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>

在一個(gè)配置中有很多Context,每個(gè)Contexr都包含Logger等具體配置,XML格式本身是一種樹形結(jié)構(gòu)的數(shù)據(jù)格式。在實(shí)際應(yīng)用中,很多復(fù)雜的表示都可以使用樹形結(jié)構(gòu)來分解代表。因此,使用XML來表示這種樹形結(jié)構(gòu)的數(shù)據(jù)無疑是非常合適的。

Jive中,jive_config.xmlJive系統(tǒng)的配置文件。這個(gè)配置文件是在Jive系統(tǒng)安裝時(shí),按照用戶的選擇動(dòng)態(tài)生成的,其中包含數(shù)據(jù)庫連接參數(shù)、界面顯示顏色、電子郵件配置以及緩沖配置、搜索配置和文件或圖片上傳配置。

分析讀取XML數(shù)據(jù)有很多工具,如DOMhttp://www.worg/DOM/)和SAXhttp://www./)。這兩種是標(biāo)準(zhǔn)的XML分析器,可以使用任何語言來實(shí)現(xiàn),DOM分析XML數(shù)據(jù)時(shí),是將整個(gè)文檔一下子讀入內(nèi)存,如果文檔很大,性能就發(fā)生影響,而SAX則是動(dòng)態(tài)地對(duì)每一行分析,無需全部讀入,因此在分析大文檔時(shí)速度比較快。

但是這兩種分析方法都是圍繞XML樹形結(jié)構(gòu)展開的,在編制這兩種分析器時(shí),會(huì)涉及到大量XML概念的API,需要一定的XML基礎(chǔ)和知識(shí),使用起來有一定難度。

JDOMhttp://www.)封裝了DOM/SAX的具體使用技術(shù),以非常符合Java編程方式的形式來分析XML,因此使用起來非常方便。

在分析速度方面,JDOMDOM要快,比SAX慢一點(diǎn)。但用在分析配置文件上,速度不是主要的,因?yàn)榭梢允褂?/span>lazy initialization。這類似緩存機(jī)制,在第一次讀取后就保存在內(nèi)存中,以后每次直接從內(nèi)存中獲取。

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);

 }

}

從上面代碼看出,對(duì)XML文件讀寫非常方便,使用properties.getProperty(name)就可以獲得name的配置值,而properties.setProperty(name, value)一句就可以將name和其值value保存到XML文件中,非常類似Hashtable的讀取和存入。

XMLPropertiesJDOM的一個(gè)屬性文件輔助包,它主要是對(duì)屬性名進(jìn)行分解和合成,例如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可以支持的,在對(duì)XML文件保存細(xì)節(jié)中,由XMLProperties將這種屬性名稱寫法具體轉(zhuǎn)換成XML文檔操作。具體內(nèi)部代碼如下:

  public String getProperty(String name) {

        if (propertyCache.containsKey(name)) {  //從緩存中獲取

            return (String)propertyCache.get(name);

        }

         //email.fromName轉(zhuǎn)變?yōu)?/span>String數(shù)組

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

        String[] propName = parsePropertyName(name);

        // 通過propName數(shù)組循環(huán),遍歷XML的樹形結(jié)構(gòu)層次,尋找出對(duì)應(yīng)的屬性值

        Element element = doc.getRootElement();

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

            element = element.getChild(propName[i]);

            if (element == null) {

                return null;

            }

        }

        // 尋找到element后,獲得其內(nèi)容

        String value = element.getText();

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

        else {

            // 保存到緩存中

            value = value.trim();

            propertyCache.put(name, value);

            return value;

        }

    }

以上只是分析了JDOMXMLProperties包是如何做屬性配置提取的,正是因?yàn)?/span>JDOM內(nèi)部做了很多基礎(chǔ)支持性的細(xì)節(jié)工作,才使得使用JDOM變得非常方便。

總結(jié)使用JDOM對(duì)配置文件讀寫操作語法如下:

·          獲得配置(查詢):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。

注意,如果要在系統(tǒng)中支持上述功能,必須下載JDOM包,還要有DataFormatFilter. java、DataUnformatFilter.java、XMLFilterBase.javaXMLProperties.java支持。這幾個(gè)類不包含在JDOM標(biāo)準(zhǔn)包中,作為一個(gè)應(yīng)用包含在其Sample中。當(dāng)然也可以直接從Jive中復(fù)制出來使用。

5.3  全文檢索和Lucene

Jive中支持全文檢索,這個(gè)功能主要核心依賴另外一個(gè)開放源代碼項(xiàng)目Lucenehttp://jakarta./lucene/docs/index.html)。Jakarta Lucene是一個(gè)高性能全文搜索引擎,可以跨平臺(tái)應(yīng)用于任何搜索應(yīng)用。

使用Lucene作為搜索引擎,應(yīng)用系統(tǒng)需要做兩件事情:

1)建立索引文件。將Jive數(shù)據(jù)庫中的數(shù)據(jù)內(nèi)容建立索引文件,這是通過SearchManager來完成。SearchManager代碼如下:

public interface SearchManager {

    public boolean isSearchEnabled();

    public void setSearchEnabled(boolean searchEnabled);

    /**

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

    public boolean isBusy();

    //返回索引完成率

    public int getPercentComplete();

    //是否自動(dòng)建立索引

    //通過TaskEngine.scheduleTask方法實(shí)現(xiàn)定期自動(dòng)索引

    public boolean isAutoIndexEnabled();

    public void setAutoIndexEnabled(boolean value);

    //自動(dòng)索引間隔的分鐘數(shù)

    public int getAutoIndexInterval();

    public void setAutoIndexInterval(int minutes);

    //獲得上次建立索引的時(shí)間

    public Date getLastIndexedDate();

//在實(shí)時(shí)建立索引時(shí),將當(dāng)前帖子加入索引

    public void addToIndex(ForumMessage message);

    public void removeFromIndex(ForumMessage message);

    //手動(dòng)更新自上次建立索引后的新內(nèi)容

    public void updateIndex();

    //手動(dòng)重新建立全部的索引

    public void rebuildIndex();

    //優(yōu)化

    public void optimize();

}

·          SearchManager定義了建立索引的一些屬性,建立索引有兩種方式:當(dāng)有新帖子加入時(shí),通過調(diào)用indexMessage()方法實(shí)時(shí)索引;或者通過TaskEngine.scheduleTask方法每隔一定時(shí)間建立索引。

·          DbSearchManager作為SearchManager的子類實(shí)現(xiàn),又是一個(gè)線程類,它是建立索引的主要功能類。在DbSearchManager中主要使用了LuceneIndexWriter、 Analyzer、 Document Field等功能類來建立索引。

·          IndexWriter用戶建立新的索引,當(dāng)然也可以將文檔加入已經(jīng)存在的索引。

在文本被索引之前,它必須通過一個(gè)分析器Analyzer。分析器Analyzer 負(fù)責(zé)從文本中分離出索引關(guān)鍵字。Lucene有幾種不同類型的分析器:

·          SimpleAnalyzer是將英文轉(zhuǎn)換為小寫字母,按空格和標(biāo)點(diǎn)符號(hào)切分出英文單詞,

I am Java這一句,使用SimpleAnalyzer切詞就會(huì)切分出下列詞語:

token1=I

token2=am

token3=Java

·          StandardAnalyzer是對(duì)英文進(jìn)行了較為復(fù)雜的處理。除了按詞語建立索引關(guān)鍵字(token)外,還能夠?yàn)樘厥饷Q、郵件地址、縮寫格式等建立索引單元,而且對(duì)“and”、“ the”等詞語做了過濾。

·          ChineseAnalyzer是專門用來分析中文的索引的。關(guān)于中文分析器,有很多嘗試,如車東的http:///projects/weblucene/;zhoujunhttp://www. jdon.com/jive/thread.jsp? forum=61&thread=8400等,該問題將在后面章節(jié)繼續(xù)討論。

一個(gè)索引是由一系列Document組成,每個(gè)Document是由一個(gè)或多個(gè)Field組成,每個(gè)Field都有一個(gè)名字和值,可以把Document作為關(guān)系數(shù)據(jù)庫中一條記錄,而Field則是記錄中某列字段。一般建立索引如下:

//指定將在哪個(gè)目錄建立索引

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

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

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

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

//建立一個(gè)IndexWriter

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

//建立Document

Document document  = new Document();

//進(jìn)行切詞、索引

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

//加入索引中

writer.addDocument(document);

writer.close();

其中,Field根據(jù)具體要求有不同用法,Lucene提供4種類型的Field: Keyword、 UnIndexed、 UnStored Text

·          Keyword 不實(shí)現(xiàn)切詞,逐字地保存在索引中,這種類型適合一些如URL、日期、個(gè)人姓名、社會(huì)安全號(hào)碼、電話號(hào)碼等需要原封不動(dòng)保留的詞語。

·          UnIndexed既不實(shí)現(xiàn)切詞也不索引,但是其值是一個(gè)詞一個(gè)詞地保存在索引中,這不適合很大很長的詞語,適合于顯示一些不經(jīng)過直接搜索的結(jié)果值。

·          UnStoredUnIndexed正好相反,將被切詞和索引,但是不保存在索引中,這適合巨大文本,如帖子內(nèi)容、頁面內(nèi)容等。

·          Text是實(shí)現(xiàn)切詞、索引,并且保存在索引中。

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

{

    //建立一個(gè)  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加入當(dāng)前索引中

    writer.addDocument(doc);

}

DbSearchManager中同時(shí)也實(shí)現(xiàn)了自動(dòng)建立索引的過程,通過在構(gòu)造方法中生成TimeTask實(shí)例:

timerTask = TaskEngine.scheduleTask(

                    this,autoIndexInterval*JiveGlobals.MINUTE,

                    autoIndexInterval*JiveGlobals.MINUTE);

因?yàn)?/span>DbSearchManager是線程類,它在run方法中實(shí)現(xiàn)索引任務(wù)自動(dòng)運(yùn)行:

TaskEngine.addTask(new IndexTask(false));

2)建立完成后,就可以直接搜索特定的詞語了。搜索語句一般代碼如下:

Searcher searcher = new IndexSearcher((indexDir);  //創(chuàng)建一個(gè)搜索器

//使用和索引同樣的語言分析器

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

//搜索結(jié)果使用Hits存儲(chǔ)

Hits hits = searcher.search(query);

//通過hits得到相應(yīng)字段的數(shù)據(jù)和查詢的匹配度

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

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

};

Jive實(shí)現(xiàn)搜索就復(fù)雜得多,它為搜索專門建立了一個(gè)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接口中主要定義了和搜索相關(guān)的一些參數(shù),可以根據(jù)具體要求定制,直接使用Query就可以達(dá)到搜索的目的,如需要搜索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();

     //輸出結(jié)果

}

追查代碼會(huì)發(fā)現(xiàn),上面forumFactory.createQuery(forums)方法實(shí)際內(nèi)容是new DbQuery(forums, this)。DbQuery作為Query的一個(gè)子類,它的搜索語句通過executeQuery()方法中下列語句實(shí)現(xiàn):

private void executeQuery() {

    try {

       Searcher searcher = getSearcher();  //創(chuàng)建一個(gè)搜索器

       …

       //使用分析器獲得Query對(duì)象

       org.apache.lucene.search.Query bodyQuery =

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

      org.apache.lucene.search.Query subjectQuery =

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

        //將兩個(gè)Query對(duì)象加入BooleanQuery

        BooleanQuery comboQuery = new BooleanQuery();

        comboQuery.add(subjectQuery,false,false);

        comboQuery.add(bodyQuery,false,false);

        //Jive自己的搜索結(jié)果過濾器

        MultiFilter multiFilter = new MultiFilter(3);

        int filterCount = 0;

    

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

             //將其他論壇內(nèi)容搜索結(jié)果過濾掉

            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++;

        }

 

         //日期過濾器  如只查詢某日期以后的內(nèi)容

        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) {//實(shí)現(xiàn)搜索

             hits = searcher.search(comboQuery, multiFilter);

        } else {

             hits = searcher.search(comboQuery);

        }

        //搜索結(jié)果不要超過最大大小

        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的搜索使用了過濾器,以便過濾掉不想出現(xiàn)的結(jié)果,然后還對(duì)搜索結(jié)果進(jìn)行了限制轉(zhuǎn)換,這些在實(shí)際使用中都是必需的。

5.4  Jive的中文問題

Jive默認(rèn)的字符集編碼方式是ISO8859_1,即Latin-1字符集,這是國際標(biāo)準(zhǔn)化組織用來表示Latin等西方語言使用的字符集。

ISO8859_1字符集非常類似常見的ASCII字符集。由于ISO8859_1是使用單字節(jié)來表示,而漢字是采取雙字節(jié)來表示一個(gè)漢字,我國制定了一套專門用來表示漢字GB2312GBK編碼字符集。

Java內(nèi)部運(yùn)算中,涉及到的所有字符串都會(huì)被轉(zhuǎn)化為UTF-8編碼來進(jìn)行運(yùn)算。那么,在被Java轉(zhuǎn)化之前,字符串是什么樣的字符集? Java總是根據(jù)操作系統(tǒng)的默認(rèn)編碼字符集來決定字符串的初始編碼,而且Java系統(tǒng)的輸入和輸出的都是采取操作系統(tǒng)的默認(rèn)編碼。

因此,如果能統(tǒng)一Java系統(tǒng)的輸入、輸出和操作系統(tǒng)3者的編碼字符集合,將能夠使Java系統(tǒng)正確處理和顯示漢字。這是處理Java系統(tǒng)漢字的一個(gè)原則,但是在實(shí)際項(xiàng)目中,能夠正確抓住和控制住Java系統(tǒng)的輸入和輸出部分是比較難的。

Jive是運(yùn)行在Web容器中的一個(gè)Servlet/JSP系統(tǒng)。在這個(gè)系統(tǒng)中,輸入途徑有很多種:一種是通過頁面表單打包成請求(request)發(fā)往服務(wù)器的;第二種是通過數(shù)據(jù)庫讀入;還有第3種輸入比較復(fù)雜,JSP在第一次運(yùn)行時(shí)總是被編譯成Servlet,JSP中常常包含中文字符,那么編譯使用javac時(shí),Java將根據(jù)默認(rèn)的操作系統(tǒng)編碼作為初始編碼。除非特別指定,如在Jbuilder中可以指定默認(rèn)的字符集。

輸出途徑也有幾種:第一種是JSP頁面的輸出。由于JSP頁面已經(jīng)被編譯成Servlet,那么在輸出時(shí),也將根據(jù)操作系統(tǒng)的默認(rèn)編碼來選擇輸出編碼,除非指定輸出編碼方式;還有輸出途徑是數(shù)據(jù)庫,將字符串輸出到數(shù)據(jù)庫。

由此看來,一個(gè)J2EE系統(tǒng)的輸入輸出是非常復(fù)雜,而且是動(dòng)態(tài)變化的,而Java是跨平臺(tái)運(yùn)行的,在實(shí)際編譯和運(yùn)行中,都可能涉及到不同的操作系統(tǒng),如果任由Java自由根據(jù)操作系統(tǒng)來決定輸入輸出的編碼字符集,這將不可控制地出現(xiàn)亂碼。

正是由于Java的跨平臺(tái)特性,使得字符集問題必須由具體系統(tǒng)來統(tǒng)一解決,所以在一個(gè)Java應(yīng)用系統(tǒng)中,解決中文亂碼的根本辦法是明確指定整個(gè)應(yīng)用系統(tǒng)統(tǒng)一字符集。

Jive中如果指定默認(rèn)字符集為某個(gè)字符集,那么就要在所有的輸入輸出環(huán)節(jié)都要標(biāo)識(shí)為這個(gè)字符集。但是,前面已經(jīng)提到,要完全在編碼時(shí)做到還是有一定難度,必須對(duì)Web程序有相當(dāng)?shù)卣莆蘸屠斫?,而且步驟較繁瑣。

有一種相對(duì)省事的做法,例如統(tǒng)一指定為ISO8859_1,因?yàn)槟壳按蠖鄶?shù)軟件都是西方人編制的,他們默認(rèn)的字符集就是ISO8859_1,包括操作系統(tǒng)Linux和數(shù)據(jù)庫MySQL等。這樣,如果指定Jive統(tǒng)一編碼為ISO8859_1,那么就有下面3個(gè)環(huán)節(jié)必須把握:

·          開發(fā)和編譯代碼時(shí)指定字符集為ISO8859_1。

·          運(yùn)行操作系統(tǒng)的默認(rèn)編碼必須是ISO8859_1,如Linux。

·          JSP頭部聲明:<%@ page contentType="text/html;charset=ISO8859_1" %>。

如果統(tǒng)一指定為GBK中文字符集,上述3個(gè)環(huán)節(jié)同樣需要做到,不同的是只能運(yùn)行在默認(rèn)編碼為GBK的操作系統(tǒng),如中文Windows。

所以統(tǒng)一編碼為ISO8859_1GBK雖然帶來編制代碼的方便,但是也破壞了Java跨平臺(tái)運(yùn)行的優(yōu)越性,只在一定范圍內(nèi)行得通。

很多情況下,程序員大都是在中文Windows下開發(fā)調(diào)試Java系統(tǒng),然后直接部署到Linux等系統(tǒng)上真正運(yùn)行。而且其中可能涉及到XML文件讀寫。XML是對(duì)編碼方式要求嚴(yán)格的數(shù)據(jù)存儲(chǔ)體,XML又可以隨著代碼移動(dòng)。因此,在進(jìn)行真正大規(guī)模Java系統(tǒng)開發(fā)運(yùn)行時(shí),上述臨時(shí)簡單的變通方式就沒有效果了。

要從根本上解決Java的中文問題,只要將Java系統(tǒng)的統(tǒng)一編碼定義為UTF-8。UTF-8編碼是一種兼容所有語言的編碼方式,惟一比較麻煩的就是要找到應(yīng)用系統(tǒng)的所有出入口,然后使用UTF-8去“結(jié)扎”它。

Jive默認(rèn)的字符集編碼方式是ISO8859_1,如果都統(tǒng)一為UTF-8,那么也需要做下列幾步工作:

·          開發(fā)和編譯代碼時(shí)指定字符集為UTF-8。

·          使用過濾器,將所有請求(request)轉(zhuǎn)換為UTF-8;針對(duì)不同應(yīng)用過濾器有兩種。

·          如果所有請求都經(jīng)過一個(gè)Servlet控制分配器,那么使用Servletfilter執(zhí)行語句。

·          request.setCharacterEncoding("UTF-8")。

·          如果不經(jīng)過Servlet,而直接是JSP,那么每個(gè)JSP頭部設(shè)置上述語句。

·          JSP頭部聲明:<%@ page contentType="text/html;charset= UTF-8" %>。

·          設(shè)定數(shù)據(jù)庫連接方式是UTF-8。

以上討論了Jive以及通用Java的中文問題。如果整個(gè)應(yīng)用系統(tǒng)是從開始進(jìn)行開發(fā),那么統(tǒng)一指定編碼為UTF-8就非常容易做到。如果是在英文源代碼基礎(chǔ)上二次開發(fā),那么首先要將原來的源代碼轉(zhuǎn)換為統(tǒng)一編碼UTF-8,那么這種轉(zhuǎn)換工作會(huì)帶來一定的麻煩。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多