compass是建立在lucene基础之上的全文搜索框架,它提供了多种格式与索引进行映射的引擎:OSEM、XSEM、RSEM,以致不管你是使用JavaBean的形式进行开发、或用XML来存储数据、甚至是最原始的非面象对象方式的开发,你都会发现Compass适合你,为你的应用添加全文搜索功能。
了解Lucene基本概念的同学应该都很清楚,索引的基本单元是Document,而Document又是由Field组成的。用关系数据库的角度思考,Document相当于一条纪录,Field则相当于纪录中的一个字段(与关系数据库不一样的是Field的值可以有多个)。在Compass的叫法是这样的:一个Document在Compass里面叫Resource,Field在Compass里叫Property。下面我们以OSEM(Object/Search Engine Mapppings)为例来观察一下Compass所创建的索引的真实面目。
下面假设读该文章的同学对Compass至少有感性上的认识。如果发觉阅读困难建议先到官方上去下载一份回来瞅一瞅先。
一、认识对象
使用OSEM引擎,相对应的做法是使用对象与索引进行映射产生关系。为了方便,我直接把Compass的Petclinic的模型拿来开刀了。以Owner和Pet两个类为例,先看看cpm文件代码:
- <class name="Owner" alias="${petclinic.owner}" extends="person">
- <property name="firstName">
- <meta-data>${petclinic.firstName}</meta-data>
- </property>
- <property name="lastName">
- <meta-data>${petclinic.lastName}</meta-data>
- </property>
- <property name="address">
- <meta-data>${petclinic.address}</meta-data>
- </property>
- <property name="city">
- <meta-data>${petclinic.city}</meta-data>
- </property>
- <property name="telephone">
- <meta-data>${petclinic.telephone}</meta-data>
- </property>
- <reference name="petsInternal" ref-alias="${petclinic.pet}" />
- </class>
- <class name="Pet" alias="${petclinic.pet}" extends="named-entity">
- <property name="birthDate">
- <meta-data>${petclinic.birthdate}</meta-data>
- </property>
- <component name="type" ref-alias="${petclinic.petType}" />
- <reference name="owner" ref-alias="${petclinic.owner}" />
- <reference name="visitsInternal" ref-alias="${petclinic.visit}" />
- </class>
这里就不贴Java源码了,从映射文件上很容易想像Java源码是怎样的。其中Owner和Pet是一对多的关系。而Pet和Visit也是一对多的关系,Visit在这里就不重复贴了。
看起来和Hibernate的映射文件很相似吧。Property标签就不用解释了,稍解释一下Reference。Reference指引用,上面配置显示的Owner和Pet互相都配了Reference可能引起疑惑。引用大部分使用在这种情景:
- @Searchable
- public class A {
- @SearchableId
- private Long id;
- @SearchableReference
- private B b;
- // …
- }
- @Searchable
- public class B {
- @SearchableId
- private Long id;
- // …
- }
A类持有一个B的成员变量,也叫引用。实际上如果把B改成一个Collection,Compass也会把它当作一个引用处理,它通过检测得知是一般类引用还是集合。实际上Owner和Pet关系的源码是这样的:
- @Searchable
- public class Owner {
- @SearchableReference
- private List petsInternal;
- // …
- }
- @Searchable
- public class Pet {
- @SearchableReference
- private Owner owner;
- }
对于引用的映射,Compass会把引用目标的ID保存起来,在查询的时候通过ID把关联的对象加载进来,类似于关系数据库的外键。
Compass在创建索引的时候并不是把所有的索引放到一起,而是根据所定义的别名(alais)把不同类型的索引文件分开存储。在PetClinic里面,我们可以看到索引被分成了owner,pet,vet,visit四个文件夹。owner文件夹只存放Owner的索引。一个索引文件夹就像关系数据库中的一张表一样。
二、索引内部结构
现在将要深入到具体一个索引库里面去看一下,这些对象在索引的世界里是如何被组起来的。这时需要用到另外一个好朋友Luck。它将带我们深入分析索引的结构。
打开Luck,file/open lucene index 菜单,浏览到Owner文件夹,打开。这时可以看到Owner和Pet下面存储的文档(Document)都有些什么Field:
- $/owner/id
- $/owner/petsInternal/colSize
- $/owner/petsInternal/id
- address
- alias
- all
- city
- extendedAlias
- firstName
- lastName
- telephone
- $/pet/id
- $/pet/owner/id
- $/pet/visitsInternal/colSize
- $/pet/visitsInternal/id
- alias
- all
- birthdate
- extendedAlias
- name
- petType
从中可以找到规律:
一、alias,all,extendsAlias这三个Field是所有Document都有的,它们各有意义,如Alais用来标识该文档的类型。
二、普通属性使用指定的名字为Field命令,并且在默认情况下,对这些Field的原始值进行索引(Index)、分析(Tokenizer)、并存储(Store)。要改变这种行为,可在配置Meta-data时指定。
三、对象的ID对应的Field名字比较特别,格式是$/(alias)/id
四、引用(Reference)的对象非集合时,将其引用对象的ID以关键字(索引、存储但不分析)的形式作为Field人值及以$/(alias)/(referenceName)/id 为名加到文档中。
五、引用的对象是集合时,将在文档中添加两个Field,命名为$/(alias)/(collection)/colSize的Field将保存所引用集合的Size,命名为$/(alias)/(collection)/id的Field将保存引用集合中每个元素的ID,保存格式均为关键字。到这里你可能会问,一个Field如何保存N个元素的ID,别担心,Luence允许你这样做。下面看一个真实的Document就明白了。
第二行显示出ColSize的值为2,说明该Owner拥有两个Pet,而这两个Pet的ID则可以通过下面两行来找到,是3和4。
到目前为止,对Compass的索引存储应该有了一定的了解了。本文只是一个结论和提供探究的方法,有兴趣的同学还是应该自己动手去做一下。