Compass的一个简单例子

来源:互联网 发布:tensorflow中文版 编辑:程序博客网 时间:2024/06/02 04:04

Compass是一个封装了Lucene的框架。使用Compass的API可以非常方便地建立索引、删除索引、实现检索。Compass非常像Hibernate,Compass封装了Lucene就像Hibernate封装了JDBC操作一样,通过简单的API就能实现底层的操作。

但是,初次接触Compass,并不像想象的那样,我想,在以后的不断学习中,会一点点地深入理解Compass的更多内容。

感觉,如果不真正地实现一个Compass的例子,即使是非常简单的例子,我的印象中的Compass还是很模糊的。就拿一个最简单的例子来展示一下Compass到底能够做到什么,有了这样一种直观的印象,才能与Compass拉近距离,便于深入学习。

新建一个Java Project,作为测试的工程。

Compass版本

Compass 1.2

工程结构

实现Compass的例子的工程结构如下所示:

Compass
│ .classpath
│ .project

├─bin
│ └─org
│      └─shirdrn
│          └─compass
│              │ compass.cfg.xml
│              │
│              ├─document
│              │      alias.cmd.xml
│              │      Book.class
│              │      Book.cpm.xml
│              │
│              └─main
│                      MyCompass.class

├─src
│ └─org
│      └─shirdrn
│          └─compass
│              │ compass.cfg.xml
│              │
│              ├─document
│              │      alias.cmd.xml
│              │      Book.cpm.xml
│              │      Book.java
│              │
│              └─main
│                      MyCompass.java

└─target
    └─index
        └─index
            └─book
                    segments.gen
                    segments_5
                    _0.cfs
                    _0_1.del
                    _1.cfs
                    _1_1.del
                    _2.cfs
                    _3.cfs

上面的红色部分(即target所在目录分支)是在运行程序的时候才生成的,在目录/target/index/index/book/下面生成的就是索引文件。这些文件的格式,在Lucene中已经非常熟悉了。

开发过程

需要用到的jar包,通过工程的classpath文件可以看到都用到了哪些jar包,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="E:/JAR包/Compass 1.2/compass.jar"/>
<classpathentry kind="lib" path="E:/JAR包/Others/log4j-1.2.11.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/lucene/lucene-core.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/lucene/lucene-highlighter.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/lucene/lucene-queries.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/lucene/lucene-snowball.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/lucene/lucene-analyzers.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/lib/log4j/log4j-1.2.13.jar"/>
<classpathentry kind="lib" path="E:/Compass/compass-1.2-with-dependencies/compass-1.2/dist/commons-logging.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

看看Compass的配置文件compass.cfg.xml是如何配置的:

<!DOCTYPE compass-core-configuration PUBLIC
    "-//Compass/Compass Core Configuration DTD 1.0//EN"
    "http://www.opensymphony.com/compass/dtd/compass-core-configuration.dtd">
<compass-core-configuration>
<compass>
   <setting name="compass.engine.connection">target/index</setting>
   <meta-data
    resource="org/shirdrn/compass/document/alias.cmd.xml" />
</compass>
</compass-core-configuration>

很像Hibernate的配置文件。

setting元素中配置了索引文件的存放位置,上面为target/index,即在当前工程目录下,生成的索引文件会存放在target/index/index/目录下。

meta-data元素通过指定属性resource的值,即一个包含完整包名的alias(别名)定义文件。alias.cmd.xml文件中包含的内容是一个实体类的相关信息,如下所示:

<?xml version="1.0"?>
<!DOCTYPE compass-core-meta-data PUBLIC
    "-//Compass/Compass Core Meta Data DTD 1.0//EN"
    "http://www.opensymphony.com/compass/dtd/compass-core-meta-data.dtd">
<compass-core-meta-data>
<meta-data-group id="mybooks" displayName="Mybooks Meta Data">

   <description>Book Meta Data</description>
   <uri>http://compass/mybooks</uri>

   <alias id="book" displayName="Book">
    <description>Book alias</description>
    <uri>http://compass/mybooks/Book</uri>
    <name>book</name>
   </alias>
   <meta-data id="bookNo" displayName="BookNo">
    <description>Author alias</description>
    <uri>http://compass/mybooks/bookNo</uri>
    <name>bookNo</name>
   </meta-data>
   <meta-data id="bookName" displayName="BookName">
    <description>BookName alias</description>
    <uri>http://compass/mybooks/bookName</uri>
    <name>bookName</name>
   </meta-data>
   <meta-data id="author" displayName="Author">
    <description>Text alias</description>
    <uri>http://compass/mybooks/author</uri>
    <name>author</name>
   </meta-data>
   <meta-data id="category" displayName="Category">
    <description>Category alias</description>
    <uri>http://compass/mybooks/category</uri>
    <name>category</name>
   </meta-data>
   <meta-data id="contents" displayName="Contents">
    <description>Text alias</description>
    <uri>http://compass/mybooks/contents</uri>
    <name>contents</name>
   </meta-data>
   <meta-data id="price" displayName="Price">
    <description>Price alias</description>
    <uri>http://compass/mybooks/price</uri>
    <name>price</name>
   </meta-data>
