solr中如何定义自己的解析器插件(QParserPlugin)

By | 05月15日
Advertisement
/*****************************************************/
>solr中如何定义自己的解析器插件
/*****************************************************/  

0.为什么要自定义自己的解析器插件
/*****************************************************/
// 因为solr默认的LuceneQParserPlugin解析器插件是不支持很多高级查询的如
// SpanQuery,MoreLikeThis等,如果要使用这些特性就必须自定的解析器插件。
/*****************************************************/

1.显示自定义解析器插件的方法
/*****************************************************/
//定义的插件必须继承自org.apache.solr.search.QParserPlugin;
public class MyParserPlugin extends QParserPlugin {
    @Override
    public void init(NamedList args) {
    }

    @Override
    public QParser createParser(String qstr, SolrParams localParams,
            SolrParams params, SolrQueryRequest req) {
        //创建出内部类就可以了
        //return new MyParserPlugin.MyParser(qstr, localParams, params, req);
    }

    //继承自org.apache.solr.search.Qparser;
    private class MyParser extends QParser{
        public MyParser(String qstr, SolrParams localParams, SolrParams params,
                SolrQueryRequest req) {
            super(qstr, localParams, params, req);
        }
        @Override
        public Query parse() throws ParseException {
            //返回org.apache.lucene.search.Query;就可以了其他的不用管
            return null;
        }
    }
}

//如何配置自定义的解析器插件
// <queryParser name="simple" class="com.zyb.javacc.QParserPlugin" /> \
// 然后在查询的时候需要指定defType这个属性进行指定是哪个解析器来解析你的检索式
// 如:http://localhost:8080/testSolr/select/q=*:*&defType=simple;
// 或者也可以在指定的查询处理器中默认的指定也是可以的
//<requestHandler name="search" class="solr.SearchHandler" default="true">
// <lst name="defaults">
//   <str name="defType">simple</str>  //通过在这里指定后就不用在Url后面显示的指定
//   <str name="echoParams">explicit</str>
//   <int name="rows">10</int>
// </lst>
//</requestHandler>
/*****************************************************/  

2.原理说明
/*****************************************************/
public abstract class QParserPlugin implements NamedListInitializedPlugin {
  /** internal use - name of the default parser */
  public static String DEFAULT_QTYPE = LuceneQParserPlugin.NAME;//solr默认的解析器插件

  /** internal use - name to class mappings of builtin parsers */
  //solr中存在的查询解析器插件
  public static final Object[] standardPlugins = {
   // LuceneQParserPlugin.NAME, LuceneQParserPlugin.class,
    OldLuceneQParserPlugin.NAME, OldLuceneQParserPlugin.class,
    FunctionQParserPlugin.NAME, FunctionQParserPlugin.class,
    PrefixQParserPlugin.NAME, PrefixQParserPlugin.class,
    BoostQParserPlugin.NAME, BoostQParserPlugin.class,
    DisMaxQParserPlugin.NAME, DisMaxQParserPlugin.class,
    ExtendedDismaxQParserPlugin.NAME, ExtendedDismaxQParserPlugin.class,
    FieldQParserPlugin.NAME, FieldQParserPlugin.class,
    RawQParserPlugin.NAME, RawQParserPlugin.class,
    TermQParserPlugin.NAME, TermQParserPlugin.class,
    NestedQParserPlugin.NAME, NestedQParserPlugin.class,
    FunctionRangeQParserPlugin.NAME, FunctionRangeQParserPlugin.class,
    SpatialFilterQParserPlugin.NAME, SpatialFilterQParserPlugin.class,
    SpatialBoxQParserPlugin.NAME, SpatialBoxQParserPlugin.class,
  };

  /** return a {@link QParser} */
  public abstract QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req);

//采用lucene解析器的实现
public class LuceneQParserPlugin extends QParserPlugin {
  public static String NAME = "lucene";

  public void init(NamedList args) {
  }

  @Override
  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
   // return new LuceneQParser(qstr, localParams, params, req);
  }
}

//继承自QParser会带来很多好处因为他已经帮我们实现了部分我们不需要实现的东西我们只需要关心怎么去生成
//org.apache.lucene.search.Query这个接口的实现类就可以了
//QParser是一个抽象类他可以通过this.qstr得到检索式如*:*等
class LuceneQParser extends QParser {
  String sortStr;
  // SolrQueryParser lparser;//SolrQueryParser还是继承自org.apache.lucene.queryParser;所以还是采用的lucene
              //的原有解析器他是不能够满足做spanQuery,moreLikeThis这样的查询的
              //除非自己写扩展的查询解析器插件才可以实现

