封装solrj

  categories:搜索资料  tags:,   author:

Solrj已经是很强大的solr客户端了。它本身就包装了httpCliet,以完全对象的方式对solr进行交互。很小很好很强大。
不过在实际使用中,设置SolrQuery 的过程中,为了设置多个搜索条件和排序规则等等参数,我们往往会陷入并接字符串的地步,实在是很丑陋,不符合面向对象的思想。扩展性几乎为0,。基于这点,开发了一个小东西,我们只需要设置搜索对象,将对象扔给后台就可以了。
比如,我们搭建的solr服务支持某10个字段的搜索,我们要搜索其中的一些,那么我们只需要传入要搜索的对象POJO,将要搜索的字段内容,set到POJO对象对应额字段即可。

比如如下一个类:

Java代码
  1. package org.uppower.tnt.biz.core.manager.blog.dataobject;
  2. /**
  3. * @author yingmu
  4. * @version 2010-7-20 下午01:00:55
  5. */
  6. public class SolrPropertyDO {
  7. private String auction_id;
  8. private String opt_tag;
  9. private String exp_tag;
  10. private String title;
  11. private String desc;
  12. private String brand;
  13. private String category;
  14. private String price;
  15. private String add_prov;
  16. private String add_city;
  17. private String quality;
  18. private String flag;
  19. private String sales;
  20. private String sellerrate;
  21. private String selleruid;
  22. private String ipv15;
  23. public String getAuction_id() {
  24. return auction_id;
  25. }
  26. public void setAuction_id(String auctionId) {
  27. auction_id = auctionId;
  28. }
  29. ……
  30. public String getExp_tag() {
  31. return exp_tag;
  32. }
  33. public void setExp_tag(String expTag) {
  34. exp_tag = expTag;
  35. }
  36. }

那么我们在定义搜索对象时候,就按照如下设置:

Java代码
  1. SolrPropertyDO propertyDO = new SolrPropertyDO();
  2. propertyDO.setAdd_city(“(杭州AND成都)OR北京”);
  3. propertyDO.setTitle(“丝绸OR剪刀”);
  4. ……

设置排序条件,也是类似的做法:

Java代码
  1. SolrPropertyDO compositorDO = new SolrPropertyDO();
  2. compositorDO.setPrice (“desc”);
  3. compositorDO.setQuality (“asc”);
  4. ……

将定义好的两个对象扔给后面的接口就可以了。

接口函数querySolrResult传入四个参数,其中包含搜索字段对象,排序条件对象。为了提供类似limit的操作,用于分页查询,提供了startIndex和pageSize。
函数querySolrResultCount是单纯为了获得搜索条数,配合分页使用。
以下是定义的接口:

Java代码
  1. package org.uppower.tnt.biz.core.manager.blog;
  2. import java.util.List;
  3. import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;
  4. /**
  5. * @author yingmu
  6. * @version 2010-7-20 下午03:51:15
  7. */
  8. public interface SolrjOperator {
  9. /**
  10. * 获得搜索结果
  11. *
  12. * @param propertyDO
  13. * @param compositorDO
  14. * @param startIndex
  15. * @param pageSize
  16. * @return
  17. * @throws Exception
  18. */
  19. public List<Object> querySolrResult(Object propertyDO,
  20. Object compositorDO, Long startIndex, Long pageSize)
  21. throws Exception;
  22. /**
  23. * 获得搜索结果条数
  24. *
  25. * @param propertyDO
  26. * @param compositorDO
  27. * @return
  28. * @throws Exception
  29. */
  30. public Long querySolrResultCount(SolrPropertyDO propertyDO,
  31. Object compositorDO) throws Exception;
  32. }

 

实现逻辑为,首先将传入的两个实体对象,解析为<K,V>结构的Map当中,将解析完成的Map放入solrj实际的搜索对象当中。返回的对 象为solrj的API提供的SolrDocument,其中结果数量为直接返回SolrDocumentList对象的getNumFound()
具体实现类:

