`
wangwanbao
  • 浏览: 20587 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

构建基于Hibernate的Compass搜索OSEM的应用

阅读更多

     接触Compass有比较长的一段时间了,以前的应用都是基于XML配置文件的XSEM搜索的,比如一个学生选修多门学科,如“语文”,如果搜索哪些学生选修“语文”学科时,基于配置文件的搜索通常是先搜索出“语文”科目的id,然后再搜索学生中含该id的记录,非常的不方便。最近做项目时发现Compass提供了基于对象方式的搜索,但苦于网上关于这方面的内容介绍的要么太少,要么太浅。Compass的官方手册也介绍的不详尽。于是花费了比较长的时间自已摸索,光一个小问题就困挠了一周才得到结果,呵呵,为了方便同样有这方面需要的朋友,特别整理文档。本文的环境是:compass 2.2,如果朋友们对本文的描述不理解,可以加我的MSN沟通,xunmeng_hot@hotmail.com,希望不致于误人子弟。呵呵。

 

  我的demo:一个学生(Student)有一个HomeTown,每个学生可以选修多门学科。

 

  实体类如下:

    Student:

@Searchable
@SearchAnalyzer(name = "student", type = AnalyzerType.Standard)
public class Student {
	
	private int id;
	
	private String name;
	private HomeTown homeTown;
	private List<Course> courses;
	@SearchableId
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@SearchableProperty(name = "studentName",index=Index.NOT_ANALYZED,managedIdIndex = ManagedIdIndex.NOT_ANALYZED)
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@SearchableComponent(refAlias = "HomeTown")
	public HomeTown getHomeTown() {
		return homeTown;
	}
	public void setHomeTown(HomeTown homeTown) {
		this.homeTown = homeTown;
	}
	@SearchableComponent(refAlias = "Course")
	public List<Course> getCourses() {
		return courses;
	}
	public void setCourses(List<Course> courses) {
		this.courses = courses;
	}
	
}

 

   HomeTown

  

@Searchable
@SearchAnalyzer(name = "homeTown", type = AnalyzerType.Standard)
public class HomeTown {
	private int id;
	private String city;
	@SearchableId
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@SearchableProperty(name = "homeTownName",index=Index.NOT_ANALYZED,managedIdIndex = ManagedIdIndex.NOT_ANALYZED)
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	
}

   

    Course:

   

@Searchable
@SearchAnalyzer(name = "course", type = AnalyzerType.Standard)
public class Course {
	private int id;
	private String name;
	@SearchableId
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	@SearchableProperty(name = "courseName",index=Index.NOT_ANALYZED,managedIdIndex = ManagedIdIndex.NOT_ANALYZED)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

 

 

   同时给出它们的Hibernate配置文件:

  

//Student
<hibernate-mapping default-lazy="false">
	<class name="org.wangwanbao.compass.osem.entity.Student" table="student">
		<id name="id" type="int">
			<column name="id" />
			<generator class="native" />
		</id>
		<property name="name" column="name" type="string" />
		<many-to-one name="homeTown" column="hometown_id" cascade="all"
			class="org.wangwanbao.compass.osem.entity.HomeTown" />
		
		<list name="courses"
			table="courses" cascade="all" lazy="false">
			<key column="stuent_id" not-null="true" />
			<index column="IDX" />
			<one-to-many
				class="org.wangwanbao.compass.osem.entity.Course" />
		</list>
	</class>
</hibernate-mapping>


//HomeTown
<hibernate-mapping default-lazy="false">
	<class name="org.wangwanbao.compass.osem.entity.HomeTown" table="hometown">
		<id name="id" type="int">
			<column name="id" />
			<generator class="native" />
		</id>
		<property name="city" column="city" type="string" />
	</class>
</hibernate-mapping>

//Course
<hibernate-mapping default-lazy="false">
	<class name="org.wangwanbao.compass.osem.entity.Course" table="course">
		<id name="id" type="int">
			<column name="id" />
			<generator class="native" />
		</id>
		<property name="name" column="name" type="string" />
	</class>
</hibernate-mapping>

 

Mysql数据库脚本及测试数据

CREATE TABLE `course` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `stuent_id` int(11) NOT NULL,
  `IDX` int(11) default NULL,
  PRIMARY KEY  (`id`),
  KEY `FKAF42E01BFB3BE844` (`stuent_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

