用SolrJ操作Solr做搜索(上篇)

达芬奇密码2018-07-16 15:34

Solr是一个基于Apache Lucene ,流行的,极速,开源的企业搜索平台。主要由以下特点:

1、高级的全文搜索功能
2、专为高通量的网络流量进行的优化
3、基于开放接口(XML和HTTP)的标准
4、综合的HTML管理界面
5、可伸缩性-能够有效地复制到另外一个Solr搜索服务器
6、使用XML配置达到灵活性和适配性
7、可扩展的插件体系
今天主要介绍用SolrJ来操作Solr处理数据。

1、系统环境

1、Solr5.5.0
2、Java 1.7
3、MySQL 5.7
4、SolrJ 5.5.0
5、Tomcat 7.0
6、Windows 7
PS:Solr各个版本差别略大,配置文件也不尽相同。需要谨慎选择版本型号,不然可能本文的一些配置无法生效。

2、Solr单机安装与部署

这里提供两种安装Solr的方式。 安装部署之前先到官网(http://archive.apache.org/dist/lucene/solr/)下载文件包。
一、Solr安装部署
1、启动
 本地把安装包解压到E:\tools\solr\solr-5.5.0,在本路径执行 bin/solr.cmd start命令,默认端口是8983。
2、查看
 访问链接http://localhost:8983/solr/  我们就可以查到Solr已经启动了!
 

Solr就算安装好了。
二、Solr与tomcat集成
1、迁移solr工程
将solr-5.5.0\server\solr-webapp目录下的webapp文件夹拷贝到tomcat7下webapps下,并改名为solr
2、修改eb.xml
修改tomcat7\webapps\solr\WEB-INF目录下的web.xml文件,添加如下配置:
这里配置了Solr的主目录,后期创建core,日志信息都会在这个路径里面。
   <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>E:\\tools\\tomcat\\tomcat7\\apache-tomcat-7.0.54\\bin\\solr</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </env-entry>
3、拷贝jar包
solr-5.5.0\server\lib\ext目录下的所有jar包拷贝到tomcat7 -> webapps -> solr -> WEB-INF -> lib
4、拷贝log4j.properties
solr-5.5.0\server\resources目录下的log4j.properties文件拷贝到 D:\ apache-tomcat-7.0.54\solr\WEB-INF\classes下。配置后,可以查看系统日志信息。对于后期的全量,增量数据处理,错误信息查询等非常重要。
5、启动Tomcat
访问链接http://localhost/solr/index.html  我们就可以查到Solr已经启动了!

3、初始化数据

 初始化数据包括两个部分,第一部分为为Solr建立core,第二部分为core放入索引数据(这里用SolrJ来处理,当然也可以配置MySQL实现增量,全量配置)。我们用8983端口的Solr来操作。
一、Solr创建Core
1、初始化Core
   默认Solr的主目录为solr-5.5.0\server\solr\目录下面,我们就在这个目录创建core,命名为new_core,新建conf文件夹,把solr-5.5.0\example\example-DIH\solr\solr\conf里面的内容复制到solr-5.5.0\server\solr\new_core\conf里面;在solr-5.5.0\server\solr\new_core目录新建data文件夹。
2、添加core
   在Solr Admin管理界面如下操作,这样一个core就创建成功了。
   
二、初始化Core数据
1、创建数据库表
CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `categoryName` varchar(64) DEFAULT NULL,
  `brandName` varchar(64) DEFAULT NULL,
  `productName` varchar(128) DEFAULT NULL,
  `productUrl` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=206085 DEFAULT CHARSET=utf8;

2、定义Model
public class Product {

	public static final String ID = "id";
	public static final String CATEGORYNAME = "cateGoryName";
	public static final String BRANDNAME = "brandName";
	public static final String COMMODITYNAME = "commodityName";
	public static final String COMMODITYIMGURL = "commodityImgUrl";
	
	public static final String SUGGESTION = "suggestion";
	
	@Field(ID)
	private String id ;
	@Field(CATEGORYNAME)
	private String cateGoryName; 
	@Field(BRANDNAME)
	private String brandName; 
	@Field(COMMODITYNAME)
	private String commodityName; 
	@Field(COMMODITYIMGURL)
	private String commodityImgUrl; 
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getCateGoryName() {
		return cateGoryName;
	}
	public void setCateGoryName(String cateGoryName) {
		this.cateGoryName = cateGoryName;
	}
	public String getBrandName() {
		return brandName;
	}
	public void setBrandName(String brandName) {
		this.brandName = brandName;
	}
	public String getCommodityName() {
		return commodityName;
	}
	public void setCommodityName(String commodityName) {
		this.commodityName = commodityName;
	}
	public String getCommodityImgUrl() {
		return commodityImgUrl;
	}
	public void setCommodityImgUrl(String commodityImgUrl) {
		this.commodityImgUrl = commodityImgUrl;
	}
	
}
@Field里面的字段名称需要能在Coremanaged-schema配置文件里面找到,如果没有则手动添加:
     <field name="cateGoryName" type="string" indexed="true" stored="true" multiValued="false" /> 
   <field name="brandName" type="string" indexed="true" stored="true" multiValued="false" /> 
   <!-- 定义solr索引结果的字段 -->
   <field name="commodityName" type="string" indexed="true" stored="true" multiValued="false" />
   <field name="commodityImgUrl" type="string" indexed="true" stored="true" multiValued="false" /> 
3、添加数据
  这里用Spring JdbcTemplate 来添加数据的,20W条数据花了2个小时醉了。。。 代码如下:
public DataSource createDataSource (){
DriverManagerDataSource dataSource = new DriverManagerDataSource("jdbc:mysql://localhost:3306/solr?useUnicode=true&amp;characterEncoding=UTF-8", "root", "XXXX");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}


public JdbcTemplate getJdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate(createDataSource());
return jdbcTemplate;
}