</meta-data-group>

</compass-core-meta-data>

该文件中meta-data-group元素的id属性值标识了一个组id,通过它可以设置一个实体类的每个属性的元数据(meta-data),在后面的实体(Book)的映射文件中可以看到。

在Compass中,实体类对应于Lucene中的Document,就像Hibernate中的POJO对应于数据库中的表一样,是这样的一种映射的关系。

为了测试,只建立一个实体类Book类,就是一个JavaBean,Book。Book.java的代码如下所示:

package org.shirdrn.compass.document;

public class Book {

private String bookNo;
private String bookName;
private String author;
private String category;
private String contents;
private Double price;

public String getAuthor() {
   return author;
}
public void setAuthor(String author) {
   this.author = author;
}
public String getCategory() {
   return category;
}
public void setCategory(String category) {
   this.category = category;
}
public String getContents() {
   return contents;
}
public void setContents(String contents) {
   this.contents = contents;
}

public Double getPrice() {
   return price;
}
public void setPrice(Double price) {
   this.price = price;
}
public String getBookNo() {
   return bookNo;
}
public void setBookNo(String bookNo) {
   this.bookNo = bookNo;
}
public String getBookName() {
   return bookName;
}
public void setBookName(String bookName) {
   this.bookName = bookName;
}

}

其中,在Book.java中定义了几个属性,这些属性对应于Lucene中的Field。

一个实体类对应着一个映射文件,比如Book类对应的映射文件名称为Book.cpm.xml,配置如下所示:

<?xml version="1.0"?>
<!DOCTYPE compass-core-mapping PUBLIC
    "-//Compass/Compass Core Mapping DTD 1.0//EN"
    "http://www.opensymphony.com/compass/dtd/compass-core-mapping.dtd">
<compass-core-mapping package="org.shirdrn.compass.document">
<class name="Book" alias="${mybooks.book}">
   <id name="bookNo" />
   <property name="bookName">
    <meta-data>${mybooks.bookName}</meta-data>
   </property>
   <property name="author">
    <meta-data>${mybooks.author}</meta-data>
   </property>
   <property name="category">
    <meta-data>${mybooks.category}</meta-data>
   </property>
   <property name="contents">
    <meta-data>${mybooks.contents}</meta-data>
   </property>
   <property name="price">
    <meta-data>${mybooks.price}</meta-data>
   </property>
</class>

</compass-core-mapping>

通过使用alias.cmd.xml文件中的meta-data-group id="mybooks"来设置一个实体的属性,使用${}来设置。

其中,id唯一地标识了一个Book对象,可以对其执行save、delete操作,其实就是在对一个Document进行上述操作。

编写一个测试类MyCompass类,如下所示:

package org.shirdrn.compass.main;

import org.compass.core.Compass;
import org.compass.core.CompassHits;
import org.compass.core.CompassSession;
import org.compass.core.CompassTransaction;
import org.compass.core.config.CompassConfiguration;
import org.shirdrn.compass.document.Book;

