企业级搜索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 日志  出现以下异常

 

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

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

    继续访问 出现以下错误 

java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z
	org.apache.solr.servlet.SolrDispatchFilter.consumeInputFully(SolrDispatchFilter.java:284)
	org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:274)
	org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)

    明显是Servlet版本不一致  tomcat6不支持solr5.54 加大tomcat版本 tomcat7也不支持 换成tomcat8  启动后访问 依然错误:

org.apache.solr.common.SolrException: Error processing the request. CoreContainer is either not initialized or shutting down.
	org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:217)
	org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)

   是因为需要配置solrhome和solrhome的配置环境

 

   3》配置solrhome

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

    <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>D:\learn\solr-5.5.4\home</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </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的类型定义,唯一标识符等

    常用配置如下:

定义字段 _version_ 类型为long  indexed="true" 会进行分词索引  stored="true"表示存储到磁盘
<field name="_version_" type="long" indexed="true" stored="true"/>
定义字段 id required="true" 表示所有的document必须添加id字段 multiValued="false" 表示是否是多值字段
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" /> 
定义动态字段 所以_i结尾的字段都可以写入到当前的core
<dynamicField name="*_i"  type="int"    indexed="true"  stored="true"/>
定义唯一标识符的字段
<uniqueKey>id</uniqueKey>
定义字段类型的别名
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />

  solrconfig.xml 主要用于配置solor的主要配置信息 比如lucene版本 缓存 数据目录 请求路径映射 等 

表示lucene版本
<luceneMatchVersion>5.5.4</luceneMatchVersion>
表示数据目录 默认是data目录
<dataDir>${solr.data.dir:}</dataDir> 
自动提交配置
<autoCommit> 
       当超过15000ms后自动提交所有数据
       <maxTime>${solr.autoCommit.maxTime:15000}</maxTime> 
       是否马上就可以查询到
       <openSearcher>false</openSearcher> 
</autoCommit>
表示当路径为 /select时查询所有的数据
<requestHandler name="/select" class="solr.SearchHandler">
    <!-- default values for query parameters can be specified, these
         will be overridden by parameters in the request
      -->
     <lst name="defaults">
       <str name="echoParams">explicit</str>
       <int name="rows">10</int>
     </lst>
</requestHandler>

 尝试在界面上添加数据和查询数据

 添加数据

 

 

 查询结果

 

查询的参数列表

  q表示查询的条件  字段名:值的格式 多个条件组合查询可以使用 字段:字段值 && 字段1:字段值1  也可以使用大写的AND或者OR

  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   设置参数为 路径,路径来设置需要搜索的核

 

String shards = "localhost:8080/solr/mycore,localhost:8080/solr/mycore1"; 
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

 

<dependency>
		  <groupId>org.apache.lucene</groupId>
		  <artifactId>lucene-core</artifactId>
		  <version>5.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.janeluo</groupId>
			<artifactId>ikanalyzer</artifactId>
			<version>2012_u6</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.lucene</groupId>
		 			 <artifactId>lucene-core</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

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


 

代码修改对应的方法即可

IKAnalyzer

/**

 *
 */
package org.wltea.analyzer.lucene;

import java.io.Reader;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;

/**
 */
public final class IKAnalyzer extends Analyzer {

  private boolean useSmart;

  public boolean useSmart() {
    return useSmart;
  }

  public void setUseSmart(boolean useSmart) {
    this.useSmart = useSmart;
  }

  /**

   */
  public IKAnalyzer() {
    this(false);
  }

  /**
   */
  public IKAnalyzer(boolean useSmart) {
    super();
    this.useSmart = useSmart;
  }

  /**这里就去掉了 Reader的一个参数
   */
  @Override
  protected TokenStreamComponents createComponents(String fieldName) {
    Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart());
    return new TokenStreamComponents(_IKTokenizer);
  }

}

IKTokenizer

/**
 * 
 */
package org.wltea.analyzer.lucene;

import java.io.IOException;
import java.io.Reader;

import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;

import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;


public final class IKTokenizer extends Tokenizer {


  private IKSegmenter _IKImplement;


  private final CharTermAttribute termAtt;

