企业级搜索solr应用

一 。solr简介

    solr是以lucene为内核开发的企业级搜索应用  应用程序可以通过http请求方式来提交索引,查询索引,提供了比lucene更丰富的查询语言,是

一个高性能,高可用环境全文搜索引擎

二 。solr安装配置

     1》下载solr安装包  solr所有版本 (http://archive.apache.org/dist/lucene/solr/) 

           这里下载 solr-5.5.4

     2》安装 解压将solr-5.5.4\server\solr-webapp下的webapp 拷贝到tomcat\webapps目录下 改名为solr 启动tomcat

       直接访问 出现404  找到tomcat/logs/localhost.2017-08-17.log 日志  出现以下异常

[html]  view plain  copy
  1. java.lang.NoClassDefFoundError: Failed to initialize Apache Solr: Could not find necessary SLF4j logging jars.   
  2. If using Jetty, the SLF4j logging jars need to go in the jetty lib/ext directory. For other containers,   
  3. the corresponding directory should be used. For more information, see: http://wiki.apache.org/solr/SolrLogging  
  4.     at org.apache.solr.servlet.CheckLoggingConfiguration.check(CheckLoggingConfiguration.java:27)  
  5.     at org.apache.solr.servlet.BaseSolrFilter.<clinit>(BaseSolrFilter.java:30)  

 
       可用看到缺少SLF4j包 应该去 应该去 解压包 /server/lib/ext下找到并拷贝到 tomcat/solr/lib目录下  然后重启 
 

    继续访问 出现以下错误 

[html]  view plain  copy
  1. java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z  
  2.     org.apache.solr.servlet.SolrDispatchFilter.consumeInputFully(SolrDispatchFilter.java:284)  
  3.     org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:274)  
  4.     org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)  
    明显是Servlet版本不一致  tomcat6不支持solr5.54 加大tomcat版本 tomcat7也不支持 换成tomcat8  启动后访问 依然错误:

[html]  view plain  copy
  1. org.apache.solr.common.SolrException: Error processing the request. CoreContainer is either not initialized or shutting down.  
  2.     org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:217)  
  3.     org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)  
   是因为需要配置solrhome和solrhome的配置环境

   3》配置solrhome

     找到 tomcat\solr\WEB-INF\web.xml 编辑  找到以下这段(配置solrhome)  去掉注释 将第二个参数配置为本地任意一个目录即可 

[html]  view plain  copy
  1. <env-entry>  
  2.    <env-entry-name>solr/home</env-entry-name>  
  3.    <env-entry-value>D:\learn\solr-5.5.4\home</env-entry-value>  
  4.    <env-entry-type>java.lang.String</env-entry-type>  
  5. </env-entry>  
   找到solr解压包/server/solr目录拷贝所有文件到 以上web.xml指定的路径D:\learn\solr-5.5.4\home下 重启tomcat 访问

   http://localhost:8080/solor/index.html  或者 http://localhost:8080/solr/admin.html  

  

   4》配置core(core类似于数据库可以插入多个document(数据库表行)每个document拥有多个 field 数据库的列)

   solrhome下新建一个core目录  比如mycore

   拷贝 solr解压包下\server\solr\configsets\basic_configs到新建目录 mycore中

  进入solr管理网页 点击 core admin 添加该core

 

  点击Add core后 成功后 检查 mycore目录 发现多了 core.properties和data两个资源

 登陆solr管理网站发现 列表中多了mycore

 4》配置文件理解

    core/conf目录下的两个配置文件非常重要 

    managed-schema 主要用于配置 可以提交到该core的所有field定义,field的类型定义,唯一标识符等

    常用配置如下:

[html]  view plain  copy
  1. 定义字段 _version_ 类型为long  indexed="true" 会进行分词索引  stored="true"表示存储到磁盘  
  2. <field name="_version_" type="long" indexed="true" stored="true"/>  
  3. 定义字段 id required="true" 表示所有的document必须添加id字段 multiValued="false" 表示是否是多值字段  
  4. <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />   
  5. 定义动态字段 所以_i结尾的字段都可以写入到当前的core  
  6. <dynamicField name="*_i"  type="int"    indexed="true"  stored="true"/>  
  7. 定义唯一标识符的字段  
  8. <uniqueKey>id</uniqueKey>  
  9. 定义字段类型的别名  
  10. <fieldType name="string" class="solr.StrField" sortMissingLast="true" />  
   solrconfig.xml  主要用于配置solor的主要配置信息 比如lucene版本 缓存 数据目录 请求路径映射 等 

