Compass: 在你的应用中集成搜索功能

时间:2022-04-23
本文章向大家介绍Compass: 在你的应用中集成搜索功能,主要内容包括驱动力、Compass简介、Compass核心API、搜索引擎映射、OSEM、XSEM、Compass Gps、总结、关于作者、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

驱动力

在许多应用程序中,用户总会提出搜索和查询领域实例的需求。他们或者希望构建一个进入应用程序的入口或者希望填充表单的机制。非常典型的解决方案是用浏览的方式(把领域的继承关系表现出来,这样用户可以定位和选择一个自己需要的)或者一个检索表单的方式(展现一个多个输入域的表单,用户可以检索他们需要的信息)。

现实中,对于可用性的角度来说,这两种方案都不是最佳的。浏览的方式会在有许多分支的时候变得缓慢而笨重。而且,用户通常精确地知道他们要用到那个应用,然而却不情愿要浏览整个系统来找到他要的应用。检索表单的方式同样被检索条件个数的多少限制住了。这就要在设置足够的检索域还是检索表单的复杂性上作出权衡。

从可用性的角度来说,解决这个问题的答案就是提供一个单一的、Google样式的检索框,用户可以输入任何符合实例字段的内容。他们可以检索和表示符合这些内容的结果。表单中的这个检索框可以自动填充、Google建议模式的输入框,或者是返回表格式结果的正则表达式搜索。不管怎样,这种解决方案的精髓就是UI是简单的,用户可以输入任何他们选择的条件,然后由搜索引擎去做这些复杂的工作。现在唯一的问题时:如何实现这样的搜索机能。

当面对实现传统的多输入域的表单的时候,大部分应用程序都选择了SQL。典型的情况是,检索的字段都与列名相匹配,并且使用SQL的LIKE语句。然而,因为复杂的SQL要去匹配太多的字段,并且很多情况下由于这些字段的文本长度问题,造成实现的性能经常是非常差的。第二个问题是,对检索结果没有排名并且返回的提示并没有反应出与查询的内容有多大相关性,只是简单地返回结果罢了。第三,对检索结果相关联的关键字没有高亮表示。

很快,大家意识到大部分应用程序需要搜索引擎。所有实体的字段可以像只有一个文件那样被索引,并且是正则文本搜索可以匹配的实体。现在非常流行的搜索引擎之一是Luence。Lucene是相当不错的搜索引擎,在很多项目中应用成功。它提供了底层的搜索引擎API,能够使用Lucene数据结构(Document/Field)去索引数据,能供使用查询API或搜索引擎在索引上检索。它已经在多种编程语言上实现了全部功能,包括Java、C#和C++等。

如果我们分析一个典型的Web应用程序,一般都有个一个共通的架构和特点。通常,应用与后端的关系数据库一起工作。这个应用使用领域模型表示这个系统中的实体,并使用ORM框架把领域模型映射到数据库上。一般情况下,使用一个服务层框架去管理事务、协作,有时也包括业务逻辑和Web框架。问题就在于怎么把Lucene集成到这样的应用程序中去。

当你试图去集成Lucene的时候,刚刚把第一个简单的程序跑起来的时候,马上就会遇到一连串的挑战。第一个问题就是索引应用数据。在之前很长一段时间,相当多的样板式代码热衷于把领域模型映射到Lucene数据模型上去。Lucene文档,是Lucene主要的数据结构,它是一个扁平的类似Map的,只包含字符串的数据结构——所以许多无意义的代码热衷于“植入”和“植出”领域模型。另外一个问题是缺少对Lucene的事务控制,把领域模型数据存储到数据库和搜索引擎是有问题的。而且还有几个其他的很有名的实践和模式要在Lucene中实现,比如缓存、隐式的搜索、为支持Google样式的搜索而创建聚集的属性和为合适的语义保持可识别的Document对象,等等。

Compass简介

Compass的设计目标是简化企业在集成搜索功能时的花费。Compass是在Lucene之上,使用了设计很好的搜索引擎的抽象。Compass扩展了核心Lucene,增加了事务控制功能和快速更新,也包括在数据库存储索引的功能。当然,它没有去隐藏Lucene的特性——所有Lucene的功能都能通过Compass实现。

Compass核心API