Java代码
  1. package org.uppower.tnt.biz.core.manager.blog;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.TreeMap;
  7. import org.apache.solr.common.SolrDocumentList;
  8. import org.uppower.tnt.biz.core.manager.isearch.common.SolrjCommonUtil;
  9. import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;
  10. import org.uppower.tnt.biz.core.manager.isearch.solrj.SolrjQuery;
  11. /**
  12. * @author yingmu
  13. * @version 2010-7-20 下午03:51:15
  14. */
  15. public class DefaultSolrOperator implements SolrjOperator {
  16. private Logger logger = LoggerFactory.getLogger(this.getClass());
  17. private SolrjQuery solrjQuery;
  18. public void setSolrjQuery(SolrjQuery solrjQuery) {
  19. this.solrjQuery = solrjQuery;
  20. }
  21. @Override
  22. public List<Object> querySolrResult(Object propertyDO,
  23. Object compositorDO, Long startIndex, Long pageSize)
  24. throws Exception {
  25. Map<String, String> propertyMap = new TreeMap<String, String>();
  26. //排序有顺序,使用TreeMap
  27. Map<String, String> compositorMap = new TreeMap<String, String>();
  28. try {
  29. propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
  30. compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
  31. } catch (Exception e) {
  32. logger.error(“SolrjCommonUtil.getSearchProperty() is error !”+ e);
  33. }
  34. SolrDocumentList solrDocumentList = solrjQuery.query(propertyMap, compositorMap,
  35. startIndex, pageSize);
  36. List<Object> resultList = new ArrayList<Object>();
  37. for (int i = 0; i < solrDocumentList.size(); i++) {
  38. resultList.add(solrDocumentList.get(i));
  39. }
  40. return resultList;
  41. }
  42. @Override
  43. public Long querySolrResultCount(SolrPropertyDO propertyDO,
  44. Object compositorDO) throws Exception {
  45. Map<String, String> propertyMap = new TreeMap<String, String>();
  46. Map<String, String> compositorMap = new TreeMap<String, String>();
  47. try {
  48. propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
  49. compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
  50. } catch (Exception e) {
  51. logger.error(“SolrjCommonUtil.getSearchProperty() is error !” + e);
  52. }
  53. SolrDocumentList solrDocument = solrjQuery.query(propertyMap, compositorMap,
  54. null, null);
  55. return solrDocument.getNumFound();
  56. }
  57. }

 

其中,对象的解析式利用反射原理,将实体对象中不为空的值,以映射的方式,转化为一个Map,其中排序对象在转化的过程中,使用TreeMap,保证其顺序性。
解析公共类实现如下:

Java代码
  1. package org.uppower.tnt.biz.core.manager.blog.common;
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.util.HashMap;
  6. import java.util.Map;
  7. /**
  8. * @author yingmu
  9. * @version 2010-7-20 下午01:07:15
  10. */
  11. public class SolrjCommonUtil {
  12. public static Map<String, String> getSearchProperty(Object model)
  13. throws NoSuchMethodException, IllegalAccessException,
  14. IllegalArgumentException, InvocationTargetException {
  15. Map<String, String> resultMap = new TreeMap<String, String>();
  16. // 获取实体类的所有属性,返回Field数组
  17. Field[] field = model.getClass().getDeclaredFields();
  18. for (int i = 0; i < field.length; i++) { // 遍历所有属性
  19. String name = field[i].getName(); // 获取属性的名字
  20. // 获取属性的类型
  21. String type = field[i].getGenericType().toString();
  22. if (type.equals(“class java.lang.String”)) { // 如果type是类类型,则前面包含”class “,后面跟类名
  23. Method m = model.getClass().getMethod(
  24. “get” + UpperCaseField(name));
  25. String value = (String) m.invoke(model); // 调用getter方法获取属性值
  26. if (value != null) {
  27. resultMap.put(name, value);
  28. }
  29. }
  30. }
  31. return resultMap;
  32. }
  33. // 转化字段首字母为大写
  34. private static String UpperCaseField(String fieldName) {
  35. fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName
  36. .substring(0, 1).toUpperCase());
  37. return fieldName;
  38. }
  39. }

 

搜索直接调用solr客户端solrj,基本逻辑为循环两个解析之后的TreeMap,设置到SolrQuery当中,最后直接调用solrj的API,获得搜索结果。最终将搜索结构以List<Object>的形式返回。
具体实现:

Java代码
  1. package org.uppower.tnt.biz.core.manager.blog.solrj;
  2. import java.net.MalformedURLException;
  3. import java.util.Map;
  4. import org.apache.solr.client.solrj.SolrQuery;
  5. import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
  6. import org.apache.solr.client.solrj.response.QueryResponse;
  7. import org.apache.solr.common.SolrDocumentList;
  8. /**
  9. * @author yingmu
  10. * @version 2010-7-20 下午02:57:04
  11. */
  12. public class SolrjQuery {
  13. private String url;
  14. private Integer soTimeOut;
  15. private Integer connectionTimeOut;
  16. private Integer maxConnectionsPerHost;
  17. private Integer maxTotalConnections;
  18. private Integer maxRetries;
  19. private CommonsHttpSolrServer solrServer = null;
  20. private final static String ASC = “asc”;
  21. public void init() throws MalformedURLException {
  22. solrServer = new CommonsHttpSolrServer(url);
  23. solrServer.setSoTimeout(soTimeOut);
  24. solrServer.setConnectionTimeout(connectionTimeOut);
  25. solrServer.setDefaultMaxConnectionsPerHost(maxConnectionsPerHost);
  26. solrServer.setMaxTotalConnections(maxTotalConnections);
  27. solrServer.setFollowRedirects(false);
  28. solrServer.setAllowCompression(true);
  29. solrServer.setMaxRetries(maxRetries);
  30. }
  31. public SolrDocumentList query(Map<String, String> propertyMap,
  32. Map<String, String> compositorMap, Long startIndex, Long pageSize)
  33. throws Exception {
  34. SolrQuery query = new SolrQuery();
  35. // 设置搜索字段
  36. if (null == propertyMap) {
  37. throw new Exception(“搜索字段不可为空!”);
  38. } else {
  39. for (Object o : propertyMap.keySet()) {
  40. StringBuffer sb = new StringBuffer();
  41. sb.append(o.toString()).append(“:”);
  42. sb.append(propertyMap.get(o));
  43. String queryString = addBlank2Expression(sb.toString());
  44. query.setQuery(queryString);
  45. }
  46. }
  47. // 设置排序条件
  48. if (null != compositorMap) {
  49. for (Object co : compositorMap.keySet()) {
  50. if (ASC == compositorMap.get(co)
  51. || ASC.equals(compositorMap.get(co))) {
  52. query.addSortField(co.toString(), SolrQuery.ORDER.asc);
  53. } else {
  54. query.addSortField(co.toString(), SolrQuery.ORDER.desc);
  55. }
  56. }
  57. }
  58. if (null != startIndex) {
  59. query.setStart(Integer.parseInt(String.valueOf(startIndex)));
  60. }
  61. if (null != pageSize && 0L != pageSize.longValue()) {
  62. query.setRows(Integer.parseInt(String.valueOf(pageSize)));
  63. }
  64. try {
  65. QueryResponse qrsp = solrServer.query(query);
  66. SolrDocumentList docs = qrsp.getResults();
  67. return docs;
  68. } catch (Exception e) {
  69. throw new Exception(e);
  70. }
  71. }
  72. private String addBlank2Expression(String oldExpression) {
  73. String lastExpression;
  74. lastExpression = oldExpression.replace(“AND”, ” AND “).replace(“NOT”,
  75. ” NOT “).replace(“OR”, ” OR “);
  76. return lastExpression;
  77. }
  78. public Integer getMaxRetries() {
  79. return maxRetries;
  80. }
  81. ……
  82. public void setMaxTotalConnections(Integer maxTotalConnections) {
  83. this.maxTotalConnections = maxTotalConnections;
  84. }
  85. }

 

整个实现是在Spring的基础上完成的,其中SolrjQuery的init()方法在Spring容器启动是初始化。Init()方法内的属性,也是直接注入的。上层与下层之间也完全用注入的方式解决。具体配置就不贴不出来了,大家都会。
整个代码很简陋,但是几乎支持了你想要搜索的条件设置,而且不会暴露任何与solr相关的内容给上层调用,使整个搜索几乎以sql语言的思想在设置条件。

来源:http://ilovejavaforever.iteye.com/blog/731181



快乐成长 每天进步一点点