public class MyCompass {

private static Compass compass;
static{    // 解析Compass配置文件,加载Book实体类
   CompassConfiguration config = new CompassConfiguration();
   config.configure("/org/shirdrn/compass/compass.cfg.xml");
   config.addClass(Book.class);
   compass = config.buildCompass();
}

private Book addBookA(){ //   构造一个Book实例,实际上是构造了一个Document,并向其中添加Field
   Book book = new Book();
   book.setBookNo("ISBN 987-5-624-16000-8/TP");
   book.setBookName("Compass框架技术");
   book.setAuthor("飞云居士");
   book.setCategory("Java");
   String contents = "Compass是封装了Lucene的一个功能强大的框架。"+
       "Compass简单易学,这使得开发者可以像使用Hibernate一样轻松地开发自己的搜索引擎。"+
       "即使对Lucene并不是很熟悉,使用Compass框架开发使你变得游刃有余。";
   book.setContents(contents);
   book.setPrice(new Double(23.00));
   return book;
}

private Book addBookB(){
   Book book = new Book();
   book.setBookNo("ISBN 388-2-244-16000-9/TP");
   book.setBookName("鬼吹灯");
   book.setAuthor("本物天下霸唱");
   book.setCategory("考古小说类");
   String contents = "几位“摸金校尉”(盗墓贼)通过风水秘术在发掘古墓时的诡异经历。"+
       "主人公胡八一祖传的风水秘书残卷,上山下乡时误入大山深处的辽代古墓,"+
       "参军时在昆仑山大冰川发现的九层妖楼,东北中蒙边境的关东军秘密地下...";
   book.setContents(contents);
   book.setPrice(new Double(58.50));
   return book;
}

private void createIndex(Book book) {    // 建立索引,与Hibernate很相似
   CompassSession session = compass.openSession();
   CompassTransaction tx = session.beginTransaction();
   session.save(book);
   tx.commit();
   session.close();
}

private void search(String keyword) {    // 根据关键字keyword检索
   CompassSession session = compass.openSession();
   CompassTransaction tx = session.beginTransaction();
   CompassHits hits = session.find(keyword);
   System.out.println("检索关键字 "+keyword+" 的检索结果如下:");
   System.out.println("********************************************");
   for (int i = 0; i < hits.getLength(); i++) {
    Book book = (Book)hits.data(i);
    System.out.println("书的编号为: "+book.getBookNo());
    System.out.println("书的名称为: "+book.getBookName());
    System.out.println("书的作者为: "+book.getAuthor());
    System.out.println("书的类别为: "+book.getCategory());
    System.out.println("书的内容为: "+book.getContents());
    System.out.println("书的定价为: "+book.getPrice());
    System.out.println("********************************************");
   }
   System.out.println("共检索出结果 "+hits.length()+" 个。");
   hits.close();
   tx.commit();
   session.close();
   compass.close();
}

public static void main(String[] args) {
   MyCompass myCompass = new MyCompass();
   myCompass.createIndex(myCompass.addBookA());
   myCompass.createIndex(myCompass.addBookB());
   myCompass.search("鬼吹灯");
}
}

通过两个方法:addBookA()和addBookB()构造两个Book实例,要为它们建立索引。

当执行上面测试函数的时候,会在工程目录下生成如下索引目录:

\target\index\index\book\

在索引目录下生成建立的索引文件。

通过Book的bookName来检索,检索关键字“鬼吹灯”,运行结果如下所示:

检索关键字 鬼吹灯 的检索结果如下:
********************************************
书的编号为: ISBN 388-2-244-16000-9/TP
书的名称为: 鬼吹灯
书的作者为: 本物天下霸唱
书的类别为: 考古小说类
书的内容为: 几位“摸金校尉”(盗墓贼)通过风水秘术在发掘古墓时的诡异经历。主人公胡八一祖传的风水秘书残卷,上山下乡时误入大山深处的辽代古墓,参军时在昆仑山大冰川发现的九层妖楼,东北中蒙边境的关东军秘密地下...
书的定价为: 58.5
********************************************
共检索出结果 1 个。

如果想要通过文章中词条作为关键字来检索,也一样,比如检索关键字“的”,因为它在两个Book的内容中都出现了,预测应该能检索出两个Book。

看运行结果,如下所示:

检索关键字 的 的检索结果如下:
********************************************
书的编号为: ISBN 388-2-244-16000-9/TP
书的名称为: 鬼吹灯
书的作者为: 本物天下霸唱
书的类别为: 考古小说类
书的内容为: 几位“摸金校尉”(盗墓贼)通过风水秘术在发掘古墓时的诡异经历。主人公胡八一祖传的风水秘书残卷,上山下乡时误入大山深处的辽代古墓,参军时在昆仑山大冰川发现的九层妖楼,东北中蒙边境的关东军秘密地下...
书的定价为: 58.5
********************************************
书的编号为: ISBN 987-5-624-16000-8/TP
书的名称为: Compass框架技术
书的作者为: 飞云居士
书的类别为: Java
书的内容为: Compass是封装了Lucene的一个功能强大的框架。Compass简单易学,这使得开发者可以像使用Hibernate一样轻松地开发自己的搜索引擎。即使对Lucene并不是很熟悉,使用Compass框架开发使你变得游刃有余。
书的定价为: 23.0
********************************************
共检索出结果 2 个。

修改和删除索引,需要通过一个实体类对象的唯一标识来定位该索引文件,然后对其删除或者修改,比如上面的Book中bookNo就可以标识一个Book对象,通过指定一个bookNo,通过CompassSession的delete方法进行删除。

删除索引的操作也是非常简单的,如下所示:

private void deleteIndex(Book book) { // 删除索引,与Hibernate很相似
   CompassSession session = compass.openSession();
   CompassTransaction tx = session.beginTransaction();
   session.delete(book);
   tx.commit();
   session.close();
}

修改索引与Hibernate稍有不同,实际上是先把原来已经存在的索引删除掉,之后再重新创建索引,如下所示:

private void updateIndex(Book book) { // 修改索引
   CompassSession session = compass.openSession();
   CompassTransaction tx = session.beginTransaction();
   session.delete(book);
   session.save(book);
   tx.commit();
   session.close();
}

上面实现这个例子是最最基础的,供参考以入门。