public boolean batchInsertProducts(final List<Product> list) {

String sql = "insert into product(categoryName, brandName, productName, productUrl) values (?, ?, ?, ?)";

getJdbcTemplate().batchUpdate(sql,new BatchPreparedStatementSetter(){
@Override
            public void setValues(PreparedStatement ps,int i)throws SQLException
            {

 String categoryName = list.get(i).getCateGoryName();
 String brandName = list.get(i).getBrandName();
 String productName = list.get(i).getCommodityName();
 String productUrl = list.get(i).getCommodityImgUrl();
 
             ps.setString(1, categoryName);
             ps.setString(2, brandName);
             ps.setString(3, productName);
             ps.setString(4, productUrl);
             
            }
            
            @Override
            public int getBatchSize()
            {
             return list.size();
            }

     });

return true;
}
数据库有数据了,下面就把数据库数据索引到Solr里面:

public List<Product> findProducts(){

return getJdbcTemplate().query("select * from product", new RowMapper<Product>() {
            @Override
            public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
             Product product = new Product();

             product.setId(String.valueOf(rs.getInt("id")));
             product.setCateGoryName(rs.getString("categoryName"));
             product.setBrandName(rs.getString("brandName"));
             product.setCommodityName(rs.getString("productName"));
             product.setCommodityImgUrl(rs.getString("productUrl"));
            
                return product;
            }
        });

}
List<Product> list = new SolrServer().findProducts();
solrClient.indexProducts(list);
		
/**
	 * 索引所有数据到Solr
	 * @param list
	 * @throws Exception
	 */
	public void indexProducts(List<Product> list) throws Exception{
		
		if(ObjectUtils.isEmpty(list)){
			return;
		}
		
		httpSolrClient.addBeans(list);
		commitSolrClient(httpSolrClient);
	}
这样就把数据库的所有数据索引(部分数据为了测试后面的智能Suggest模块手动做了修改)到Solr上面了,可以用Solr Admin 看下:

4、Solr 实现分页查询数据

QueryBuilder.java 
public class QueryBuilder {
	
	/**
	 * 拼接查询参数
	 * @param queryStr
	 * @param filterMap
	 * @param solrQuery
	 */
	public static void handlerSolrQueryParamByOr(Map queryStr, Map filterMap, SolrQuery solrQuery){ 
		System.out.println(combileQueryStr(queryStr, true));
		if(!ObjectUtils.isEmpty(queryStr) && queryStr.size() > 0 ){
			solrQuery.setQuery(combileQueryStr(queryStr, true));
		}
		if(!ObjectUtils.isEmpty(filterMap) && filterMap.size() > 0){
			solrQuery.setFilterQueries(combileQueryStr(filterMap, true)); //过滤结果集[这里可以不用or连接,也可以空格 例如:id:72 id:73 id:74]
		}
	}
	