  private final OffsetAttribute offsetAtt;

  private final TypeAttribute typeAtt;

  private int endPosition;

  //去掉了其中Reader的第一个构造参数
  public IKTokenizer(boolean useSmart) {
    super();//去掉super中的构造参数
    offsetAtt = addAttribute(OffsetAttribute.class);
    termAtt = addAttribute(CharTermAttribute.class);
    typeAtt = addAttribute(TypeAttribute.class);
    _IKImplement = new IKSegmenter(input, useSmart);
  }

 
  @Override
  public boolean incrementToken() throws IOException {

    clearAttributes();
    Lexeme nextLexeme = _IKImplement.next();
    if (nextLexeme != null) {

      termAtt.append(nextLexeme.getLexemeText());
   
      termAtt.setLength(nextLexeme.getLength());
      
      offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());
   
      endPosition = nextLexeme.getEndPosition();
  
      typeAtt.setType(nextLexeme.getLexemeTypeString());

      return true;
    }

    return false;
  }

  /*
   * (non-Javadoc)
   * @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader)
   */
  @Override
  public void reset() throws IOException {
    super.reset();
    _IKImplement.reset(input);
  }

  @Override
  public final void end() {
    // set final offset
    int finalOffset = correctOffset(this.endPosition);
    offsetAtt.setOffset(finalOffset, finalOffset);
  }
}

将编译好的class文件替换原始jar包即可

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

<fieldType name="text_ik" class="solr.TextField" >
      <analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>   
      <analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/> 
    </fieldType>

不能修改 StrField 不支持自定义分词器

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

 

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

<dynamicField name="*_s"  type="text_ik"  indexed="true"  stored="true" />

重启 或者 cloud环境下重新生成collection 插入数据即可实现中文分词  通过某些中文关键字搜索

 6》添加业务拓展词库 

ik默认支持拓展词库(连续词不用在分次,比如爱他美不使用就回拆分为 爱和他和美)和拓展停止词
在类路径添加配置文件IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">words/cb.txt;</entry>
    <entry key="ext_stopwords">words/stopwords.txt</entry>
</properties>

新建words目录添加cb.txt和stopwords.txt
假设cb.txt有几个新词是:爱他美,格力  cb.txt内容(一个词占一行,公司系统内部正式库中可以通过sql导出一份词库):

爱他美
格力

停止词表示遇到这样的词直接忽略 比如的 是这样的词
项目测试
添加依赖:

dependencies>
        <!-- https://mvnrepository.com/artifact/com.janeluo/ikanalyzer -->
        <dependency>
            <groupId>com.janeluo</groupId>
            <artifactId>ikanalyzer</artifactId>
            <version>2012_u6</version>
        </dependency>

    </dependencies>

添加测试类

package cn.gvt;

import org.wltea.analyzer.cfg.Configuration;
import org.wltea.analyzer.cfg.DefaultConfig;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author jiaozi<liaomin @ gvt861.com>
 * @since JDK8
 * Creation time:2019/5/7 18:33
 */
public class TestMain {
    public static void main(String[] args) throws IOException {
        String s = "JAVA一级结构";
        queryWords(s);
    }

    public static void queryWords(String query) throws IOException {
        Configuration cfg = DefaultConfig.getInstance();
        System.out.println(cfg.getMainDictionary()); // 系统默认词库
        System.out.println(cfg.getQuantifierDicionary());
        List<String> list = new ArrayList<String>();
        IKSegmenter ikSeg = null;
        Scanner scanner=new Scanner(System.in);
        while(true) {
            System.out.print("请输入您的词:");
            String next=scanner.nextLine();
            if(ikSeg==null){
                ikSeg = new IKSegmenter(new StringReader(next.trim()), true);// true 用智能分词 ,false细粒度
            }else{
                ikSeg.reset(new StringReader(next.trim()));
            }
            for (Lexeme lexeme = ikSeg.next(); lexeme != null; lexeme = ikSeg.next()) {
                System.out.print(lexeme.getLexemeText() + "|");
            }
            System.out.println();
        }

    }
}

运行:

请输入您的词:爱他美我爱你
加载扩展词典:words/cb.txt
加载扩展停止词典:words/stopwords.txt
爱他美|我爱你|

集成到solr中,将words目录和IKAnalyzer.cfg.xml丢到tomcat下sor/WEB-INF/classes目录即可

分析是否成功使用分词


 7》初始全量导入数据库数据 

  假设存在表 news表示 其中 有以下数据:

进入solr所在服务器 搜索 dataimport相关jar包
 

solr@localhost:/opt/solr$ find / -name *import*.jar
/opt/solr/dist/solr-dataimporthandler-5.5.5.jar
/opt/solr/dist/solr-dataimporthandler-extras-5.5.5.jar

将这两个jar包拷贝到  solr启动应用 webapp/lib目录下 

cp /opt/solr/dist/solr-dataimporthandler-5.5.5.jar /opt/solr/server/solr-webapp/webapp/WEB-INF/lib
cp /opt/solr/dist/solr-dataimporthandler-extras-5.5.5.jar /opt/solr/server/solr-webapp/webapp/WEB-INF/lib

同时将mysql的驱动包 丢到该目录下  

编辑core的conf/solrconfig.xml 添加

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">  
      <lst name="defaults">  
         <str name="config">data-config.xml</str>  
      </lst>  
 </requestHandler>
 

在solrconfig.xml同一目录下 添加 data-config.xml(配置连接的数据库以及查询的sql语句  )

 <?xml version="1.0" encoding="UTF-8"?>  
<dataConfig>  
    <dataSource name="source1" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.1.3:3306/test" user="root" password="123456" batchSize="-1" />  
<document>  
          <entity name="book" pk="newid"  dataSource="source1"   
                query="select * from  mynew" >
            <field column="newid" name=""/>  
            <field column="newtitle" name="title_ik"/>  
        </entity>
</document>  
</dataConfig>  

访问solrweb管理界面  http://ip:

 

三。solr客户端

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

使用maven引入solrj的依赖库

 

<!-- https://mvnrepository.com/artifact/org.apache.solr/solr-solrj -->
<dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>5.5.3</version>
</dependency>

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

 

public class TestCrud {
	//请求的url
	public static final String url="http://localhost:8080/solr/mycore";
	/**
	 * 测试写入数据到solr
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	@Test
	public void testWriteDoc() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(url);
		SolrInputDocument sid=new SolrInputDocument();
		sid.addField("id", 1);
		sid.addField("name_s","张三");
		hsc.add(sid);
		hsc.commit();
		hsc.close();
	}
	/**
	 * 测试从solr读取数据
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	@Test
	public void testReadDoc() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(url);
		SolrQuery sq=new SolrQuery();
		sq.setQuery("name_s:*");
		sq.set("sort", "id asc");
		sq.setStart(0);
		sq.setRows(1);
		SolrDocumentList sdl=hsc.query(sq).getResults();
		for(SolrDocument sd:sdl){
			System.out.println(sd.getFieldValue("name_s"));
		}
		hsc.close();
	}
	/**
	 * 测试通过id删除
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	@Test
	public void testDelDoc() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(url);
		hsc.deleteById("1");
		hsc.commit();
		hsc.close();
	}
}

使用javabean的方式操作

javabean定义

 

import org.apache.solr.client.solrj.beans.Field;
public class UserInfo {
	public UserInfo() {
	}
	@Field
	private String id;
	@Field
	private String name_s;
	@Field
	private int age_i;
}

执行代码如下:

 

/**
	 * 测试写入数据到solr
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	@Test
	public void testWriteDoc() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(url);
		UserInfo ui=new UserInfo();
		ui.setId("2");
		ui.setName_s("李四");
		ui.setAge_i(100);
		hsc.addBean(ui);
		hsc.commit();
		hsc.close();
	}
	/**
	 * 测试从solr读取数据
	 * @throws IOException 
	 * @throws SolrServerException 
	 */
	@Test
	public void testReadDoc() throws SolrServerException, IOException{
		HttpSolrClient hsc=new HttpSolrClient(url);
		SolrQuery sq=new SolrQuery();
		sq.setQuery("name_s:*");
		sq.set("sort", "id asc");
		sq.setStart(0);
		sq.setRows(1);
		List<UserInfo> sdl=hsc.query(sq).getBeans(UserInfo.class);
		for(UserInfo sd:sdl){
			System.out.println(sd.getName_s());
		}
		hsc.close();
	}

 

四。solr集群安装

  1》集群方式:

   solr集群目前有两种方式 主从模式和solrcloud模式(推荐方式)

solrcloud通过zookeeper管理集群 通过将索引切片的方式分发到不同的后端服务器中 后端服务器 可以通过一主多从的方式来实现高可用  一主多从

通过leader的管理方式 leader负责写入  从机负责分摊并发读取  leader挂掉后 从机选举出新的leader继续进行管理

 2》solrcloud集群概念

   solrcloud分为逻辑层和物理层 

   逻辑层:

      》Cluster(表示zookeeper集群) 用于管理solrcloud的实例collection

      》Collection 表示一个solrcloud的实例 能够被切分为多个片

      》Shards (片) 一个Collection 可以被切分为多个片 片的个数决定了并发量的大小 每个片拥有多个备份 其中包括leader 负责写入  replica负责容灾和读请求

                   

  物理层:

      》 Cluster由多个solr 节点(物理机器)组成  每个节点对应linux的后台进程

      》 Node(节点) 每个节点由多个Core组成   

      》 Core 每个片在该节点的拷贝都属于一个core  可能每个片都有一份拷贝在当前机器上 当前机器 可能有多个core

      》Replica 是每个切片的一份拷贝  必须使用使用相同的配置 该配置需要写入到zookeeper中 

      

 3》solrcloud集群实现(伪集群)

   》》拷贝三份tomcat  分别修改server.xml tomcat端口  (8080,8081,8082)

   》》同单机安装拷贝solr应用到webapps目录下 修改web.xml 分别指向不同的solrhome

           比如我的配置  

 

tomcat18080D:\learn\solr-5.5.4\home
tomcat28081D:\learn\solr-5.5.4\home1
tomcat38082D:\learn\solr-5.5.4\home2

   》》拷贝 solr解压包下\server\solr到新建目录D:\learn\solr-5.5.4\home中 同时拷贝到hom1和home2     

            solr解压包下\server\solr\configsets\basic_configs到新建目录D:\learn\solr-5.5.4\home(只拷贝home 不拷贝home1和home2) 改名为

           collections1 

   》》tomcat/bin下的 catalina.cmd添加  

tomcat1下设置为:

 

set JAVA_OPTS=-Dsolr.solr.home=D:/learn/solr-5.5.4/home -Dbootstrap_confdir=D:/learn/solr-5.5.4/home/collection1/conf 
-Dcollection.configName=myconf -DnumShards=3 -DzkHost=localhost:2181

其他tomcat 设置为 

 

 

set JAVA_OPTS=-Dsolr.solr.home=D:/learn/solr-5.5.4/home  -DzkHost=localhost:2181