Compass提供给了我们简单的并且熟悉的API。说Compass提供了让人熟悉的API是因为它模仿了当前流行的ORM框架的API来降低学习曲线。Compass以下面一些主要的接口作为主要内容:

  • CompassConfiguration:用来在一些设置参数、配置文件和映射定义上配置Compass。通常用来创建Compass接口。
  • Compass:为单线程使用,创建线程安全的实例来打开Compass Seesion。同样还提供了一些搜索引擎索引级别的操作。
  • CompassSesssion:用来执行像保存、删除、查找、装载这样的搜索操作。很轻量但是并不是线程安全的。
  • CompassTransaction:管理Compass事务的接口。使用它并不需要事务管理环境(像Spring、JTA)。

下面是使用这些API的一个简单的例子:

// 在程序中配置和创建Compass
CompassConfiguration conf =
new CompassConfiguration().setConnection("/tmp/index").addClass(Author.class);
Compass compass = conf.buildCompass();

// 一个请求操作
CompassSession session = compass.openSession();
CompassTransaction tx = null;
try {
tx = session.beginTransaction();
...
session.save(author);
CompassHits hits = session.find("jack london");
Author a = (Author) hits.data(0);
Resource r = hits.resource(0);
...
tx.commit();
} catch (CompassException ce) {
if (tx != null) tx.rollback();
} finally {
session.close();
}

为了简化事务管理代码,Compass提供了好几种选择,首先是使用CompassTemplate,它使用流行的设计模式来抽象事务管理。第二个选择是在和事务管理环境下,这样,Compass与JTA与Spring这样的事务管理器集成并在一个已经存在的事务中执行。这个情况下,当一个Session执行的时候,CompassSession 可被用做一个自动加入事务处理的代理。这个代理的创建可以是编程式的,也可使用Spring IOC(Spring 2 中支持@CompassContext)。

Compass支持原子性的事务运算,与不同的事务管理策略集成,包括本地事务管理、JTA同步、XA for JTA的集成,Spring同步的集成。

Compass的配置基于“键—值”的一一对应的设置。Compass可以使用编程式的配置,基于XML DTD的配置(定义映射和设置),基于XML Schema的配置。基于XML Schema的配置得到了Spring2新的基于Schema配置文件的支持。

搜索引擎映射

Compass的主要功能之一就是从应用程序模型到搜索引擎的声明式映射。Compass搜索引擎的领域模型由资源(Lucene Document)和属性(一个Lucene Field)组成。这是用来索引可搜索内容的抽象数据对象。

RSEM

第一个映射是RSEM(Resource/SearchEngine Mapping)。这是一个低级别从Compass资源和属性到搜索引擎抽象到搜索引擎的映射。下面是个对作者资源的RSEM的示例:

<resource alias="author">
<resource-id name="id"/>
<resource-property name="firstName"/>
<resource-property name="lastName" store="yes" index="tokenized"/>
<resource-property name="birthdate" converter="mydate"/>
</resource>

上面的例子中,我们定义了一个映射了作者别名的资源。这个资源的映射包括标识资源的ID和几个附加的属性。定义属性是可选的,尽管他们允许声明式的控制不同属性的特征,包括和一个转换器关联。下面的示例代码填充了一个资源并索引它。

Resource r = session.createResource("author");
r.addProperty("id", "1")
.addProperty("firstName", "jack")
.addProperty("lastName", "london")
.addProperty("birthdate", new Date());
session.save(r);

上面的代码显示了一些Compass的特性。第一,由于一个资源是可识别的,Compass在这个资源已经存在的情况下更新它。第二,可以声明式的分配一个转换器给这个资源,可以使用Compass内置的许多转换器。下面是上面示例代码的Compass配置(包括对mydate转换器的配置):

<compass-core-config xmlns="http://www.opensymphony.com/compass/schema/core-config" 
xsi:schemaLocation="http://www.opensymphony.com/compass/schema/core-config 
http://www.opensymphony.com/compass/schema/compass-core-config.xsd"> 
<compass name="default"> 
<connection> 
<file path="index" /> 
</connection> 
<converters> 
<converter name="mydate" type="org.compass.core.converter.basic.DateConverter"> 
<setting name="format" value="yyyy-MM-dd" /> 
</converter> 
</converters> 
<mappings>
<resource location="sample/author.cpm.xml" />
</mappings>
</compass> 
</compass-core-config>

OSEM

OSEM(Object/Search Engine Mapping)是第二个支持的映射方案。它允许把应用对象的领域模型映射到搜索引擎。下面是Author类,使用注释对它进行了OSEM定义:

@Searchable
public class Author {

@SearchableId
private Long id;

@SearchableComponent
private String Name;

@SearchableReference
private List books;

@SearchableProperty(format = "yyyy-MM-dd")
private Date birthdate;
}

// ...

