`
粟谷_sugu
  • 浏览: 25417 次
社区版块
存档分类
最新评论

solr的Nested Doc的详解及应用

    博客分类:
  • solr
阅读更多
        nested doc,是solr提供的一种父子文档嵌套的结构,但是由于在lucene中,所有文档的存储都是扁平结构的,所以嵌套只是逻辑上的说法,在物理存储中,父子嵌套是根据所有相关联的父子文档紧密排列,并且按照 子->子->父 的顺序排序,每个区块都必须父作为结尾。

如何添加nested doc结构的索引?

        直接上代码
        SolrInputDocument doc = new SolrInputDocument();
        doc.setField("id",1);
        doc.setField("author","D'angelo");
        doc.setField("type","p");
        SolrInputDocument c = new SolrInputDocument();
        c.setField("id",2);
        c.setField("title","solr in action");
        c.setField("comments","bravo");
        c.setField("type","c");
       //**将c添加为doc的子文档*/
        doc.addChildDocument(c);
        SolrInputDocument doc2 = new SolrInputDocument();
        doc2.setField("id",3);
        doc2.setField("author","Russell");
        doc2.setField("type","p");
        SolrInputDocument c2 = new SolrInputDocument();
        c2.setField("id",4);
        c2.setField("title","java in action");
        c2.setField("comments","good");
        c2.setField("type","c");
        doc2.addChildDocument(c2);


        则在solr查询时,我们可以利用{!parent}的queryParser查询方法,来通过子文档的属性来反查父文档,
/**Usage: {!parent which="PARENT:true"}CHILD_PRICE:10*/
query:
{!parent
which=type:p}title:"java in action"


"response": {
    "numFound": 1,
    "start": 0,
    "docs": [
      {
        "id": "3",
        "author": "Russell",
        "author_s": "Russell",
        "type": "p",
        "_version_": 1601161242996637700,
        "_root_": "3",
        "_childDocuments_": [
          {
            "id": "4",
            "title": "java in action",
            "comments": "good",
            "type": "c",
            "_root_": "3"
          }
        ]
      }
    ]
  }

        若想同时得到父子文档,则设置
fl:*,[child parentFilter=type:p]

        以上就是nested doc查询的基本应用,下面介绍一下在近期工作中用到nested doc的一些稍微复杂一点的操作。

业务需求

        目前有一批顾客信息和商店信息的一对多关系的数据,顾客信息包括该顾客历史吃过的所有的菜,商店信息为商店的坐标点以及该顾客在该商店中的吃过的菜
        基于以上数据,业务方想要通过两个查询条件,得到满足要求的顾客

       
  • 1:顾客以前吃过某几个特定的菜
  • 2:顾客曾光临过给定坐标点的附近某个距离范围内的店铺

        基于业务需求,很明显需要用到上面介绍的nested doc的结构,
但是如何查询得到我们想要的结果呢,很明显要借鉴{!parent}的查询方式
所以先看一下这个queryParser的源代码

public class BlockJoinParentQParserPlugin extends QParserPlugin {
  public static final String NAME = "parent";

  @Override
  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
    QParser parser = createBJQParser(qstr, localParams, params, req);
    return parser;
  }

  protected QParser createBJQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
    return new BlockJoinParentQParser(qstr, localParams, params, req);
  }
}

        发现真正起作用的是返回的BlockJoinParentQParser,再看一下这个的源代码
protected String getParentFilterLocalParamName() {
        return "which";
    }

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

    public Query parse() throws SyntaxError {
        String filter = this.localParams.get(this.getParentFilterLocalParamName());
        String scoreMode = this.localParams.get("score", ScoreMode.None.name());
        QParser parentParser = this.subQuery(filter, (String)null);
        Query parentQ = parentParser.getQuery();
        String queryText = this.localParams.get("v");
        if(queryText != null && queryText.length() != 0) {
            QParser childrenParser = this.subQuery(queryText, (String)null);
            Query childrenQuery = childrenParser.getQuery();
            return this.createQuery(parentQ, childrenQuery, scoreMode);
        } else {
            SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(this.getFilter(parentQ));
            wrapped.setCache(false);
            return wrapped;
        }
    }

    protected Query createQuery(Query parentList, Query query, String scoreMode) throws SyntaxError {
        //AllParentsAware extends ToParentBlockJoinQuery
        return new BlockJoinParentQParser.AllParentsAware(query, this.getFilter(parentList).filter, ScoreModeParser.parse(scoreMode), parentList);
    }

       

        也就是将which后的语句解析为parentFilter(下方红色部分),将{}外的语句解析为childQuery(下方蓝色部分)
        {!parent which="PARENT:true"}CHILD_PRICE:10       
        再拼装成ToParentBlockJoinQuery的一个Query类型,具体查询时先用parentFilter定位到满足条件的父文档,再根据父子文档物理位置相邻的特性,直接向上移动指针,遍历其子文档,再根据childQuery筛选我们需要的子文档
所以根据我们上述的两个需求,将第一点封装成parentFilter,将第二点封装成childQuery即可,这里的细节不细说了,直接说结果
1:{!terms f=sfield}A,B,C 用于获取吃过ABC三个菜其中之一的客户
2:{!geofilt sfield=coordinate pt=X,Y d=10} 用于获取满足经纬度X,Y范围10km内的店铺
        但是如果直接拼装成如下的语句,在解析时会出现问题,因为中间的空格都会被当做分隔符分开,造成语句的错乱

{!parent which={!terms f=sfield}A,B,C}{!geofilt sfield=coordinate pt=X,Y d=10}

        为了解决这个问题,自己定义一个queryParser,继承BlockJoinParentQParserPlugin,改写其中的createParser方法,
//UserRecommendParentQPlugin extends BlockJoinParentQParserPlugin
@Override
    public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
        String sfield = localParams.get("sfield","coordinate");
        String pt = localParams.get("pt", "0.0,0.0");
        String d = localParams.get("d", "100");
        String terms = localParams.get("termList","");
        String parentQ = "{!terms f=termcount}"+term
        String q = "{!geofilt sfield="+sfield+" pt="+ pt+" d="+d+"}";
        logger.info("child query: "+ q);
        if(localParams instanceof ModifiableSolrParams){
            ((ModifiableSolrParams) localParams).set("which","type:p");
            ((ModifiableSolrParams) localParams).set("v",q);
        }
        QParser parser = createBJQParser(q, localParams, params, req);
        return parser;
    }

即自己在解析时传入拼好的值,则能有效的避免这个问题,在查询时也就只用传入我们需要后续拼接的参数即可
{!userRecommend sfield=xxx pt=xxx d=xxx termList=xxx }userId:123

这样就解决了我们的需求
Problem Solved!
分享到:
评论
1 楼 ZJKAICO 2018-05-23  

相关推荐

    solr环境搭建详解

    solr环境搭建详解

    solr7.5官方文档doc格式

    solr7.5官方文档是pdf格式,经本人转换成doc,可供阅读参考

    Solr入门使用详解.zip

    详尽解释了solr的使用方法,java程序法开发使用solrJ,提供索引和搜索的请求方法J,索引的创建,更新。删除,field的详细使用方法,以及IK分词器的详细使用方式等

    solr开发详解

    solr开发详解

    solr_3.5_配置及应用

    solr_3.5_配置及应用

    solr3.5配置及应用

    初学者学习solr时,可以借助此文档入门,学习solr

    相关性搜索利用Solr与Elasticsearch创建智能应用

    资源名称:相关性搜索 利用Solr与Elasticsearch创建智能应用内容简介:《相关性搜索:利用Solr与Elasticsearch创建智能应用》揭开了相关性搜索的神秘面纱,告诉大家如何将 Elasticsearch与 Solr这样的搜索引擎作为可...

    solr-4.4.0.tgz

    solr-4.4.0的Linux安装包,解压后,按照相关进行部署到tomcat中

    solr开发应用教程

    Solr 3.5开发应用教程,偏实战

    SOLR的应用教程

    3.1.1 Solr的应用模式 29 3.1.2 SOLR的使用过程说明 30 3.2 一个简单的例子 30 3.2.1 Solr Schema 设计 30 3.2.2 构建索引 30 3.2.3 搜索测试 31 3.3 搜索引擎的规划设计 32 3.3.1 定义业务模型 32 3.3.2 定制索引...

    solr-7.7.3配置详解,跟springboot整合 (二)

    solr-7.7.3配置详解,跟springboot整合 (二)

    Solr3.5开发应用指导

    基于Solr3.5的最新开发应用指导,文字加代码说明模式(Schedule.xml)、配置(solrconfig.xml)、索引,搜索等详细开发内容。

    solr7.5官方文档doc加pdf格式

    solr7.5官方文档是pdf格式,经本人转换成doc,pdf和doc同时供阅读参考

    solr教程(配置+集成)

    Solr是一个独立的企业级应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的xml文件, 生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回...

    solr3.5配置及应用[借鉴].pdf

    solr3.5配置及应用[借鉴].pdf

    01.lucene&solr;-day01-v2.0.doc

    lucene和solr笔记

    solr-java运用

    solr配置,部署,定时任务更新,数据源配置, solr_java 运用详解

    springboot整合solr的方法详解

    主要介绍了springboot整合solr的方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    solr5.4.0完整包

    Lucene本质上是搜索库,不是独立的应用程序,而Solr是。Lucene专注于搜索底层的建设,而Solr专注于企业应用。Lucene不负责支撑搜索服务所必须的管理,而Solr负责。所以说,一句话概括 Solr: Solr是Lucene面向企业...

    Solr权威指南-上卷

    本书立足全球视野,综合Solr技术的发展和应用、从业人员的学习曲线,以及中英文资料的供给情况,给自己设定了一个极高的目标:力争在内容的全面性、系统性、深浅度和实战性上概括所有的同类书。从完成的结果上来看,...

Global site tag (gtag.js) - Google Analytics