  public LuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
    super(qstr, localParams, params, req);
  }

  //查询解析器的目的就是为了返回一个org.apache.lucene.search.Query的实现类就可以了
  //那么这就是和solr无关的东西了只要构造了Query的实现类的对象就可以顺利的做任何类型的查询
  @Override
  public Query parse() throws ParseException {
    String qstr = getString();

    String defaultField = getParam(CommonParams.DF);
    if (defaultField==null) {
      defaultField = getReq().getSchema().getDefaultSearchFieldName();
    }
    lparser = new SolrQueryParser(this, defaultField);

    // these could either be checked & set here, or in the SolrQueryParser constructor
    String opParam = getParam(QueryParsing.OP);
    if (opParam != null) {
      lparser.setDefaultOperator("AND".equals(opParam) ? QueryParser.Operator.AND : QueryParser.Operator.OR);
    } else {
      // try to get default operator from schema
      QueryParser.Operator operator = getReq().getSchema().getSolrQueryParser(null).getDefaultOperator();
      lparser.setDefaultOperator(null == operator ? QueryParser.Operator.OR : operator);
    }

    return lparser.parse(qstr);
  }

  @Override
  public String[] getDefaultHighlightFields() {
    return new String[]{lparser.getField()};
  }

}

//这里继承好像没什么用处
class OldLuceneQParser extends LuceneQParser {
  String sortStr;

  public OldLuceneQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
    super(qstr, localParams, params, req);
  }

  @Override
  public Query parse() throws ParseException {
    // handle legacy "query;sort" syntax
    if (getLocalParams() == null) {
      String qstr = getString();
      sortStr = getParams().get(CommonParams.SORT);
      if (sortStr == null) {
        // sort may be legacy form, included in the query string
        List<String> commands = StrUtils.splitSmart(qstr,';');
        if (commands.size() == 2) {
          qstr = commands.get(0);
          sortStr = commands.get(1);
        } else if (commands.size() == 1) {
          // This is need to support the case where someone sends: "q=query;"
          qstr = commands.get(0);
        }
        else if (commands.size() > 2) {
          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "If you want to use multiple ';' in the query, use the 'sort' param.");
        }
      }
      setString(qstr);
    }

    return super.parse();
  }

  @Override
  public SortSpec getSort(boolean useGlobal) throws ParseException {
    SortSpec sort = super.getSort(useGlobal);
    if (sortStr != null && sortStr.length()>0 && sort.getSort()==null) {
      Sort oldSort = QueryParsing.parseSort(sortStr, getReq());
      if( oldSort != null ) {
        sort.sort = oldSort;
      }
    }
    return sort;
  }

}
}
/*****************************************************/  