[html]  view plain  copy
  1. 表示lucene版本  
  2. <luceneMatchVersion>5.5.4</luceneMatchVersion>  
  3. 表示数据目录 默认是data目录  
  4. <dataDir>${solr.data.dir:}</dataDir>   
  5. 自动提交配置  
  6. <autoCommit>   
  7.        当超过15000ms后自动提交所有数据  
  8.        <maxTime>${solr.autoCommit.maxTime:15000}</maxTime>   
  9.        是否马上就可以查询到  
  10.        <openSearcher>false</openSearcher>   
  11. </autoCommit>  
  12. 表示当路径为 /select时查询所有的数据  
  13. <requestHandler name="/select" class="solr.SearchHandler">  
  14.     <!-- default values for query parameters can be specified, these  
  15.          will be overridden by parameters in the request  
  16.       -->  
  17.      <lst name="defaults">  
  18.        <str name="echoParams">explicit</str>  
  19.        <int name="rows">10</int>  
  20.      </lst>  
  21. </requestHandler>  
 尝试在界面上添加数据和查询数据

 添加数据

 

 查询结果

 

查询的参数列表

  q表示查询的条件  字段名:值的格式

  fq表示filter query 过滤条件 和q是and的关系支持各种逻辑运算符 (参考https://cwiki.apache.org/confluence/display/solr/The+Standard+Query+Parser)

  sort表示排序 的字段  字段名 asc|desc 

  start 表示从第几行开始  rows表示查询的总行数

  fl表示查询显示的列 比如只需要查询 name_s,sex_i 这两列 使用,隔开

  df表示默认的查询字段 一般不设置

  Raw Query Parameters表示原始查询字段 可以使用 start=0&rows=10这种url的方式传入参数

  wt(write type)表示写入的格式 可以使用json和xml

  shards 多核同时搜索 solrhome拷贝mycore为mycore1  管理平台添加core   设置参数为 路径,路径来设置需要搜索的核

[html]  view plain  copy
  1. String shards = "localhost:8080/solr/mycore,localhost:8080/solr/mycore1";   
  2. query.set("shards", shards);  

  其他参考(https://cwiki.apache.org/confluence/display/solr/Common+Query+Parameters)

 5》配置中文分词器

   默认solr 没有使用中文分词器  所有搜索的词 都是整个句子就是一个词 搜索时 将单词全部写入才能搜索或者使用* 需要配置中文分词器

目前比较好用的分词器 是IK  2012年停更 只支持到 Lucene4.7 所有 solr5.5 需要lucene5支持  需要修改部分源码来支持solr5.5

 找到 IKAnalyzer类   需要重写  protected TokenStreamComponents createComponents(String fieldName) 方法

 找到 IKTokenizer类 需要重写构造方法  public IKTokenizer(Reader in, boolean useSmart) 为  public IKTokenizer(boolean useSmart) {

 在任意项目中 使用maven 引用lucene5 和ik

<!-- 解决maven的冲突 -->
	<dependencies>
		<dependency>
			<groupId>com.janeluo</groupId>
			<artifactId>ikanalyzer</artifactId>
			<version>2012_u6</version>
			<exclusions><!--排除4.7.2版本中的jar-->
				<exclusion>
					<groupId>org.apache.lucene</groupId>
					<artifactId>lucene-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.lucene</groupId>
					<artifactId>lucene-queryparser</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.lucene</groupId>
					<artifactId>lucene-queries</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.lucene</groupId>
					<artifactId>lucene-sandbox</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.lucene</groupId>
					<artifactId>lucene-analyzers-common</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency><!--添加5.5.4的jar -->
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-core</artifactId>
			<version>5.5.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-queryparser</artifactId>
			<version>5.5.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-queries</artifactId>
			<version>5.5.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-sandbox</artifactId>
			<version>5.5.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.lucene</groupId>
			<artifactId>lucene-analyzers-common</artifactId>
			<version>5.5.4</version>
		</dependency>
	</dependencies> cop


在项目中 添加完整的包名和类名 和 ik中一致 拷贝源代码



代码修改对应的方法即可

IKAnalyzer

[html]  view plain  copy
  1. /**  
  2.   
  3.  *  
  4.  */  
  5. package org.wltea.analyzer.lucene;  
  6.   
  7. import java.io.Reader;  
  8.   
  9. import org.apache.lucene.analysis.Analyzer;  
  10. import org.apache.lucene.analysis.Tokenizer;  
  11.   
  12. /**  
  13.  */  
  14. public final class IKAnalyzer extends Analyzer {  
  15.   
  16.   private boolean useSmart;  
  17.   
  18.   public boolean useSmart() {  
  19.     return useSmart;  
  20.   }  
  21.   
  22.   public void setUseSmart(boolean useSmart) {  
  23.     this.useSmart = useSmart;  
  24.   }  
  25.   
  26.   /**  
  27.   
  28.    */  
  29.   public IKAnalyzer() {  
  30.     this(false);  
  31.   }  
  32.   
  33.   /**  
  34.    */  
  35.   public IKAnalyzer(boolean useSmart) {  
  36.     super();  
  37.     this.useSmart = useSmart;  
  38.   }  
  39.   
  40.   /**这里就去掉了 Reader的一个参数  
  41.    */  
  42.   @Override  
  43.   protected TokenStreamComponents createComponents(String fieldName) {  
  44.     Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart());  
  45.     return new TokenStreamComponents(_IKTokenizer);  
  46.   }  
  47.   
  48. }  
IKTokenizer

[html]  view plain  copy
  1. /**  
  2.  *   
  3.  */  
  4. package org.wltea.analyzer.lucene;  
  5.   
  6. import java.io.IOException;  
  7. import java.io.Reader;  
  8.   
  9. import org.apache.lucene.analysis.Tokenizer;  
  10. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;  
  11. import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;  
  12. import org.apache.lucene.analysis.tokenattributes.TypeAttribute;  
  13.   
  14. import org.wltea.analyzer.core.IKSegmenter;  
  15. import org.wltea.analyzer.core.Lexeme;  
  16.   
  17.   
  18. public final class IKTokenizer extends Tokenizer {  
  19.   
  20.   
  21.   private IKSegmenter _IKImplement;  
  22.   
  23.   
  24.   private final CharTermAttribute termAtt;  
  25.   
  26.   private final OffsetAttribute offsetAtt;  
  27.   
  28.   private final TypeAttribute typeAtt;  
  29.   
  30.   private int endPosition;  
  31.   
  32.   //去掉了其中Reader的第一个构造参数  
  33.   public IKTokenizer(boolean useSmart) {  
  34.     super();//去掉super中的构造参数  
  35.     offsetAtt = addAttribute(OffsetAttribute.class);  
  36.     termAtt = addAttribute(CharTermAttribute.class);  
  37.     typeAtt = addAttribute(TypeAttribute.class);  
  38.     _IKImplement = new IKSegmenter(input, useSmart);  
  39.   }  
  40.   
  41.    
  42.   @Override  
  43.   public boolean incrementToken() throws IOException {  
  44.   
  45.     clearAttributes();  
  46.     Lexeme nextLexeme = _IKImplement.next();  
  47.     if (nextLexeme != null) {  
  48.   
  49.       termAtt.append(nextLexeme.getLexemeText());  
  50.      
  51.       termAtt.setLength(nextLexeme.getLength());  
  52.         
  53.       offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());  
  54.      
  55.       endPosition = nextLexeme.getEndPosition();  
  56.     
  57.       typeAtt.setType(nextLexeme.getLexemeTypeString());  
  58.   
  59.       return true;  
  60.     }  
  61.   
  62.     return false;  
  63.   }  
  64.   
  65.   /*  
  66.    * (non-Javadoc)  
  67.    * @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader)  
  68.    */  
  69.   @Override  
  70.   public void reset() throws IOException {  
  71.     super.reset();  
  72.     _IKImplement.reset(input);  
  73.   }  
  74.   
  75.   @Override  
  76.   public final void end() {  
  77.     // set final offset  
  78.     int finalOffset = correctOffset(this.endPosition);  
  79.     offsetAtt.setOffset(finalOffset, finalOffset);  
  80.   }  
  81. }  

在MAVEN\apache-maven-3.0.4_localtest\resp\com\janeluo\ikanalyzer\2012_u6目录下找到ikanalyzer-2012_u6.jar

拷贝该jar,在编译好java类的maven项目中的target\classes\org\wltea\analyzer\lucene目录下找到class文件,

将编译好的class文件替换拷贝的jar包中

然后将jar放到tomcat的apache-tomcat-8.0.44\webapps\solr\WEB-INF\lib目录下;

将solrhome下 配置文件managed-schema 添加一个字段类型 使用ik分词器

[html]  view plain  copy
  1. <fieldType name="text_ik" class="solr.TextField" >  
  2.       <analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>     
  3.       <analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>   
  4.     </fieldType>  
不能修改 StrField 不支持自定义分词器

<fieldType name="string" class="solr.StrField" sortMissingLast="true" >
</fieldType>


然后将对应需要进行中文分词的字段使用 text_ik该字段类型 比如

[html]  view plain  copy
  1. <dynamicField name="*_s"  type="text_ik"  indexed="true"  stored="true" />  
重启 或者 cloud环境下重新生成collection 插入数据即可实现中文分词  通过某些中文关键字搜索

三。solr客户端

  solr提供的solrj java客户端可以使用java来添加和查询索引 

使用maven引入solrj的依赖库

[html]  view plain  copy
  1. <!-- https://mvnrepository.com/artifact/org.apache.solr/solr-solrj -->  
  2. <dependency>  
  3.     <groupId>org.apache.solr</groupId>  
  4.     <artifactId>solr-solrj</artifactId>  
  5.     <version>5.5.4</version>  
  6. </dependency>  

使用客户端操作添加和查询索引的代码

package cn.et;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.Group;
import org.apache.solr.client.solrj.response.GroupCommand;
import org.apache.solr.client.solrj.response.GroupResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.GroupParams;

public class TestSolr {
	static String urlString="http://localhost:8080/solr/mycore";
	static SolrClient solr;
	static{
		solr = new HttpSolrClient(urlString);
	}
	public static void main(String[] args) throws SolrServerException, IOException {
		getCount();
	}
	/**
	 * 用对象写入数据
	 * @throws SolrServerException 
	 * @throws IOException 
	 */
	public static void writeBean() throws IOException, SolrServerException{
		HttpSolrClient hsc=new HttpSolrClient(urlString);
		Food food=new Food();
		food.setId("10");
		food.setFoodname_ik("青椒炒肉");
		hsc.addBean(food);
		hsc.commit();
		hsc.close();
	}
	/**
	 * 通过对象读取数据
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	public static void readBean() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(urlString);
		SolrQuery sq=new SolrQuery();
		sq.setQuery("foodname_ik:青椒");
		sq.setSort("id",ORDER.asc);
		sq.setStart(0);
		sq.setRows(20);
		List<Food> beans = hsc.query(sq).getBeans(Food.class);
		
		for (Food food : beans) {
			System.out.println(food.getFoodname_ik());
		}
		hsc.close();
	}
	/**
	 * 统计个数
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void getCount() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(urlString);
		SolrQuery sq=new SolrQuery();
		sq.setQuery("foodname_ik:青椒");
		sq.setSort("id",ORDER.asc);
		sq.setStart(0);
		sq.setRows(20);
		//设置统计结果
		sq.setFacet(true);
		sq.addFacetField("type_s");
		QueryResponse query = solr.query(sq);
		
		List<FacetField> facetDates = query.getFacetFields();
		for (FacetField facetField : facetDates) {
			List<Count> values = facetField.getValues();
			for (Count count : values) {
				System.out.println(count.getName()+":"+count.getCount());
				
			}
		}

		hsc.close();
	}
	/**
	 * 用document装配
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void write() throws SolrServerException, IOException{
		
		SolrInputDocument document = new SolrInputDocument();
		document.addField("id", "4");
		document.addField("name_ik", "糖醋里脊");
		document.addField("price_d", "49.99");
		UpdateResponse response = solr.add(document);
		// Remember to commit your changes!
		solr.commit();
		solr.close();
	}
	/**
	 * 分组
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void groupBy() throws SolrServerException, IOException{
		SolrQuery solrQuery=new SolrQuery("content_ik:桂林");
		solrQuery.setParam(GroupParams.GROUP, true);
		
		solrQuery.setParam(GroupParams.GROUP_FIELD, "type_s");
		solrQuery.setParam("group.ngroups", true);
		QueryResponse query=solr.query(solrQuery);
		solrQuery.setParam(GroupParams.GROUP_LIMIT, "10");
		GroupResponse groupResponse=query.getGroupResponse();
		List<GroupCommand> values = groupResponse.getValues();
		for (GroupCommand groupCommand : values) {
			String name = groupCommand.getName();
			List<Group> values2 = groupCommand.getValues();
			for (Group group : values2) {
				System.out.println(group.getGroupValue());
				SolrDocumentList result = group.getResult();
				for (SolrDocument solrDocument : result) {
					System.out.println(solrDocument.getFieldValue("content_ik"));
				}
				System.out.println("----------");
			}
			
		}
	}
	/**
	 * 根据document查询,设置高亮
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void read() throws SolrServerException, IOException{
		SolrQuery solrQuery=new SolrQuery();
		//查询
		solrQuery.setQuery("foodname_ik:青椒");
		//过滤(不考虑得分)
		//solrQuery.setFilterQueries("foodname_ik:青椒");
		//排序
		solrQuery.setSort("id",ORDER.asc);
		//分页查询两个参数
		//开始位置 从0开始
		solrQuery.setStart(0);
		//返回总行数
		solrQuery.setRows(2);
		//设置高亮
		//是否高亮
		solrQuery.setHighlight(true);
		solrQuery.addHighlightField("foodname_ik");
		solrQuery.set("hl.fl", "foodname_ik");
		solrQuery.setHighlightSimplePre("</font color=red>");
		solrQuery.setHighlightSimplePost("</font>");
		//solrQuery.set(HighlightParams.HIGHLIGHT, "foodname_ik");
		QueryResponse query = solr.query(solrQuery);
		//获取集合
		SolrDocumentList results = query.getResults();
		Map<String, Map<String, List<String>>> highlighting = query.getHighlighting();
		
		for (SolrDocument solrDocument : results) {
			String id=solrDocument.getFieldValue("id").toString();
			System.out.println(id);
			System.out.println(solrDocument.getFieldValue("foodname_ik"));
			Map<String, List<String>> map = highlighting.get(id);
			List<String> list = map.get("foodname_ik");
			String highStr=list.get(0);
			System.out.println(highStr);
		}
		solr.close();
	}
	/**
	 * 根据id删除
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void delete() throws SolrServerException, IOException{
		UpdateResponse deleteById = solr.deleteById("4");
		solr.commit();
		solr.close();
	}
	/**
	 * 根据条件删除
	 * @throws SolrServerException
	 * @throws IOException
	 */
	public static void deleteByCondition() throws SolrServerException, IOException{
		UpdateResponse deleteById = solr.deleteByQuery("foodname_ik:炒蛋");
		solr.commit();
		solr.close();
	}
}
对象类

package cn.et;

import org.apache.solr.client.solrj.beans.Field;

public class Food {
	public Food(){
		
	}
	@Field
	private String id;
	@Field
	private String foodname_ik;
	@Field
	private String type_s;
	public String getType_s() {
		return type_s;
	}
	public void setType_s(String type_s) {
		this.type_s = type_s;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getFoodname_ik() {
		return foodname_ik;
	}
	public void setFoodname_ik(String foodname_ik) {
		this.foodname_ik = foodname_ik;
	}
	
}



猜你喜欢

转载自blog.csdn.net/XCL18215166914/article/details/79078797
今日推荐