程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 再談compass:集成站內搜索

再談compass:集成站內搜索

編輯:關於JAVA

前段時間已經寫了一篇關於compass的文章,相信大家對compass也已經有了一定的了解

由於最近做的項目中涉及到了站內搜索,而且是基於JPA注解形式的,在網上找了好久,關於JPA集成compass的例子很少,有些也是基於 xml的,基於注解形式的甚是少,沒有辦法只有去compass的官網下載英文文檔自己研究一下,花費了一下午時間調試出來,集成到項目中!

在這裡給大家分享下,希望大家可以少走些彎路!

1.去官方網站下載compass的jar包,我用的的2.1版本

http://www.compass-project.org/

ProductInfo.java

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

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

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的配置文件中加入以下代碼

Java代碼

<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

Java代碼

/**
  * 通過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 層代碼

Java代碼

@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;
   }
  });
  }

控制層

Java代碼

@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測試

Java代碼

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("------------");
  }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved