Compass的一个简单例子
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();
}
上面实现这个例子是最最基础的,供参考以入门。