@Searchable
public class Name {

@SearchableProperty
private String firstName;

@SearchableProperty
private String lastName;
} 

OSEM支持“植入”和“植出”一个对象的分层结构进入一个资源。当存储一个Author对象,Compass就会“植入”进一个资源,Name类也会“植入”进相同的资源来表示这个作者(由于组件的映射),也包括一个这个作者书籍列表里的每一本书(存储在其他的资源里)的引用。这个最后得到的资源会存储或者索引在搜索引擎中。

Compass提供了非常灵活的机制来把领域模型映射到搜索引擎中。上面的例子只是一个很简单的例子。OSEM允许制定不同的转换器,一个类属性对应多个元数据(从资源到属性的映射)、分析器和所有参与的字段,等等。

下面是author类怎样使用的例子:

// ...
Author author = new Author(1, new Name("jack", "london"), new Date());
session.save(author);
// ...
author = (Author) session.load(Author.class, 1); 

XSEM

最后,Compass支持的搜索引擎映射是XSEM(Xml/Search Engine Mapping)。这种映射允许基于XML映射的定义(用XPath实现),把XML数据结构直接映射到搜索引擎。XSEM的处理同样的通过对资源“植入”和“植出”的处理。Compass提供了一个XML包装对象叫做XmlObject,它定义了不同的实现(dom4j, W3C Document),这些实现允许XPath表达式来求值。如果我们给出下面的XML数据结构:

<xml-fragment>
<author id="1">
<firstName>Jack</firstName>
<lastName>London</lastName>
</author>
</xml-fragment>

下面是个XSEM的实现:

<compass-core-mapping>
<xml-object alias="author" xpath="/xml-fragment/author">
<xml-id name="id" xpath="@id" />
<xml-property xpath="firstName" />
<xml-property xpath="lastName" />
<xml-content name="content" />
</xml-object>
</compass-core-mapping>

从XML数据结构到搜索引擎的映射是使用XPath表达式来完成。XML内容映射可以在搜索引擎中存储为XML结构,这样就可以加载和搜索数据。Compass支持多种XML DOM框架(为XML内容作映射),包括JSE5, dom4j(SAX 和XPP),当然定制的实现也很好做。下面是个不错的例子:

Reader reader = // construct an xml reader over raw xml content
AliasedXmlObject xmlObj = RawAliasedXmlObject("author", reader);
session.save(xmlObj);
// ...
Resource resource = session.loadResource("author", 1);
// since we have xml-content, we can do the following as well
XmlObject xmlObj = session.load("author", 1); 

Compass Gps

Compass Gps 是Compass的一个组件,用来把不同的数据源与Compass集成。大部分常用的数据源是Compass与ORM工具的集成。Compass支持JPA、Hibernate、OJB、JDO和iBatis。

拿Hibernate作为例子,Compass给出了两个主要的操作:索引与镜像。拥有这两个映射的对象可以通过使用Hibernate API注册时间监听,进行自动的镜像操作到搜索引擎。下面的例子给出了怎样使用Compass Gps集成Hibernate:

SessionFactory sessionFactory = // Hibernate Session Factory
Compass compass = // set up a Compass instance
CompassGps gps = new SingleCompassGps(compass);
CompassGpsDevice device = new Hibernate3GpsDevice("hibernate", sessionFactory);
gps.addDevice(device);
// start the gps, mirroring any changes made through Hibernate API
// to be mirrored to the search engine
gps.start();

// ....

// this will cause the database to be indexed
gps.index();
// this will cause Hibernate to store the author in the database
// and also index the author object through Compass
hibernateSess.save(new Author(1, new Name("jack", "london"), new Date()));

总结

这篇文章对Compass的主要功能的做了介绍,但只是覆盖了怎样使用Compass的基本功能(尤其,Compass还有个与Spring集成的扩展组件,这个并没介绍)。在使用搜索引擎的时候,Compass同样也有很多现在流行功能和有一些细微的差别功能,还有配置扩展功能。Compass的主要目标,像刚才提到的,是简化集成搜索到任何类型的应用程序中,这篇文章只是介绍了怎么使用的基本信息。

关于作者

Shay是Compass开源项目的建立者,Compass是唯一集成搜索功能到各种应用模型中的解决方案。他先是专注于实时的C/C++系统,后来转到Java开发(不再回头)。在Java世界中, Shay最近在实现分布式规则引擎服务器的工作。这是一个典型的Java为基础的Web项目,面向金融行业、以消息为基础的项目。现在Shay是GigaSpaces的系统架构师。

查看英文原文:Compass: Integrate Search into your apps