-- 
-- 导出表中的数据 `course`
-- 

INSERT INTO `course` VALUES (3, '语文', 4, 0);
INSERT INTO `course` VALUES (4, '物理', 4, 1);
INSERT INTO `course` VALUES (5, '数学', 5, 0);
INSERT INTO `course` VALUES (6, '化学', 5, 1);

-- --------------------------------------------------------

-- 
-- 表的结构 `hometown`
-- 

CREATE TABLE `hometown` (
  `id` int(11) NOT NULL auto_increment,
  `city` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;

-- 
-- 导出表中的数据 `hometown`
-- 

INSERT INTO `hometown` VALUES (3, '九江');
INSERT INTO `hometown` VALUES (4, '南昌');

-- --------------------------------------------------------

-- 
-- 表的结构 `student`
-- 

CREATE TABLE `student` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `hometown_id` int(11) default NULL,
  PRIMARY KEY  (`id`),
  KEY `FK8FFE823B1AA8EF14` (`hometown_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

-- 
-- 导出表中的数据 `student`
-- 

INSERT INTO `student` VALUES (4, 'wangwanbao', 3);
INSERT INTO `student` VALUES (5, 'xunmeng_hot@hotmail.com', 4);

 

OK了,准备工作都做好了,下面开如Compass之旅。当然,首先需要创建索引。我们需要先构建一个Compass类。

采用工厂方法创建

public class CompassSessionFactory {
	public static Compass getCompass() {
		CompassConfiguration conf = new CompassConfiguration()
				.configure("/student_compass.cfg.xml");
		conf.getSettings().setSetting(CompassEnvironment.Cache.FirstLevel.TYPE,
				NullFirstLevelCache.class.getName());
		conf.getSettings().setBooleanSetting(CompassEnvironment.DEBUG, true);
		
		conf.addClass(Student.class).addClass(HomeTown.class).addClass(
				Course.class);
		return (InternalCompass) conf.buildCompass();
	}

}

 

   大家注意到代码中有一个配置文件,这个配置文件中可以设置索引存放的路径及一些其它参数。文件如下:

<compass-core-config xmlns="http://www.compass-project.org/schema/core-config"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://www.compass-project.org/schema/core-config http://www.compass-project.org/schema/compass-core-config-2.2.xsd">

    <compass name="default">
        <connection>
            <file path="f:/student"/>
        </connection>

        <transaction>
            <processors>
                <readCommitted transLog="ram://" />
            </processors>
        </transaction>

        <cache>
            <firstLevel type="org.compass.core.cache.first.NullFirstLevelCache" />
        </cache>

        <searchEngine useCompoundFile="true">
            <optimizer schedule="false" />
        </searchEngine>
    </compass>

</compass-core-config>

 

配置Spring的工厂Bean

<bean id="compass" class="org.wangwanbao.compass.osem.compass.CompassSessionFactory"  factory-method="getCompass"/>

创建索引需要CompassGps,这里配置一下:

	<bean id="hibernateGpsDevice"
		class="org.compass.gps.device.hibernate.HibernateGpsDevice">
		<property name="name">
			<value>hibernateDevice</value>
		</property>
		<property name="sessionFactory">
			<ref bean="mySessionFactory" />
		</property>
		<property name="nativeExtractor">
			<bean
				class="org.compass.spring.device.hibernate.SpringNativeHibernateExtractor" />
		</property>
	</bean>	

<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps"
		init-method="start" destroy-method="stop">
		<property name="compass">
			<ref bean="compass" />
		</property>
		<property name="gpsDevices">
			<list>
				<bean
					class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">
					<property name="gpsDevice" ref="hibernateGpsDevice" />
				</bean>
			</list>
		</property>
	</bean>

 

 Ok了,现在可以创建索引文件了。

	public void index() {
		StopWatch sw = new StopWatch();
		sw.start();
		compassGps.index();
		sw.stop();
		log.info("索引建立完毕,共花费时间:" + sw.getTime() / 1000 + "秒");
	}

 

创建索引后进入我们的主要任务吧,构建一个搜索器,首先我们创建一个Request类,它是基于泛型的。代码:

public class SearchRequest {
	Class entityClass;
	String alias;
	List<SearchField> fields;

	public class SearchField {
		String fieldName;
		String fieldValue;
		Occur fieldType;
		public String getFieldName() {
			return fieldName;
		}

		public void setFieldName(String fieldName) {
			this.fieldName = fieldName;
		}

		public String getFieldValue() {
			return fieldValue;
		}

		public void setFieldValue(String fieldValue) {
			this.fieldValue = fieldValue;
		}

		public Occur getFieldType() {
			return fieldType;
		}

		public void setFieldType(Occur fieldType) {
			this.fieldType = fieldType;
		}

		

	}

	public Class getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class entityClass) {
		this.entityClass = entityClass;
	}

	public String getAlias() {
		return alias;
	}

	public void setAlias(String alias) {
		this.alias = alias;
	}

	public List<SearchField> getFields() {
		return fields;
	}

	public void setFields(List<SearchField> fields) {
		this.fields = fields;
	}
}

 

主要搜索类的代码:

 

public <T> List<T> searchByOSEM(SearchRequest sr){
		CompassSession session = getCompass().openSession();
		CompassTransaction tr = session.beginTransaction();
		CompassQueryBuilder builder = session.queryBuilder();

		CompassQueryBuilder.CompassBooleanQueryBuilder booleanQuery = builder
				.bool();
		List<SearchRequest.SearchField> searchFields = sr.getFields();
		if(searchFields.size() <1)
			return null;
		for(SearchRequest.SearchField sf : searchFields){
			if(Occur.MUST.equals(sf.getFieldType())){
				booleanQuery.addMust(builder.term(sf.getFieldName(),sf.getFieldValue()));
			}else if(Occur.SHOULD.equals(sf.getFieldType())){
				booleanQuery.addShould(builder.term(sf.getFieldName(),sf.getFieldValue()));
			}else if(Occur.MUST_NOT.equals(sf.getFieldType())){
				booleanQuery.addMustNot(builder.term(sf.getFieldName(),sf.getFieldValue()));
			}
		}
		CompassQuery compassQuery = booleanQuery.toQuery()
		.setAliases(sr.getAlias());
		CompassHits hits = compassQuery.hits();

		CompassHit[] detachedHits = new CompassHit[0];
		detachedHits = hits.detach().getHits();
		return termSearchResults(sr.getEntityClass(), detachedHits);
	} 

 

最后我们来创建一个测试代码来,来看看是否能达到效果:

public void testSearch() {
		
		CompassDaoImpl cd = (CompassDaoImpl) factory.getBean("compassDAO");
		List<SearchRequest.SearchField> searchFields = new ArrayList<SearchRequest.SearchField>();
		SearchRequest.SearchField s1 = new SearchRequest().new SearchField();
		s1.setFieldName("Student.name");
		s1.setFieldType(Occur.MUST);
		s1.setFieldValue("wangwanbao");
		
		SearchRequest.SearchField s2 = new SearchRequest().new SearchField();
		s2.setFieldName("Student.courses.name");
		s2.setFieldType(Occur.MUST);
		s2.setFieldValue("语文");
		
		searchFields.add(s1);
		searchFields.add(s2);
		SearchRequest sr = new SearchRequest();
		sr.setAlias("Student");
		sr.setEntityClass(Student.class);
		sr.setFields(searchFields);
		
		List<Student> students = cd.searchByOSEM(sr);
		if(students.size() <1){
			System.out.println("长度为0");
		}else{
			for(Student s : students){
				System.out.println(s.getName() + "---" + s.getHomeTown().getCity());
			}
		}
	}

 

控制台最后输出:wangwanbao---九江,测试成功!

 

应网友要求,给出例子的源代码。

分享到:
评论
9 楼 kwj 2011-08-24  
好东西....
8 楼 SARA520 2010-05-03  
麻烦给发给源代码吧,这样怎么看的懂啊!
只给出方法,都不知道方法在哪个类里啊。。。
7 楼 SARA520 2010-05-03  
不是给出源代码么???
源代码呢???
希望可以发我一份啊,谢谢
601443818@qq.com
6 楼 xwlcool 2009-12-23  
楼主,请发我一份源代码,急需!邮箱是:xwlcool@yahoo.cn
5 楼 wangwanbao 2009-09-25  
songzi0206 写道
请教楼主,用compass做并发搜索的时候,性能如何?

你用Luke去看一下Compass建立的索引,就会发现,Compass其实是用空间来换取时间的。它将一条记录及相关联的信息都记录到一个Document中去。
拿上述的例子来说,它可能会建立一条Document内容如下:
name:wangwanbao
course:语文
hometown:九江
简单来说,它是用一条记录维护数据库中的关系。另外,它是建立在lucene基础上的,而lucene是倒排索引机制的。所以说,用Comass和用lucene是一个概念。。
4 楼 songzi0206 2009-09-25  
请教楼主,用compass做并发搜索的时候,性能如何?
3 楼 wangwanbao 2009-07-07  
xuan 写道
伤心啊。。。
我花了一个星期查了N多资料、都被老板催死了
为什么今天才看到这个blog
楼主好人哈。。。


哈哈,用的到就好!有朋友这么一句表扬,心里挺受用的。谢谢!
2 楼 xuan 2009-07-07  
伤心啊。。。
我花了一个星期查了N多资料、都被老板催死了
为什么今天才看到这个blog
楼主好人哈。。。
1 楼 单色光- 2009-05-31  
踏破铁鞋无觅处,得来全不费工夫
精辟的例子,热情的博主
谢谢

相关推荐

    COMPASS+spring构建自己的搜索引擎.pdf

    总的来说,构建基于Compass和Spring的搜索引擎涉及理解Compass的核心概念,如OSEM、配置方式和API,以及如何利用Spring的集成优势简化开发流程。通过这种方式,开发者可以快速、高效地实现一个功能完备的全文搜索...

    基于Luncene的compass框架详解-java

    因此,Compass是基于Lucene之上构建的,但其功能更为丰富和高级。一个形象的比喻是,Compass对于Lucene,就像Hibernate对于JDBC一样,它完全参照Hibernate的开发模式,极大地提升了搜索框架的易用性和功能性。 3. *...

    基于JavaLuncene的compass框架说明使用技术文档.doc

    Compass 是一个基于 Java 的搜索引擎框架,它充分利用了 Apache Lucene 的强大功能,并与流行的框架如 Hibernate 和 Spring 集成,使得在 Java 应用中集成全文搜索变得简单高效。该框架的主要目的是减少开发者的编码...

    Compass原理深入学习笔记

    1. Compass Core:基础模块,提供事务管理和搜索引擎映射功能,如对象-搜索引擎映射(OSEM)。 2. Compass GPS:用于与不同数据源集成,如ORM框架,支持JDBC等。 3. Compass Spring:整合Spring框架,便于在Spring...

    基于Java的Luncene的compass框架说明使用技术文档.pdf

    Compass依赖于顶级的Lucene搜索引擎,并与诸如Hibernate和Spring等流行框架相结合,为应用程序提供了从数据模型和数据源同步变化的搜索能力。此外,它还添加了两方面的特性:事务管理和快速更新优化。 Compass的...

    Compass技术文档

    Compass基于Lucene之上,类似于Hibernate之于JDBC的关系,提供了更高层次的封装,使得开发者能够更容易地集成搜索引擎到Java应用程序中。 - **OSEM**:OSEM (Object Search Engine Mapping) 是Compass提供的一个...

    compass-2.2.0.zip

    2. **对象-搜索引擎映射(OSEM)**:类似于Hibernate的对象-关系映射,Compress实现了对象-搜索引擎映射,允许开发者将Java对象直接映射到搜索索引中。这样,你可以直接操作业务对象,而无需关心底层的索引结构。 3...

    compass-reference.pdf

    Compass是为Java开发人员设计的一个高性能、高灵活性的搜索引擎库,旨在简化在应用中集成全文搜索功能的过程。它基于Lucene构建,提供了丰富的API和高度可定制性,允许开发者以最小的努力实现复杂的数据索引和查询。...

    compass内部分享

    **Compass**是一个功能强大、高性能的对象/搜索引擎映射(OSEM)框架,它基于Java语言开发,提供了一个方便的方式来管理和查询索引数据。Compass的主要特点包括: - **搜索引擎抽象层**:提供了一套统一的接口来...

    JAVA上百实例源码以及开源项目源代码

    Java 3DMenu 界面源码 5个目标文件 内容索引:Java源码,窗体界面,3DMenu Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都...

    java源码包---java 源码 大量 实例

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    java源码包2

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...

    java源码包3

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...

    java源码包4

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码,文件操作,压缩包查看 Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码,文件操作,压缩包查看 Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码...

Global site tag (gtag.js) - Google Analytics