zoukankan      html  css  js  c++  java
  • 企业级搜索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.4serversolr-webapp下的webapp 拷贝到tomcatwebapps目录下 改名为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

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

        <env-entry>
           <env-entry-name>solr/home</env-entry-name>
           <env-entry-value>D:learnsolr-5.5.4home</env-entry-value>
           <env-entry-type>java.lang.String</env-entry-type>
        </env-entry>
       找到solr解压包/server/solr目录拷贝所有文件到 以上web.xml指定的路径D:learnsolr-5.5.4home下 重启tomcat 访问

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

      

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

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

       拷贝 solr解压包下serversolrconfigsetsasic_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 插入数据即可实现中文分词  通过某些中文关键字搜索

    三。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:learnsolr-5.5.4home
    tomcat28081D:learnsolr-5.5.4home1
    tomcat38082D:learnsolr-5.5.4home2

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

                solr解压包下serversolrconfigsetsasic_configs到新建目录D:learnsolr-5.5.4home(只拷贝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();
    	}
    }
    



  • 相关阅读:
    我从0开始开发了一个LDAP服务。
    C#开发中常用的小功能
    webapi swagger 报错 路由集合中已存在名为“swagger_docsswagger/docs/{apiVersion}”的路由。路由名称必须唯一
    h5 web vlc 播放rtsp流
    Docker的基础概念与在window10下的安装
    .Net Core JWT 动态设置接口与权限
    .Net Core官方的 JWT 授权验证
    IdentityServer4中文文档
    中介者模式及在NetCore中的使用MediatR来实现
    .Net Core 使用 FluentValidation
  • 原文地址:https://www.cnblogs.com/liaomin416100569/p/9331196.html
Copyright © 2011-2022 走看看