3.solr查询内部实现
/*****************************************************/
  [email protected]
  public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception, ParseException, InstantiationException, IllegalAccessException
  {
    // int sleep = req.getParams().getInt("sleep",0);
    // if (sleep > 0) {log.error("SLEEPING for " + sleep);  Thread.sleep(sleep);}
    //ResponseBuilder rb = new ResponseBuilder();//在查询的时候最重要的对象
    rb.req = req;
    rb.rsp = rsp;
    rb.components = components;
    rb.setDebug(req.getParams().getBool(CommonParams.DEBUG_QUERY, false));

    final RTimer timer = rb.isDebug() ? new RTimer() : null;

    if (timer == null) {
      // non-debugging prepare phase
      //for( SearchComponent c : components ) {
       // c.prepare(rb);//准备查询组件
      //}
    } else {
      // debugging prepare phase
      RTimer subt = timer.sub( "prepare" );
     // for( SearchComponent c : components ) {
      //  rb.setTimer( subt.sub( c.getName() ) );
        //c.prepare(rb);
      //  rb.getTimer().stop();
     // }
      subt.stop();
    }

    if (rb.shards == null) {
      // a normal non-distributed request

      // The semantics of debugging vs not debugging are different enough that
      // it makes sense to have two control loops
      if(!rb.isDebug()) {
        // Process 根据组件处理查询请求
        //for( SearchComponent c : components ) {
         // c.process(rb);
       // }
      }
      else {
        // Process
        RTimer subt = timer.sub( "process" );
        //for( SearchComponent c : components ) {
        //  rb.setTimer( subt.sub( c.getName() ) );
        //  c.process(rb);
        //  rb.getTimer().stop();
        //}
        subt.stop();
        timer.stop();

        // add the timing info
        if( rb.getDebugInfo() == null ) {
          rb.setDebugInfo( new SimpleOrderedMap<Object>() );
        }
        rb.getDebugInfo().add( "timing", timer.asNamedList() );
      }

    } else {
      // a distributed request

      HttpCommComponent comm = new HttpCommComponent();

      if (rb.outgoing == null) {
        rb.outgoing = new LinkedList<ShardRequest>();
      }
      rb.finished = new ArrayList<ShardRequest>();

      int nextStage = 0;
      do {
        rb.stage = nextStage;
        nextStage = ResponseBuilder.STAGE_DONE;

        // call all components
        for( SearchComponent c : components ) {
          // the next stage is the minimum of what all components report
          nextStage = Math.min(nextStage, c.distributedProcess(rb));
        }

        // check the outgoing queue and send requests
        while (rb.outgoing.size() > 0) {

          // submit all current request tasks at once
          while (rb.outgoing.size() > 0) {
            ShardRequest sreq = rb.outgoing.remove(0);
            sreq.actualShards = sreq.shards;
            if (sreq.actualShards==ShardRequest.ALL_SHARDS) {
              sreq.actualShards = rb.shards;
            }
            sreq.responses = new ArrayList<ShardResponse>();

            // TODO: map from shard to address[]
            for (String shard : sreq.actualShards) {
              ModifiableSolrParams params = new ModifiableSolrParams(sreq.params);
              params.remove(ShardParams.SHARDS);      // not a top-level request
              params.remove("indent");
              params.remove(CommonParams.HEADER_ECHO_PARAMS);
              params.set(ShardParams.IS_SHARD, true);  // a sub (shard) request
              String shardHandler = req.getParams().get(ShardParams.SHARDS_QT);
              if (shardHandler == null) {
                params.remove(CommonParams.QT);
              } else {
                params.set(CommonParams.QT, shardHandler);
              }
              comm.submit(sreq, shard, params);
            }
          }

          // now wait for replies, but if anyone puts more requests on
          // the outgoing queue, send them out immediately (by exiting
          // this loop)
          while (rb.outgoing.size() == 0) {
            ShardResponse srsp = comm.takeCompletedOrError();
            if (srsp == null) break;  // no more requests to wait for

            // Was there an exception?  If so, abort everything and
            // rethrow
            if (srsp.getException() != null) {
              comm.cancelAll();
              if (srsp.getException() instanceof SolrException) {
                throw (SolrException)srsp.getException();
              } else {
                throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, srsp.getException());
              }
            }

            rb.finished.add(srsp.getShardRequest());

            // let the components see the responses to the request
            for(SearchComponent c : components) {
              c.handleResponses(rb, srsp.getShardRequest());
            }
          }
        }

        for(SearchComponent c : components) {
            c.finishStage(rb);
         }

        // we are done when the next stage is MAX_VALUE
      } while (nextStage != Integer.MAX_VALUE);
    }
  }