  》》这里假设本机开启了zookeeper 端口是2181 当然可以开启多个zookeeper地址使用,隔开(参考http://blog.csdn.net/liaomin416100569/article/details/71642091)

 

  依次启动 zookeeper  和所有的tomcat  访问 http://localhost:8080/solr/admin.html

  》》使用命令创建collection (其他操作参考 https://cwiki.apache.org/confluence/display/solr/Collections+API)

 

http://localhost:8081/solr/admin/collections?action=CREATE&name=collection1&numShards=3&replicationFactor=3&maxShardsPerNode=3&collection.configName=myconf

 

  》》查看zookeeper信息和集群分片信息

 查看zookeeper中写入的数据

   

查看分片到哪些节点

 

五。solr一些其他高级查询(参考代码)

 

package cn.et.solor;

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

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.response.FacetField;
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.PivotField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.common.params.GroupParams;
import org.apache.solr.common.util.NamedList;
import org.junit.Test;
/**
 * 支持一些高级特性 比如高亮 分类 分组 mtl(相似)
{"id":"1","country_s":"美国","provice_s":"加利福尼亚州","city_s":"旧金山","age_i":"30","name_s":"John","desc_s":"John is come from austrina  John,s Dad is Johh Super"}
{"id":"2","country_s":"美国","provice_s":"加利福尼亚州","city_s":"好莱坞","age_i":"40","name_s":"Mike","desc_s":"Mike is come from austrina  Mike,s Dad  is Mike Super"}
{"id":"3","country_s":"美国","provice_s":"加利福尼亚州","city_s":"圣地牙哥","age_i":"50","name_s":"Cherry","desc_s":"Cherry is come from austrina  Cherry,s Dad  is Cherry Super"}
{"id":"4","country_s":"美国","provice_s":"德克萨斯州","city_s":"休斯顿","age_i":"60","name_s":"Miya","desc_s":"Miya is come from austrina  Miya,s Dad  is Miya Super"}
{"id":"5","country_s":"美国","provice_s":"德克萨斯州","city_s":"大学城","age_i":"70","name_s":"fubos","desc_s":"fubos is come from austrina  fubos,s Dad  is fubos Super"}
{"id":"6","country_s":"美国","provice_s":"德克萨斯州","city_s":"麦亚伦","age_i":"20","name_s":"marry","desc_s":"marry is come from austrina  marry,s Dad  is marry Super"}
{"id":"7","country_s":"中国","provice_s":"湖南省","city_s":"长沙市","age_i":"18","name_s":"张三","desc_s":"张三来自长沙市 是公务员一名"}
{"id":"8","country_s":"中国","provice_s":"湖南省","city_s":"岳阳市","age_i":"15","name_s":"李四","desc_s":"李四来自岳阳市 是一名清洁工"}
{"id":"9","country_s":"中国","provice_s":"湖南省","city_s":"株洲市","age_i":"33","name_s":"李光四","desc_s":"李光四 老家岳阳市 来自株洲 是李四的侄子"}
{"id":"10","country_s":"中国","provice_s":"广东省","city_s":"深圳市","age_i":"67","name_s":"王五","desc_s":"王五来自深圳市  是来自深圳的一名海关缉私精英"}
{"id":"11","country_s":"中国","provice_s":"广东省","city_s":"广州市","age_i":"89","name_s":"王冠宇","desc_s":"王冠宇是王五的儿子"}
 */
public class TestCloud {
	/**
	 * 连接solrcloud
	 * @return
	 */
	public CloudSolrClient getCloudSolrClient(){
		String zkHost="localhost:2181";
	    CloudSolrClient csc=new CloudSolrClient(zkHost);
	    csc.setDefaultCollection("collection1");//集合名称
	    return csc;
	}
	/**
	 * solrcloud保存 修改 删除和单机相同
	 */
	@Test
	public void save() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		UserInfo ui=new UserInfo();
		ui.setId("4");
		ui.setName_s("王五");
		ui.setAge_i(100);
		csc.addBean(ui);
		csc.commit();
		csc.close();
	}
	/**
	 * solrcloud 删除
	 */
	//@Test
	public void delete() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		csc.deleteByQuery("*:*");
		csc.commit();
		csc.close();
	}

	/**
	 * solrcloud高亮显示
	 * 必须设置中文分词器
	 */
	@Test
	public void queryHign() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		SolrQuery sq=new SolrQuery();
		sq.setQuery("desc_s:王五");
		sq.addHighlightField("desc_s");
		sq.setHighlight(true);
		sq.setHighlightSimplePre("<font color=red>");
		sq.setHighlightSimplePost("</font>");
		QueryResponse qr=csc.query(sq);
		List<UserInfo> userInfo=qr.getBeans(UserInfo.class);
		Map<String, Map<String, List<String>>> highlighting = qr.getHighlighting();
		System.out.println(highlighting);
		for(UserInfo ui:userInfo){
			System.out.println(ui.getName_s());
		}
		System.out.println(userInfo.size());
		csc.commit();
		csc.close();
	}
	