	/**
	 * 拼接查询参数
	 * @param queryStr
	 * @param filterMap
	 * @param solrQuery
	 */
	public static void handlerSolrQueryParamByAnd(Map queryStr, Map filterMap, SolrQuery solrQuery){ 
		System.out.println(combileQueryStr(queryStr, true));
		if(!ObjectUtils.isEmpty(queryStr) && queryStr.size() > 0 ){
			solrQuery.setQuery(combileQueryStr(queryStr, false));
		}
		if(!ObjectUtils.isEmpty(filterMap) && filterMap.size() > 0){
			solrQuery.setFilterQueries(combileQueryStr(filterMap, true)); //过滤结果集[这里可以不用or连接,也可以空格 例如:id:72 id:73 id:74]
		}
	}
	
	
	private static String combileQueryStr(Map queryStr, boolean flag) {

		if(ObjectUtils.isEmpty(queryStr)){
			return null;
		}
		
		String joinerStr = "AND"; 
		if(flag){
			joinerStr = "OR";
		}
		
		StringBuilder stringBuilder = new StringBuilder();
		for (Map.Entry entry : queryStr.entrySet()) {
			stringBuilder.append(entry.getKey() + ":" + "*" + entry.getValue() + "* " + joinerStr + " ");
		}

		return flag?stringBuilder.toString().substring(0, stringBuilder.toString().length()-4):stringBuilder.toString().substring(0, stringBuilder.toString().length()-5);
		
	}
	
}
分页查询函数:
/**
	 * 查询索引数据
	 * @param queryStr
	 * @param pageNo
	 * @param pageSize
	 * @param sort
	 * @return
	 * @throws Exception
	 */
	public Map queryData(Map map, Map filterMap, int pageNo, int pageSize, String sort, Opt opt) throws Exception {

		Map maps = new HashMap();
		List proList = new ArrayList<>();
		SolrQuery solrQuery = new SolrQuery();
		
		if(opt.equals(Opt.ADD)){
			QueryBuilder.handlerSolrQueryParamByAnd(map, filterMap, solrQuery);
		}else if(opt.equals(Opt.OR)){
			QueryBuilder.handlerSolrQueryParamByOr(map, filterMap, solrQuery);
		}
		
		int start = (pageNo - 1) * pageSize;
		solrQuery.setStart(start); // 设置起始位置
		solrQuery.setRows(pageSize); // 设置页大小
		solrQuery.setHighlight(true); // 启用高亮
		solrQuery.addHighlightField("commodityName"); // 设置高亮字段
		solrQuery.setHighlightFragsize(200); // 设置高亮内容大小
		solrQuery.setHighlightSimplePre(""); // 设置高亮前缀
		solrQuery.setHighlightSimplePost(""); // 设置高亮后缀

		if(sort != null){
			solrQuery.setSort("//TODD", ORDER.desc);
		}

		QueryResponse queryResponse = httpSolrClient.query(solrQuery);
		int qtime = queryResponse.getQTime();
		SolrDocumentList solrDocumentList = queryResponse.getResults();

		Map<String, Map<String, List<String>>> highlightingMaps = queryResponse.getHighlighting();
		long totals = solrDocumentList.getNumFound(); // 查询到的总记录数

		if (!solrDocumentList.isEmpty()) {
			Iterator it = solrDocumentList.iterator();
			while (it.hasNext()) {
				SolrDocument solrDocument = it.next();
				String id = solrDocument.getFieldValue("id").toString();
				Map> highlightFieldMap = highlightingMaps.get(id);
				if (!highlightFieldMap.isEmpty()) {
					List highlightName = highlightFieldMap.get("commodityName"); //这里对命中的字段做高亮展示
					if (highlightName != null && !highlightName.isEmpty()) {
						String name = highlightName.get(0);
						solrDocument.setField("commodityName", name);
					}
					
				}
				Product product = doc2Bean(solrDocument);
				proList.add(product);
			}
		}
		maps.put("qtime", qtime);
		maps.put("totals", totals);
		maps.put("results", proList);
		return maps;

	}
执行,查看效果,当然我们也可以对结果集进行过滤(模糊过滤;字段类型过滤):
数据如下:


相关阅读:用SolrJ操作Solr做搜索(下篇)

本文来自网易实践者社区,经作者田躲躲授权发布。