org.apache.solr.handler.component.QueryComponent.public void prepare(ResponseBuilder rb) throws IOException
  {
    //ResponseBuilder在查询的时候是在关键的对象
    SolrQueryRequest req = rb.req;
    //现在的参数样子
    //{params(indent=on&start=0&q=*:*&version=2.2&rows=10),
    // defaults(echoParams=explicit&rows=10&defType=simple)}
    // defaults表示在requestHandler节点中定义的默认参数

    SolrParams params = req.getParams();
    if (!params.getBool(COMPONENT_NAME, true)) {
      return;
    }
    SolrQueryResponse rsp = rb.rsp;

    // Set field flags
    String fl = params.get(CommonParams.FL);
    int fieldFlags = 0;
    if (fl != null) {
      fieldFlags |= SolrPluginUtils.setReturnFields(fl, rsp);
    }
    rb.setFieldFlags( fieldFlags );

    //String defType = params.get(QueryParsing.DEFTYPE,QParserPlugin.DEFAULT_QTYPE);

    if (rb.getQueryString() == null) {
      rb.setQueryString( params.get( CommonParams.Q ) );
    }

    try {
      //根据defType得到自定的解析插件对象
      //QParser parser = QParser.getParser(rb.getQueryString(), defType, req);
      //rb.setQuery( parser.getQuery() );//通过getQuery()然后调用parse(str)
      //返回生成查询对象,关键在于怎么写
      //rb.setSortSpec( parser.getSort(true) );
      //rb.setQparser(parser);

      String[] fqs = req.getParams().getParams(CommonParams.FQ);
      if (fqs!=null && fqs.length!=0) {
        List<Query> filters = rb.getFilters();
        if (filters==null) {
          filters = new ArrayList<Query>(fqs.length);
        }
        for (String fq : fqs) {
          if (fq != null && fq.trim().length()!=0) {
            QParser fqp = QParser.getParser(fq, null, req);
            filters.add(fqp.getQuery());
          }
        }
        // only set the filters if they are not empty otherwise
        // fq=&someotherParam= will trigger all docs filter for every request
        // if filter cache is disabled
        if (!filters.isEmpty()) {
          rb.setFilters( filters );
        }
      }
    } catch (ParseException e) {
      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
    }

    // TODO: temporary... this should go in a different component.
    //如果有分布式查询就得到shards参数通过,进行切割
    //String shards = params.get(ShardParams.SHARDS);
    //if (shards != null) {
     // List<String> lst = StrUtils.splitSmart(shards, ",", true);
     // rb.shards = lst.toArray(new String[lst.size()]);
    //}
    //String shards_rows = params.get(ShardParams.SHARDS_ROWS);
    //if(shards_rows != null) {
      //rb.shards_rows = Integer.parseInt(shards_rows);
    //}
    //String shards_start = params.get(ShardParams.SHARDS_START);
    //if(shards_start != null) {
      //rb.shards_start = Integer.parseInt(shards_start);
    //}

    boolean grouping = params.getBool(GroupParams.GROUP, false);
    if (!grouping) {
      return;
    }
    SolrIndexSearcher.QueryCommand cmd = rb.getQueryCommand();
    SolrIndexSearcher searcher = rb.req.getSearcher();
    GroupingSpecification groupingSpec = new GroupingSpecification();
    rb.setGroupingSpec(groupingSpec);

    //TODO: move weighting of sort
    Sort groupSort = searcher.weightSort(cmd.getSort());
    // groupSort defaults to sort
    String groupSortStr = params.get(GroupParams.GROUP_SORT);
    if (groupSort == null) {
      groupSort = new Sort();
    }
    //TODO: move weighting of sort
    Sort sortWithinGroup = groupSortStr == null ?  groupSort : searcher.weightSort(QueryParsing.parseSort(groupSortStr, req));
    groupingSpec.setSortWithinGroup(sortWithinGroup);
    groupingSpec.setGroupSort(groupSort);

    String formatStr = params.get(GroupParams.GROUP_FORMAT, Grouping.Format.grouped.name());
    Grouping.Format responseFormat;
    try {
       responseFormat = Grouping.Format.valueOf(formatStr);
    } catch (IllegalArgumentException e) {
      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, String.format("Illegal %s parameter", GroupParams.GROUP_FORMAT));
    }
    groupingSpec.setResponseFormat(responseFormat);

    groupingSpec.setFields(params.getParams(GroupParams.GROUP_FIELD));
    groupingSpec.setQueries(params.getParams(GroupParams.GROUP_QUERY));
    groupingSpec.setGroupOffset(params.getInt(GroupParams.GROUP_OFFSET, 0));
    groupingSpec.setGroupLimit(params.getInt(GroupParams.GROUP_LIMIT, 1));
    groupingSpec.setOffset(rb.getSortSpec().getOffset());
    groupingSpec.setLimit(rb.getSortSpec().getCount());
    groupingSpec.setIncludeGroupCount(params.getBool(GroupParams.GROUP_TOTAL_COUNT, false));
    groupingSpec.setMain(params.getBool(GroupParams.GROUP_MAIN, false));
    groupingSpec.setNeedScore((cmd.getFlags() & SolrIndexSearcher.GET_SCORES) != 0);
    groupingSpec.setTruncateGroups(params.getBool(GroupParams.GROUP_TRUNCATE, false));
  }
/*****************************************************/