	/**
	 * Facet 面 用于对搜索的结果进行分类 
	 *  比如按国家分类   addFacetField 表示按某些字段进行分类是普通分类  结果为:
	 *   country_s
			美国:6
			中国:5
	    sq.addFacetQuery("age_i:[1 TO 20]");
		sq.addFacetQuery("age_i:[21 TO 50]");
		sq.addFacetQuery("age_i:[51 TO *]");
	    可以将多个范围值 添加到FacetQuery可以获取到这些Query的统计数量 比如
		 {age_i:[1 TO 20]=3, age_i:[20 TO 50]=5, age_i:[50 TO *]=5}	
		其他 参考 https://wiki.apache.org/solr/SimpleFacetParameters#facet.query_:_Arbitrary_Query_Faceting	
	 */
	@Test
	public void queryFacet() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		SolrQuery sq=new SolrQuery();
		
		sq.setFacet(true);
		//按字段分类 相同的归于一类
		sq.addFacetField("country_s");
		//特殊分类 添加范围
		sq.addFacetQuery("age_i:[1 TO 20]");
		sq.addFacetQuery("age_i:[21 TO 50]");
		sq.addFacetQuery("age_i:[51 TO *]");
		//这只facet字段分类的前缀
		sq.setFacetPrefix("");
		//根据 count 数量 升序和降序 也可以根据索引 
		sq.setFacetSort("count asc");
		sq.setQuery("*:*");
		QueryResponse qr=csc.query(sq);
		List<FacetField> ff=qr.getFacetFields();
		//获取到范围分类的对应统计数量
		System.out.println(qr.getFacetQuery());
		//获取到根据字段分类的对应统计数量
		for(FacetField ftmp:ff){
			System.out.println(ftmp.getName());
			List<Count> cou=ftmp.getValues();
			for (Count count : cou){
	            System.out.println(count.getName()+":"+ count.getCount());
	        }
		}
		csc.commit();
		csc.close();
	}
	
	/**
	 * Facet  参考https://wiki.apache.org/solr/SimpleFacetParameters#Pivot_.28ie_Decision_Tree.29_Faceting
	 *  可以按照多维度来进行分类  
	 *    比如按照国家分类后  再按照省份分类(国家和省份字段不要使用中文分词器 否则分类被拆成很多类别)
	 *    
	 *  结果一般为:
	 *    美国6:
	 *      加利福尼亚州3  
	 *      德克萨斯州3
	 *    中国5:
	 *      湖南省3
	 *      广东省2

	 */
	@Test
	public void queryFacetPivot() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		SolrQuery sq=new SolrQuery();
		sq.setFacet(true);
		//按国家和省份进行二维分类  同一个字符串使用,隔开
		sq.addFacetPivotField("country_s,provice_s");
		sq.setQuery("*:*");
		QueryResponse qr=csc.query(sq,SolrRequest.METHOD.POST);
		NamedList<List<PivotField>> ff=qr.getFacetPivot();
		//获取到根据字段分类的对应统计数量
		for(Map.Entry<String,List<PivotField>> me:ff){
			List<PivotField> lpf=me.getValue();
			for(PivotField pf:lpf){
				System.out.println("一级分类:"+pf.getValue()+pf.getCount()+"---->");
				List<PivotField> clpf=pf.getPivot();
				for(PivotField cpf:clpf){
					System.out.println("二级分类:"+cpf.getValue()+cpf.getCount());
				}
			}
		}
		csc.commit();
		csc.close();
	}
	/**
	 * 分组是分类的升级 同时可以获取到分组下的一部分元素(https://cwiki.apache.org/confluence/display/solr/Result+Grouping)
	   分组的字段的数据如果是集群环境 要求数据被写入到一个分片中 否则无法分组查询
	 */
	@Test
	public void queryGroup() throws IOException, SolrServerException{
		CloudSolrClient csc=getCloudSolrClient();
		SolrQuery sq=new SolrQuery();
		//sq.setParam("shards.tolerant", true);
		sq.setParam(GroupParams.GROUP,true);
		sq.setParam(GroupParams.GROUP_FIELD, "country_s");
		sq.setParam("group.ngroups", true);
		//sq.setParam(GroupParams.GROUP_LIMIT, "5");
		sq.setQuery("*:*");
		sq.setRows(10);
		QueryResponse qr=csc.query(sq);
		GroupResponse ff=qr.getGroupResponse();
		
		for(GroupCommand me:ff.getValues()){
			System.out.println(me.getName());
			 List<Group> groups=me.getValues();
			 System.out.println(groups);
			
		}
		csc.commit();
		csc.close();
	}
}

六。springboot集成solr

添加springboot的maven依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-solr</artifactId>
		</dependency>
	</dependencies>

添加实体类 

@SolrDocument(solrCoreName="mycore")
public class News {

	@Id
	@Field
	private String id;
	@Field(value="title_ik")
	private String title ;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
}

设置solr的主机信息 (application.yml) 

spring: 
  data: 
    solr: 
      host: http://192.168.1.238:8983/solr
server: 
  port: 8888      

springboot提供了基于spring jpa类似的语法操作solr 比如

package cn.ps.dao;

import java.util.List;

import org.springframework.data.repository.query.Param;
import org.springframework.data.solr.repository.Query;
import org.springframework.data.solr.repository.SolrCrudRepository;

import cn.ps.entity.News;

public interface NewDao extends SolrCrudRepository<News, String> {
	/**
	 * 名字的规范 可以使用 findByTitle表示按照title来查 等价于  title:?0
	 *   比如findByNameAndPopularity 就是两个条件组合来查 name:?0  AND Popularity:?1
	 *   具体比如范围 大小 in等参考 springdatasolr  query-methods章节
	 *   https://docs.spring.io/spring-data/solr/docs/2.1.14.RELEASE/reference/html/#solr.query-methods
	 * @param title
	 * @return
	 */
	public List<News> findByTitle(String title);
	
	/**
	 * 如果不是特定的方法名可以使用 Query中指定查询条件 
	 * @Query中可以制定filters 查看的列 fields
	 * @param title
	 * @return
	 */
	@Query(value="title_ik:?0")
	public List<News> queryT(@Param("title")String title);
}

同时提供 SolrTemlate直接操作底层的solrj相关api
在配置类 实例化 
 

@Bean
	 public SolrTemplate solrTemplate(SolrClient client) {
	    return new SolrTemplate(client);
	 }

控制层装配 并使用

@Autowired
	private SolrTemplate solrTemplate;
	@Autowired
	private NewDao newDao;
	
	@RequestMapping("/query")
	public Iterable<News> query() {
		Iterable<News> findAll = newDao.findAll();
		return findAll;
	}
	
	
	@RequestMapping("/queryAnno")
	public List<News> queryAnno(String title) {
		return newDao.queryT(title);
	}
	
	@RequestMapping("/queryHign")
	public List<News> queryHign(String title) {
		SimpleHighlightQuery query = new SimpleHighlightQuery(new SimpleStringCriteria("title_ik:"+title));
		HighlightOptions highlightOptions = new HighlightOptions();
		highlightOptions.setSimplePrefix("<font color=red>");
		highlightOptions.setSimplePostfix("</font>");
		query.setHighlightOptions(highlightOptions);
		HighlightPage<News> page1 = solrTemplate.queryForHighlightPage(query, News.class);
		//�����б� Ҫ�
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
# 学习对象在全民造车、造芯的大时代,在努力去解决卡脖子的时代,ASIC硬件、SOC底层软件、Linux Kernel等操作系统软件(内核/驱动)、软硬件方面的系统架构师等的岗位需求也越来越明显,社会一直都是非常缺人的,缺的是核心的那一小撮、领头的那一小撮,社会所缺的更是能够软硬件融合的那一小撮人……总之,要想在这个时代,站稳自己的脚跟,能够在大公司或行业上拥有一席之地,就必需深入学习底层技术原理,核心技术才是您的看家本领。本课程设计之初,主要针对SOC底层软件开发的者、系统开发者,或者励志成为这样的人。既适合资深/高级工程师来查缺补漏,又适合初级工程师入门。(理论上该课程和ASIC硬件电路设计无关,该课程偏软件,但事实购买该课程的做ASIC的同学已然超过了15%)适用人群1、芯片开发者(包括底层软件、或做ASIC硬件的)。不限行业,例如车、云、物联网、移动端等领域;2、汽车行业开发者(主机厂、tier1、SOC厂家、各级供应商);3、嵌入式开发者、kernel开发者、驱动、软件工程师;4、学生。既适合学生从入门到精通,也适合资深工程师查缺补漏;您的收益:1、全体系的掌握ARMv8/ARMv9的核心知识点(ARM基础、异常中断GIC、MMU/Cache、architecture...);2、掌握ARM架构、掌握SOC架构、掌握常规IP(gic、smmu、timer、AXI/ACE/CHI、TZC400...);3、快速熟悉常规系统软件(bootrom、spl、ATF、TEE、bootloader、kernel...), Secureboot安全启动...4、技术水平提升N个level, 掌握快速的学习方法;# 学习什么在ARM蓬勃发展的年代,不仅仅涉及到物联网IOT、移动领域(如手机)、汽车电子领域,现在还涉及到PC、服务器的,简直就是各行各业。ARMv8出来已经有10年了,ARMv9也2年时间了。在技术不断更新迭代的背景下,此时再去学习十五年前的ARMv7、二十年前的ARMv5/v6显然不是明智的选择。本课程主要基于当前最新的架构ARMv8的aarch64和ARMv9,如涉及具体的ARM Core IP主要还是以最新的ARM Core IP为主,软件架构也是以当前最主流的/未来所趋势的架构来讲解。以下也给大家列举初了一个ARM产品的timeline的总结(在本课程中有着大量的这种总结),从这张图中,您是可以清晰的看到本课程拥有独具一格的风格、拥有全网最新(且唯一)的资料总结或学习路线。# 本课程大纲和规划(课程持续更新中,课程总量统计:2022/10/02  当前是 61节课, 22小时)第一章:主要是快速学习: ARM简介、指令集、寄存器总结等。第二章:本系列视频的一大亮点,系统全面地讲解了arm异常中断gic等相关的软硬件知识,本人一直在倡导“学arm安全其实就是学arm架构,学arm架构其实就是学习arm的异常和中断”,异常中断是领着你进入架构的入门,是让你变成系统软硬件架构师的必走之路。第三章:安全专题,这也是本视频最核心的东西。因为你无论买书还是看博客等,你都很难找到讲解安全的教程,这里就是有和无的区别。本人系统的整理的安全的知识,带领你快速入门。第四章:mmu专题,透过事务看本质的讲解,白话式的演讲。在所有模块中,mmu也算是相对较简单模块。相信人人听得懂,人人学得会。第五章:cache专题,一切追求实事求是,不人云亦云,一切知识点都有迹可循,推翻了网络的很多观念。在众多模块中,cache算是一个比较难的模块。了解了cache后,才能算真正了解系统的软硬件架构。第六章:虚拟化,本人不擅长,会啥就随便讲点啥。(以后学会了再来补)第七章:architecture,就是零散和零碎的系统架构知识,如exclusive、arch timer、reset、系统启动、SOC设计、AMBA/AXI/ACE、DSU、WFE/WFI这样的。第八章: 新增的ARMv9 CCA/RME安全架构专题第九章:主要放置一些直播课。# 课程收益1、知道我学习什么,我要怎么去学习,从此之后有了一个明确的学习路线。2、认识一些共同目标的人,相互讨论问题,共同进步。勤学、共学、助学。3、ARM不再神秘,SOC不在神秘,让您短期内就能cover住全局4、熟悉ARM Architecture架构知识5、熟悉SOC架构知识6、熟悉主流的系统软件框架7、熟悉各项硬件原理和机制,如异常中断、MMU、cache、TLB、VMSA、Trustzone6、深入了解当前的系统架构、软硬件架构,能够看懂这些大家,将来也能够自己设计。7、熟悉系统的启动流程、Secureboot等8、熟悉各类标准和规范9、能够进入芯片厂商干活、能够在非芯片产生成为技术担当。10、学习资料的获取方法,会看11500多页的ARM手册,会看数以百计的ARM各项参考手册。 本课程会持续更新。也希望通过本课程的学习,能够让大家的ARMv8/ARMv9开发技术能有质的飞越,能找到自己心仪的工作。在购买之前,也建议大家看一看第一章第一节的课程介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值