再谈compass:集成站内搜索 博客分类: Compass Lucene SpringquartzJPABeanlucene
程序员文章站
2024-03-08 13:41:04
...
前段时间已经写了一篇关于compass的文章,相信大家对compass也已经有了一定的了解
由于最近做的项目中涉及到了站内搜索,而且是基于JPA注解形式的,在网上找了好久,关于JPA集成compass的例子很少,有些也是基于 xml的,基于注解形式的甚是少,没有办法只有去compass的官网下载英文文档自己研究一下,花费了一下午时间调试出来,集成到项目中!
在这里给大家分享下,希望大家可以少走些弯路!
1.去官方网站下载compass的jar包,我用的的2.1版本
http://www.compass-project.org/
ProductInfo.java
ProductType.java
ProductStyle.java
这里要特别注意有集合类型要搜索或显示的时候,两边定义的@SearchableReference或 @SearchableComponent必须一致
2.再spring的配置文件中加入以下代码
3.自动建立索引的java bean
4.建立索引Service 层代码
控制层
junit测试
由于最近做的项目中涉及到了站内搜索,而且是基于JPA注解形式的,在网上找了好久,关于JPA集成compass的例子很少,有些也是基于 xml的,基于注解形式的甚是少,没有办法只有去compass的官网下载英文文档自己研究一下,花费了一下午时间调试出来,集成到项目中!
在这里给大家分享下,希望大家可以少走些弯路!
1.去官方网站下载compass的jar包,我用的的2.1版本
http://www.compass-project.org/
ProductInfo.java
@Entity @Searchable public class ProductInfo implements Serializable{ private static final long serialVersionUID = -8860864584425256200L; private Integer id; /** 货号 **/ private String code; /** 产品名称 **/ private String name; /** 产品类型 **/ private ProductType type; /** 产品样式 **/ private Set<ProductStyle> styles = new HashSet<ProductStyle>(); public ProductInfo() {} @OneToMany(cascade={CascadeType.REMOVE,CascadeType.PERSIST}, mappedBy="product",fetch=FetchType.EAGER) @OrderBy("visible desc, id asc") @SearchableReference public Set<ProductStyle> getStyles() { return styles; } public void setStyles(Set<ProductStyle> styles) { this.styles = styles; } @Id @GeneratedValue @SearchableId public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(length=30) @SearchableProperty(index = Index.TOKENIZED, store = Store.YES) public String getCode() { return code; } public void setCode(String code) { this.code = code; } @Column(length=50,nullable=false) @SearchableProperty(index = Index.TOKENIZED, store = Store.YES) public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToOne(cascade=CascadeType.REFRESH,optional=false) @JoinColumn(name="typeid") @SearchableReference public ProductType getType() { return type; } public void setType(ProductType type) { this.type = type; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ProductInfo other = (ProductInfo) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
ProductType.java
@Entity @Searchable public class ProductType implements Serializable{ private static final long serialVersionUID = 8106351120886053881L; /** 类别id **/ private Integer typeid; /** 类别名称 **/ private String name; /** 子类别 **/ private Set<ProductType> childtypes = new HashSet<ProductType>(); /** 所属父类 **/ private ProductType parent; private Set<ProductInfo> products = new HashSet<ProductInfo>(); @OneToMany(mappedBy="type", cascade=CascadeType.REMOVE) @SearchableReference public Set<ProductInfo> getProducts() { return products; } public void setProducts(Set<ProductInfo> products) { this.products = products; } public ProductType() {} @ManyToOne(cascade=CascadeType.REFRESH) @JoinColumn(name="parentid") public ProductType getParent() { return parent; } public void setParent(ProductType parent) { this.parent = parent; } @OneToMany(cascade={CascadeType.REFRESH,CascadeType.REMOVE},mappedBy="parent") public Set<ProductType> getChildtypes() { return childtypes; } public void setChildtypes(Set<ProductType> childtypes) { this.childtypes = childtypes; } @Column(length=36,nullable=false) public String getName() { return name; } public void setName(String name) { this.name = name; } @Id @GeneratedValue(strategy=GenerationType.AUTO) @SearchableId public Integer getTypeid() { return typeid; } public void setTypeid(Integer typeid) { this.typeid = typeid; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((typeid == null) ? 0 : typeid.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ProductType other = (ProductType) obj; if (typeid == null) { if (other.typeid != null) return false; } else if (!typeid.equals(other.typeid)) return false; return true; } }
ProductStyle.java
@Entity @Searchable public class ProductStyle implements Serializable{ private static final long serialVersionUID = -4926119953511144279L; private Integer id; /** 样式的名称 **/ private String name; /** 图片 **/ private String imagename; private String image140FullPath; /** 是否可见 **/ private Boolean visible = true; private ProductInfo product; public ProductStyle() {} public ProductStyle(Integer id) { this.id = id; } public ProductStyle(String name, String imagename) { this.name = name; this.imagename = imagename; } @ManyToOne(cascade=CascadeType.REFRESH,optional=false) @JoinColumn(name="productid") @SearchableReference public ProductInfo getProduct() { return product; } public void setProduct(ProductInfo product) { this.product = product; } @Id @GeneratedValue @SearchableId public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(length=30,nullable=false) public String getName() { return name; } public void setName(String name) { this.name = name; } @Column(length=40,nullable=false) @SearchableProperty(index = Index.UN_TOKENIZED, store = Store.YES) public String getImagename() { return imagename; } public void setImagename(String imagename) { this.imagename = imagename; } @Column(nullable=false) public Boolean getVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } @Transient public String getImageFullPath(){ return "/images/product/"+ this.getProduct().getType().getTypeid()+ "/"+ this.getProduct().getId()+ "/prototype/"+ this.imagename; } @Transient public String getImage140FullPath(){ image140FullPath = "/images/product/"+ this.getProduct().getType().getTypeid()+ "/"+ this.getProduct().getId()+ "/140x/"+ this.imagename; return image140FullPath; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ProductStyle other = (ProductStyle) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } }
这里要特别注意有集合类型要搜索或显示的时候,两边定义的@SearchableReference或 @SearchableComponent必须一致
2.再spring的配置文件中加入以下代码
<bean id="annotationConfiguration" class="org.compass.annotations.config.CompassAnnotationsConfiguration"> </bean> <!-- compass Bean --> <bean id="compass" class="org.compass.spring.LocalCompassBean"> <property name="compassConfiguration" ref="annotationConfiguration" /> <property name="transactionManager" ref="txManager" /> <property name="compassSettings"> <props> <!-- 定义索引的存储位置 --> <prop key="compass.engine.connection">d:/compass</prop> <prop key="compass.transaction.factory"> org.compass.spring.transaction.SpringSyncTransactionFactory </prop> <!-- 定义分词器--> <prop key="compass.engine.analyzer.MMAnalyzer.CustomAnalyzer"> org.mira.lucene.analysis.IK_CAnalyzer </prop> </props> </property> <property name="resourceDirectoryLocations"> <list> <value>classpath:net/shopin/bean/product</value> </list> </property> </bean> <bean id="jpaGpsDevice" class="org.compass.gps.device.jpa.JpaGpsDevice"> <property name="name"> <value>JpaGpsDevice</value> </property> <property name="entityManagerFactory" ref="entityManagerFactory" /> <property name="mirrorDataChanges"> <value>true</value> </property> </bean> <!-- 数据库中的数据变化后同步更新索引 --> <bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop"> <property name="compass" ref="compass" /> <property name="gpsDevices"> <list> <bean class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper"> <property name="gpsDevice" ref="jpaGpsDevice" /> </bean> </list> </property> </bean> <bean id="compassTemplate" class="org.compass.core.CompassTemplate"> <property name="compass" ref="compass" /> </bean> <!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 --> <bean id="compassIndexBuilder" class="net.shopin.service.search.impl.CompassIndexBuilder" lazy-init="false"> <property name="compassGps" ref="compassGps" /> <property name="buildIndex" value="true" /> <property name="lazyTime" value="5" /> </bean>
3.自动建立索引的java bean
/** * 通过quartz定时调度定时重建索引或自动随Spring ApplicationContext启动而重建索引的Builder. * 会启动后延时数秒新开线程调用compassGps.index()函数. * 默认会在Web应用每次启动时重建索引,可以设置buildIndex属性为false来禁止此功能. * 也可以不用本Builder, 编写手动调用compassGps.index()的代码. * */ public class CompassIndexBuilder implements InitializingBean { // 是否需要建立索引,可被设置为false使本Builder失效. private boolean buildIndex = false; // 索引操作线程延时启动的时间,单位为秒 private int lazyTime = 10; // Compass封装 private CompassGps compassGps; // 索引线程 private Thread indexThread = new Thread() { @Override public void run() { try { Thread.sleep(lazyTime * 1000); System.out.println("begin compass index..."); long beginTime = System.currentTimeMillis(); // 重建索引. // 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引, // 索引完成后再进行覆盖. compassGps.index(); long costTime = System.currentTimeMillis() - beginTime; System.out.println("compss index finished."); System.out.println("costed " + costTime + " milliseconds"); } catch (InterruptedException e) { e.printStackTrace(); } } }; /** * 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程. * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { if (buildIndex) { indexThread.setDaemon(true); indexThread.setName("Compass Indexer"); indexThread.start(); } } public void setBuildIndex(boolean buildIndex) { this.buildIndex = buildIndex; } public void setLazyTime(int lazyTime) { this.lazyTime = lazyTime; } public void setCompassGps(CompassGps compassGps) { this.compassGps = compassGps; } }
4.建立索引Service 层代码
@Service @Transactional public class SearchServiceBean extends DaoSupport implements SearchService { @Resource(name = "compass") Compass compass; /** 创建索引 **/ public void index(ProductInfo p) { CompassSession session = compass.openSession(); CompassTransaction tx = null; try { tx = session.beginTransaction(); session.create(p); tx.commit(); } catch (Exception e) { if (tx != null) { tx.commit(); } throw new RuntimeException(e); } finally { if (session != null) { session.close(); } } } /** 删除一条索引 **/ public void delete(ProductInfo p) { CompassTemplate ct = new CompassTemplate(compass); ct.delete(p); } /** 更新(重新创建)一条索引 **/ public void update(final ProductInfo p) { CompassTemplate ct = new CompassTemplate(compass); CompassCallback<Object> action = new CompassCallback<Object>() { public Object doInCompass(CompassSession session) throws CompassException { session.delete(p); session.create(p); return null; } }; ct.execute(action); } /** 索引查询 **/ public List<ProductInfo> find(final String keywords) { CompassTemplate ct = new CompassTemplate(compass); return ct.execute(new CompassCallback<List<ProductInfo>>() { public List<ProductInfo> doInCompass(CompassSession session) throws CompassException { List<ProductInfo> result = new ArrayList<ProductInfo>(); CompassQueryBuilder queryBuilder = session.queryBuilder(); CompassHits hits = null; // session.find(query); /** 在所有字段中查询 **/ CompassQuery allPropertyQuery = queryBuilder.queryString(keywords).toQuery(); hits = allPropertyQuery.hits(); /** 在指定字段中查询 **/ // CompassQuery query = queryBuilder.term("name", keywords); // hits = query.hits(); /** 指定范围查询 **/ // CompassQuery dateRangeQuery = // queryBuilder.between("postTime",startTime, endTime, true); // hits = queryBuilder.bool() // .addMust(allPropertyQuery) // .addMust(dateRangeQuery) // .toQuery() // .hits(); // System.out.println("---------"); for (int i = 0; i < hits.length(); i++) { ProductInfo p = (ProductInfo) hits.data(i); /** 如果进行高亮的属性中没有出现关键字, 则返回null **/ // String ht = hits.highlighter(i).fragment("name"); // if (ht != null) { // p.setName(ht); // } // String hc = hits.highlighter(i).fragment("code"); // if (hc != null) { // p.setCode(hc); // } result.add(p); } return result; } }); }
控制层
@Controller("/search/gosearch") public class SearchAction extends Action { @Resource(name = "searchServiceBean") private SearchService SearchService; @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String keywords=request.getParameter("word").trim(); if(keywords==null||"".equals(keywords)){ return mapping.findForward("noproduct"); } System.out.println("------"+keywords); List<ProductInfo> list = SearchService.find(keywords); request.setAttribute("word", keywords); request.setAttribute("product",list); if(list.isEmpty()){ return mapping.findForward("noproduct"); }else{ return mapping.findForward("list"); } } }
junit测试
public class SearchTest { private static AbstractApplicationContext context; @BeforeClass public static void setUpBeforeClass() throws Exception { try { context = new ClassPathXmlApplicationContext("beans.xml"); } catch (Exception e) { e.printStackTrace(); } } @Test public void testDelete() { SearchService searchService = (SearchService) context .getBean("searchServiceBean"); ProductInfo p = new ProductInfo(2); searchService.delete(p); } @Test public void createIndex(){ SearchService searchService = (SearchService) context .getBean("searchServiceBean"); ProductInfoService productInfoService = (ProductInfoService) context .getBean("productInfoServiceBean"); List<ProductInfo> list=productInfoService.getAllProduct(); for(ProductInfo productInfo:list){ // System.out.println("-------"+productInfo.getName()); searchService.index(productInfo); } } @Test public void testSearch() { SearchService searchService = (SearchService) context .getBean("searchServiceBean"); String query = "手机"; List<ProductInfo> ProductInfos; ProductInfos = searchService.find(query); for (ProductInfo p : ProductInfos) { System.out.println(p.getName()); } System.out.println("------------"); } }
推荐阅读
-
用compass快速给你的网站添加搜索功能<一> 博客分类: Compass Lucene 搜索引擎HibernateSpring影视XML
-
Compass将lucene、Spring、Hibernate三者结合 博客分类: Compass Lucene SpringluceneHibernate搜索引擎XML
-
用compass快速给你的网站添加搜索功能<二> 博客分类: Compass Lucene log4jJ#BeanSpringfreemarker
-
用compass快速给你的网站添加搜索功能<一> 博客分类: Compass Lucene 搜索引擎HibernateSpring影视XML
-
compass 分页查询 博客分类: Compass Lucene XML
-
compass 使用详解 博客分类: Compass Lucene HibernateluceneSpringBean搜索引擎
-
再谈compass:集成站内搜索 博客分类: Compass Lucene SpringquartzJPABeanlucene
-
Spring Compass (Lucene) 全文本搜索排序问题 博客分类: Compass Lucene luceneSpringSQLApacheWeb
-
Compass将lucene、Spring、Hibernate三者结合 博客分类: Compass Lucene SpringluceneHibernate搜索引擎XML
-
用compass快速给你的网站添加搜索功能<二> 博客分类: Compass Lucene log4jJ#BeanSpringfreemarker