Similar Posts:

  • 设计模式之禅之设计模式-解析器模式

    一:解析器模式定义 --->解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的方案,在现在项目中使用较少 --->给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子 二:解析器模式角色 ● AbstractExpression--抽象解释器 具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和Non-terminalExpression完成. ● TerminalExpression--

  • ivy教程(4)-多解析器-skydream-iteye技术网站

    ivy教程(4)-多解析器-skydream-iteye技术网站 2011年06月01日 这个例子演示模块是如何被多解析器获得的.使用多解析器在很多情况下是非常有用的,这里是一些例子: * 来自发行的单独的集成构建 * 为第三方模块使用公共仓库并且为内部模块使用私有仓库 * 使用一个仓库来存储那些在无法管理的公共仓库里里面的不清晰的模块 * 使用本地仓库来暴露在一个开发人员的位置上生成的构建 在ivy中,多解析器的使用是通过一个名为解析器链的复合解析器来支持的. 在我们的例子中,我们将简单的展示

  • springmvc解析器使用总结

    转载于:点击打开链接 </pre><span style="font-size:18px;">一.序言</span><p></p><p><span style="white-space:pre"></span><span style="font-size:12px">springmvc中,请求在Controller中处理完成后,通常会将处理

  • boost之词法解析器spirit

    摘要:解析器就是编译原理中的语言的词法分析器,可以按照文法规则提取字符或者单词.功能:接受扫描器的输入,并根据语法规则对输入流进行匹配,匹配成功后执行语义动作,进行输入数据的处理. C++ 程序员需要面对的最复杂的任务之一就是在一段合理的时间期限内编写一个解析器.在为 SQL 或 C++ 这类成熟的语言开发编译器时,使用 GNU Flex/Bison 或 ANTLR 解析器生成程序通常是不错的选择:但是对于使用更简单的 Backus Naur Form(BNF)的语法,这些工具陡峭的学习曲线并不

  • 37_XML DOM 解析器错误

    XML DOM 解析器错误 当 Firefox 遇到解析器错误,它会载入一个包含错误的 XML 文档. 在 Firefox 中的解析器错误 在您试图打开一个 XML 文档时,就可能发生一个解析器错误(parser-error). 与 Internet Explorer 浏览器不同,如果 Firefox 遇到错误,它会载入包含错误描述的 XML 文档中. XML 错误文档的根节点的名称是 "parsererror".这是用来检查是否有错误. XML 错误(XML Error) 在下面的代

  • Java开源的支持xpath的html解析器介绍--JsoupXpath

    JsoupXpath (https://github.com/zhegexiaohuozi/JsoupXpath)是一款纯Java开发的使用xpath解析提取html内容的解析器,xpath语法分析与执行完全独立,html的DOM树生成借助Jsoup,故命名为JsoupXpath. 为了在java里也享受xpath的强大与方便但又苦于找不到一款足够强大的xpath解析器,故开发了JsoupXpath.JsoupXpath的实现逻辑清晰,扩展方便, 支持几乎全部常用的xpath语法,如下面这些:

  • XML的四种解析器(dom,sax,jdom,dom4j)原理及性能比较[收藏]

    1.分析 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作.由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的.DOM以及广义的基于树的处理具有几个优点.首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改.它还可以在任何时候在树中上下导航,而不是

  • Solr解析器通用的查询参数

    本文主要介绍下Solr解析器中通用的查询参数.这些参数能够在标准查询解析器.DisMax查询解析器及eDisMax查询解析器中通用. 下表总结了Solr通用的查询参数,支持标准的.DisMax.eDisMax查询请求. 参数 描述 defType 选择用来处理查询的查询分析器. sort 根据响应的分数或另一个指定的特性对查询的响应进行正序或倒序排序. start 指定Solr查询响应展示内容的偏移量(默认0). rows 控制一次返回多少条数据(默认10) fq 对查询结果指定一个查询过滤器

  • Android中使用Pull解析器解析xml文件+进行简单的单元测试

    Android中使用Pull解析器解析xml文件+进行简单的单元测试 我来给大家介绍Android用得最多的一种解析xml数据的技术:Pull解析器解析,之前介绍过Java中两种解析xml的技术:Dom解析和SAX解析,Dom比较适合小数据量的解析,然而SAX适合大数据量的解析工作,但比较复杂.第三种解析技术Pull解析,是一种比前面两种方法更加简单的解析技术,更加符合面向对象的思想,因为这是按照我们正常的思考方式进行解析,下面来看看利用这种解析器的实现过程. 首先我们需要知道这个xml文件的内

  • AutoMapper在MVC中的运用03-字典集合、枚举映射,自定义解析器

    本篇AutoMapper使用场景: ※ 源字典集合转换成目标字典集合 ※ 枚举映射 ※ 自定义解析器 ※ 源中的复杂属性和Get...方法转换成目标属性 源字典集合转换成目标字典集合 □ Domain model public class SourceValue { public int Value { get; set; } } □ View model public class DestValue { public int Value { get; set; } } □ 映射配置 Mappe

Tags: