学习Compass是个很快速的过程,它不像其他框架需要花很多时间学习它的API和了解它的工作流程.似乎Compass就是两个框架的组合版本.
为什么这样说呢?看下它的工作流程就知道了: 这个结构和Hibernate很相像,无非就是Hibernate把JDBC封装了一把.所以从结构上来说,只要我们了解了Hibernate,就已经对Compass有了了解.那么Hibernate需要提供API和配置文件来对JDBC进行操作,那么Compass呢?Compass不仅从结构上模仿了Hibernate,就连API风格也不尽相同.我们把它和Hibernate的API做个对比就知道了:
再把两个session的核心方法比较一下:
所以说,Compass与Hibernate极为相似,Compass总结起来就两句话: Hibernate的风格 Lucene的思想Compass2.2的版本所用的Lucene2.4.1,Lucene2.4与目前的Lucene3.0相比: API的差别很大,代码不能通用 3.0效率更高但是Compass用起来舒服,学习成本不高,所以这里用Compass2.2对索引库进行操作. 所需jar包 Compass配置文件 先从Compass的文档中复制配置模板: 再对其进行详细配置: < compassname ="default" > <!-- 1,连接信息 --> < connection > < filepath ="./indexDir/"/> </ connection > <!-- 2,声明映射信息 --> < mappings > < classname ="cn.domain.Article"/> </ mappings > </ compass > 在Lucene里面,如果将对象的数据存储在索引库里面,需要先把对象转换为Document,这个工作在Compass里面会自动将其转为Document,在Lucene里面还需要指定哪些Field是可以搜索的,并且是否存储在缓存数据里面,那么Compass也要提供相应的配置才行,Compass如何做到呢?它会在主配置文件(compass.cfg.xml)中通过反射得到Class,得到该类上所有的注解信息,这就是映射信息 的作用.用Article作为测试类,属性如下: publicclassArticle { privateInteger id; privateString title; privateString content; 再生成对应的getter、setter方法,我们要指明这个类的数据是可以被搜索到的,需要在类上添加注解: @Searchable publicclassArticle { 这就表明这是一个 可搜索对象,Compass会通过反射创造出该类的实例,所以一定要有 无参的构造函数,并且有删除索引的需求,还有Field的配置等,对可搜索对象的所有要求为: 要有默认的构造方法 在类上要有:@Searchable 要有一个唯一标识符的属性:@SearchableId 其他的属性用:@SearchableProperty第二个条件主要是针对删除和更新的,因为之前对Compass的核心方法和Hibernate的核心方法进行了对比,删除索引是用delete()方法,delete()方法不是接收一个对象,因为你把对象给它,它也不知道对象与与对象之间是以什么为区别的,所以给它一个Class,它通过在主配置文件中的映射信息,反射出该类, 再用你提供的第二个参数,也就是唯一标识符,来找到索引,从而进行删除和更新的操作,这就是@SearchableId的作用 . 在Lucene里面将对象转为Document对象的时候,需要保存的数据用Field对象来封装,封装的时候,要提供是否存储数据以及是否为数据建立索引等配置,这就是@SearchableProperty的作用 .完整的对类的配置为下,以id作为唯一标识符: @Searchable publicclassArticle { // 对于唯一标识符,默认不可以进行搜索,除非明确的指定name属性。 // Compass2.2对于数字是使用toString()策略,对于数字属性应指定format属性。 // 如format="00000",表示数字存为5个字符,如果不足5个,前面用若干个0补齐。 @SearchableId(name ="id", format ="00000000") privateInteger id; // name属性可以不写,默认当前属性(成员变量)的名称。 @SearchableProperty(name ="title", store = Store.YES, index = Index.ANALYZED) privateString title; @SearchableProperty(name ="content", store = Store.YES, index = Index.ANALYZED) privateString content; 注意导包不要导错了,全是 org.compass.annotations包中的.属性配置基本和Lucene里面一样.唯一需要注意的就是对数字的处理.如果在Compass的使用过程中出来问题,或者找不到索引之类的,一定要记得看你的数据类型. 创建索引的方式基本与Lucene相同,毕竟Compass是对Lucene的封装,思想是属于Lucene的.就像Hibernate使用Session需要先得到SessionFactory一样,Compass也需要获得SessionFactory,并且也是由Configuration对象来取得的: publicclassArticleDao { privateCompass compassSessionFactory; publicArticleDao() { CompassConfiguration cfg =newCompassConfiguration().configure(); compassSessionFactory = cfg.configure().buildCompass(); } 在Hibernate中,对于Session,SessionFactory只需要一个就够了,对于一个索引库,全局只有一个Compass对象就可以了: @Test publicvoidcreateIndex(){ //准备好测试数据 Article article =newArticle(); article.setId(1); article.setTitle("baby"); article.setContent("love you"); //获得Session CompassSession session = compassSessionFactory.openSession(); //这个事务是针对索引库的事务 CompassTransaction tx = session.beginTransaction(); session.save(article); tx.commit(); session.close();//一定要记得关闭 } 简单的查询,就像Lucene的查询字符串一样. @Test publicvoidsearchIndex() { //获得Session CompassSession session = compassSessionFactory.openSession(); //这个事务是针对索引库的事务 CompassTransaction tx = session.beginTransaction(); //找到一条结果数 // Article article = session.load(Article.class, 1); // System.out.println(article.getId()); // System.out.println(article.getTitle()); // System.out.println(article.getContent()); String queryString ="love"; CompassHits hits = session.find(queryString); intcount = hits.length(); for(inti = 0; i < count; i++) { Article article = (Article) hits.data(i); System.out.println(article.getId()); System.out.println(article.getTitle()); System.out.println(article.getContent()); } tx.commit(); session.close();//一定要记得关闭 } 这只是一个简单增加索引和查询索引的例子,权当做 Hello World 吧. 每次使用Session都要从SessionFactory中获取,无疑是很麻烦的,不如把SessionFactory维护在工具类里面,全局只有一个,在用一个静态方法得到一个Session. @Test publicvoiddeleteIndex(){ CompassSession session = CompassUtils.getSession(); CompassTransaction tx = session.beginTransaction(); session.delete(Article.class,1); tx.commit(); session.close(); } @Test publicvoidupdateIndex(){ CompassSession session = CompassUtils.getSession(); CompassTransaction tx = session.beginTransaction(); Article article = session.get(Article.class, 1); article.setContent("love love"); session.save(article); tx.commit(); session.close(); } 注意,如果唯一标识符是数字,并且没有在实体类里面对唯一标识符使用format属性,则以上代码获取不到! 默认的分词是 标准分词器 ,这个分词器对中文采取的是 单字分词 ,根本没办法使用.一般做法是用第三方的分词器,常用的有极易分词、庖丁分词、IKAnalyzer等.要使用分词器,需要在Compass的主配置文件里面进行配置,这里采用极易分词: <!-- 3,其他配置 --> < settings > <!-- 配置分词器类型 --> < settingname ="compass.engine.analyzer.default.type" value ="jeasy.analysis.MMAnalyzer" ></ setting > </ settings > 这个分词器对中文支持比较好,采用的是词库分词,准确度较高. 接下来还要配置 高亮器 ,配置高亮器主要有三点:前缀、后缀、高亮显示的摘要.同样是在主配置文件里面进行配置: <!-- 3,其他配置 --> < settings > <!-- 高亮配置:摘要的长度(字符数量,默认为100) --> < settingname ="compass.engine.highlighter.default.fragmenter.simple.size"value ="20" /> <!-- 高亮配置:显示效果的前缀 --> < settingname ="compass.engine.highlighter.default.formatter.simple.pre" value ="<span class='keyword'>"/> <!-- 高亮配置:显示效果的后缀 --> < settingname ="compass.engine.highlighter.default.formatter.simple.post" value ="</span>"/> <!-- 配置分词器类型 --> < settingname ="compass.engine.analyzer.default.type" value ="jeasy.analysis.MMAnalyzer" ></ setting > </ settings > 注意,前缀和后缀中的标签,要转义,不然会和配置文件中的符号产生冲突! @Test publicvoidhighlighterIndex() { CompassSession session = CompassUtils.getSession(); CompassTransaction tx = session.beginTransaction(); // 查询条件 String queryString ="love"; CompassHits hits = session.find(queryString); intcount = hits.length(); for(inti = 0; i < count; i++) { Article article = (Article) hits.data(i); // ------------------------- // 进行高亮操作,一次高亮一个属性值 // 返回高亮后的一段文本摘要,原属性值不会改变 // 如果当前高亮的属性值中没有出现关键字,则返回null. String text = hits.highlighter(i).fragment("content"); if(text != null){ // 使用的高亮后的文本替原文本 article.setContent(text); } System.out.println(article.getId()); System.out.println(article.getTitle()); System.out.println(article.getContent()); } tx.commit(); session.close(); } 一般来说搜索结果的排序和Lucene里面是一样的,按照 相关度得分 进行排序,在实际应用中,往往需要人工操纵得分,比如给了推广费之类的,既然有这种需求,自然就有这种做法,和Lucene的不同之处在于,Lucene在添加索引时 控制相关度得分的比重,Compass也是在添加索引时控制,不过需要在实体类里面增加一个属性和它的getter、setter方法,这个属性决定了所有这个类的数据的相关度得分的比重,也就是说可以让只要是这个实体类的数据,就能比别的类的数据得分要高,并且只要是设置了这个属性,就能在添加索引时控制相关度得分的比重: @SearchableBoostProperty privatefloatboostValue = 1F; 在实体里面增加这个属性,属性名无所谓,类型是 float ,最重要的是要有 getter、setter方法和@SearchableBoostProperty,必须要有这处注解,Lucene才能在你调用相关方法的时候,通过反射获得有这个注解的属性名,在调用它的getter方法,得到值,那么在程序中就要增加一句代码: //准备好测试数据 Article article =newArticle(); article.setId(3); article.setTitle("baby"); article.setContent("love you"); //设定比重,默认为1L article.setBoostValue(2L); 其他地方都是一样的.除了对相关度得分的排序以外,还可能在搜索时使用某个属性的值进行排序.这也是可以的: @Test publicvoidsortIndex() { CompassSession session = CompassUtils.getSession(); CompassTransaction tx = session.beginTransaction(); // 查询条件 String queryString ="love"; //CompassHits hits = session.find(queryString); CompassQuery query = session.queryBuilder().queryString(queryString).toQuery(); query.addSort("id"); CompassHits hits = query.hits(); intcount = hits.length(); for(inti = 0; i < count; i++) { Article article = (Article) hits.data(i); System.out.println(article.getId()); System.out.println(article.getTitle()); System.out.println(article.getContent()); } tx.commit(); session.close(); } 只需要在得到搜索结果之前做一点改动.先得到CompassQuery对象,调用这个对象的addSort()方法增加排序规则.像上述一样,则是按升序进行排序,也可以替换成下面这句使用降序排列: query.addSort("id",SortDirection.REVERSE); 注意,指定参与排序的字段时,参与排序的字段必须要是Index.NOT_ANALYZED,否则分词后无法找到! 有时候也会使用到 过滤器 对搜索结果进行过滤. @Test publicvoidfilterIndex() { CompassSession session = CompassUtils.getSession(); CompassTransaction tx = session.beginTransaction(); //查询条件 String queryString ="love"; //得到CompassQuery对象,准备查询 CompassQuery query = session.queryBuilder().queryString(queryString).toQuery(); /** * 增加过滤条件,between第三、四个参数分别代表是否包含下边界和上边界 */ CompassQueryFilter filter = session.queryFilterBuilder().between("id", 2, 3, true, true); //添加过滤器 query.setFilter(filter); CompassHits hits = query.hits(); intcount = hits.length(); for(inti = 0; i < count; i++) { Article article = (Article) hits.data(i); System.out.println(article.getId()); System.out.println(article.getTitle()); System.out.println(article.getContent()); } tx.commit(); session.close(); } 但是使用过滤器是 效率很低 的做法,使用查询字符串可以获得更高的效率,而且可以达到同样的效果.下面就对 各种查询进行简要说明: @Test publicvoidsearch()throwsException { CompassSession session = CompassUtils.openSession(); CompassTransaction tx = session.beginTransaction(); // 查询方式一:使用查询字符串(可以有查询语法) // CompassHits hits = session.find(queryString); // ----------------------------------------------------------------- // 查询方式二:构建CompassQuery对象 // 1,查询所有 CompassQuery query1 = session.queryBuilder().matchAll(); // 2,关键词查询 CompassQuery query2 = session.queryBuilder().term("title","lucene"); // 3,范围查询 CompassQuery query3 = session.queryBuilder().between("id", 5, 15, true); // 4,通配符查询 CompassQuery query4 = session.queryBuilder().wildcard("title","lu*n?"); // 5,短语查询 // 设定精确间隔的匹配方式,写法1 CompassMultiPhraseQueryBuilder qb = session.queryBuilder().multiPhrase("title"); qb.add("lucene", 0);// 第一个词位置从0开始 qb.add("工作", 2);// 第二个词位置从2开始 CompassQuery query5 = qb.toQuery(); // 设定精确间隔的匹配方式,写法2 CompassQuery query6 = session.queryBuilder().multiPhrase("title")// .add("lucene", 0)// 第一个词位置从0开始 .add("工作", 2)// 第二个词位置从2开始 .toQuery(); // 设定最多间隔的匹配方式 CompassQuery query7 = session.queryBuilder().multiPhrase("title")// .add("lucene")// .add("工作")// .setSlop(5)// 指定的词之间的最长间隔不超过5个词 .toQuery(); // 6,布尔查询 CompassQuery query = session.queryBuilder().bool()// // .addMust(query) // .addMustNot(query) // .addShould(query) .addMust(query1)// .addMustNot(query3)// .toQuery(); // ------------------------------- CompassHits hits = query.hits(); // 处理结果 List<Article> list =newArrayList<Article>(); intcount = hits.length();// 总结果数 for(inti = 0; i < hits.length(); i++) { Article article = (Article) hits.data(i); list.add(article); } tx.commit(); session.close(); // ----------------------------------------------------------------- // 显示结果 System.out.println("====== 符合条件的总记录数为:"+ count +" ======"); for(Article a : list) { System.out.println("--------------> id = "+ a.getId()); System.out.println("title = "+ a.getTitle()); System.out.println("content= "+ a.getContent()); } } 基本上还是Lucene的那一套,只是简单的封装一下而已. |
Compass
最新推荐文章于 2021-11-25 09:47:36 发布