`
suichangkele
  • 浏览: 200366 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

solr中对于关键字置顶(竞价排名)、拉黑的源码实现已经实例讲解(二)

    博客分类:
  • solr
 
阅读更多

继续看他的源码,在上一篇博客中还有几个方法没有看,第一个是getElevationMap,如果在请求中没有指定elevateIds或者没有指定excludeIds的话,则调用这个方法

 

 /** get the elevation map from the data dir <br/> 从data中读取配置文件。 */
  Map<String,ElevationObj> getElevationMap(IndexReader reader, SolrCore core) throws Exception {
    
    synchronized (elevationCache) {
      
      // 如果在配置文件中设置了(不是solrCloud的时候),则不再读取,因为此时添加的话,key就是null。
      Map<String,ElevationObj> map = elevationCache.get(null);
      if (map != null) return map;
      
      map = elevationCache.get(reader);//根据indexReader读取,如果reader发生了变化,则重新读取,否则不读取,读取的话就会重新加载elevator.xml。
      if (map == null) {
        String f = initArgs.get(CONFIG_FILE);
        if (f == null) {
          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
              "QueryElevationComponent must specify argument: " + CONFIG_FILE);
        }
        log.info("Loading QueryElevation from data dir: " + f);
        
        Config cfg;
        
        // 读取配置文件可以从zk上读取(solrCloud),也可以从本地读取(solr)
        ZkController zkController = core.getCoreDescriptor().getCoreContainer().getZkController();
        if (zkController != null) {
          cfg = new Config(core.getResourceLoader(), f, null, null);
        } else {
          InputStream is = VersionedFile.getLatestFile(core.getDataDir(), f);//从data中读取
          cfg = new Config(core.getResourceLoader(), f, new InputSource(is), null);
        }
        
        map = loadElevationMap(cfg);
        elevationCache.put(reader, map);
      }
      return map;
    }
  }

 从这个方法中可以得出,如果你的要置顶的document是变化的话,如果你使用的是单机版的solr(不是solrCloud)就不要设置在conf中,不然你必须重启才可以重新加载这个配置文件。如果elevator.xml在solrCloud或者是data下的话,只要indexReader一发生变化,就会重新加载,也就是一commit就会重新读取。

 

我们再看一下ElevatorObj的代码,这个类用于封装一个指定,也即是封装一个文本值,和要置顶以及拉黑的document id。

  class ElevationObj {
    
    /** 穿入的text,也就是搜索的词 */
    final String text;
    /** 对上面的text进行分词之后的结果,可能和text一样,也可能不一样 */
    final String analyzed;
    /**排除的id组成的termQuery*/
    final TermQuery[] exclude;
    /**要置顶的那些document封装的query*/
    final BooleanQuery include;
     /**每一个id的权重,由大变小*/
    final Map<BytesRef,Integer> priority;
    /**这个是包含的id*/
    final Set<String> ids;
    /**这个是排除的id*/
    final Set<String> excludeIds;
    
    // 第一个参数是文本值,第二个参数是包含的多个id,第三个是排除的多个id
    ElevationObj(String qstr, List<String> elevate, List<String> exclude) throws IOException {
      
      this.text = qstr;
      this.analyzed = getAnalyzedQuery(this.text);//将文本分词
      this.ids = new HashSet<>();
      this.excludeIds = new HashSet<>();
      
      this.include = new BooleanQuery();
      this.include.setBoost(0);
      this.priority = new HashMap<>();
      int max = elevate.size() + 5;
      
      //  对于要置顶的doc,采用的是封装进一个booleanQuery.
      for (String id : elevate) {
        id = idSchemaFT.readableToIndexed(id);//没有操作
        ids.add(id);
        TermQuery tq = new TermQuery(new Term(idField, id));
        include.add(tq, BooleanClause.Occur.SHOULD);
        this.priority.put(new BytesRef(id), max--);
      }
      
      if (exclude == null || exclude.isEmpty()) {
        this.exclude = null;
      } else {
        this.exclude = new TermQuery[exclude.size()];
        for (int i = 0; i < exclude.size(); i++) {
          String id = idSchemaFT.readableToIndexed(exclude.get(i));
          excludeIds.add(id);
          this.exclude[i] = new TermQuery(new Term(idField, id));//封装要拉黑的doc到一个数组中
        }
      } 
    }
  }

看完上面的代码可以总结,他是将原来我们在请求中设置的query 又封装了多个query,有要置顶的,有要拉黑的,都是用id封装的。

 

最后一个办法最关键了,用来排序,将指定的要置顶的document进行排序:ElevationComparatorSource类,它用于产生一个排序器,我们只看newComparator方法

    /*** 返回的比较器根据的就是设置的priority进行排序的。 */
    @Override
    public FieldComparator<Integer> newComparator(String fieldname, final int numHits, int sortPos, boolean reversed)
        throws IOException {
      
      return new SimpleFieldComparator<Integer>() {
        
        /**这个最终存放的是priority的值,根据*/
        private final int[] values = new int[numHits];
        private int bottomVal;
        private int topVal;
        private PostingsEnum postingsEnum;
        //最后搜集到的id(置顶的)
        private Set<String> seen = new HashSet<>(elevations.ids.size());
        //最后的排序的实现,根据value中的值,
        public int compare(int slot1, int slot2) {
          return values[slot1] - values[slot2]; // values will be small enough that there is no overflow concern
        }
        
        @Override
        public void setBottom(int slot) {
          bottomVal = values[slot];
        }
        
        @Override
        public void setTopValue(Integer value) {
          topVal = value.intValue();
        }
        
        /**
         * 读取docValue  根据lucne的id找到指定的id,再根据指定的id找到priority。最后读取的docValue就是priority
         * @param doc   lucene的id
         * @return      docValue的值
         */
        private int docVal(int doc) {
          if (ordSet.size() > 0) {
            int slot = ordSet.find(doc);
            if (slot >= 0) {//大于0表示在ordSet中,也就是这个id被指定了置顶。
              BytesRef id = termValues[slot];//指定的id
              Integer prio = elevations.priority.get(id);//根据指定的id读取docValue,也就是priority。
              return prio == null ? 0 : prio.intValue();
            }
          }
          return 0;//如果没有指定置顶,则所有的值都是0,表示排序都是一样的,再根据得分的排序器排序。
        }
        
        @Override 
        public int compareBottom(int doc) {//当排序时,先要对比bottomVal
          return bottomVal - docVal(doc);
        }
        //给value赋值,实现lucene的id和docuemnt的id的交换
        @Override
        public void copy(int slot, int doc) {
          values[slot] = docVal(doc);//docVal就是读取的指定的solr的id(和fieldCache是一样的)
        }
        /** 当切换segmentReader的时候调用,读取真正存在的id,添加到seen、ordSet和termValues中。*/
        protected void doSetNextReader(LeafReaderContext context) throws IOException {
          // convert the ids to Lucene doc ids, the ordSet and termValues needs to be the same size as the number of
          // elevation docs we have
          ordSet.clear();
          Fields fields = context.reader().fields();
          if (fields == null) return;
          Terms terms = fields.terms(idField);//和fieldCache一样,也是读取的词典表
          if (terms == null) return;
          TermsEnum termsEnum = terms.iterator();
          BytesRefBuilder term = new BytesRefBuilder();
          Bits liveDocs = context.reader().getLiveDocs();//没有被删除的id
          for (String id : elevations.ids) {
            term.copyChars(id);
            if (seen.contains(id) == false && termsEnum.seekExact(term.get())) {
              postingsEnum = termsEnum.postings(liveDocs, postingsEnum, PostingsEnum.NONE);
              int docId = postingsEnum.nextDoc();//因为是id,所以不会是重复的
              if (docId == DocIdSetIterator.NO_MORE_DOCS) continue; // must have been deleted
              termValues[ordSet.put(docId)] = term.toBytesRef();//添加lucene的id和指定的id的关系,将lucene的id放到ordSet中,返回的是在ordSet中的位置,然后将对应的指定的doc放在termVlues中,实现lucene的id和指定的id的关联。
              seen.add(id);
              assert postingsEnum.nextDoc() == DocIdSetIterator.NO_MORE_DOCS;//因为是id,所以一定只能搜到一个doc,所以是no_more_docs
            }
          }
        }
      };
    }
  }

 这个比较器就是实现用priority,也就是按照我们指定的置顶的顺序进行排序。

 

 

看完上面这些,就可以使用置顶功能了,无论是solrCloud还是单机版的solr,如果是需要配置文件的话,如果这个文件是需要修改的,那么就会很麻烦,所以我还是推荐使用将置顶的和拉黑的id放在请求的参数中,我自己做的实验也是这么做的。我做的实验是使用了一个单机版的solr,添加了两个document,每个document有两个域,一个是id,一个是title,id为1的的title含有两个hello,id为2的含有4个hello,我的select的requestHandler的df是title。

在浏览器中输入 http://localhost:8080/solr/collection1/select?q=hello&wt=json&indent=true,很明显,id为2的应该排在前面,因为他含有4个hello。

下面使用elevator,url变为:http://localhost:8080/solr/collection1/select?q=hello&wt=json&indent=true&enableElevation=on&elevateIds=1,添加了enableElevation=on,也就是开启置顶,elevateIds=1,置顶的id为1,然后现在id=1就排在前面了,并且id=2的也显示。

使用拉黑:http://localhost:8080/solr/collection1/select?q=hello&wt=json&indent=true&enableElevation=on&elevateIds=1&excludeIds=2,添加了&excludeIds=2,也就是将2拉黑,此时,只有id=1的显示。

添加排序:http://localhost:8080/solr/collection1/select?q=hello&wt=json&indent=true&sort=id asc&enableElevation=on&forceElevation=on&elevateIds=2,虽然开启了sort,按照id升序,也就是id=1的在前面,但是后面设置了forceElevation,也就是强迫使用置顶的排序并且置顶2,此时还是id=2的排在前面。

 

over,算是弄清楚solr的置顶和拉黑了。

 

 

分享到:
评论

相关推荐

    solr-6.2.0源码

    源码中包含了丰富的注释和示例,帮助开发者深入理解Solr的设计思想和实现细节。 总结来说,Solr 6.2.0是一个强大的全文搜索引擎,它的分布式特性、实时性以及丰富的功能使得它成为企业级搜索应用的理想选择。通过...

    solr(solr-9.0.0-src.tgz)源码

    源码分析是深入理解一个软件系统工作原理的重要途径,对于Solr这样的复杂系统尤其如此。这里我们将围绕"solr-9.0.0-src.tgz"这个源码包,详细探讨其主要组成部分、核心功能以及开发过程中的关键知识点。 1. **Solr...

    Solr 4.0 源代码实例

    源代码实例是学习Solr内部工作原理和自定义功能的关键资源,尤其对于开发者而言,深入理解源码有助于提升系统的优化和扩展能力。 在Solr 4.0版本中,`solr.xml`是Solr的核心配置文件,它定义了Solr实例的基本设置,...

    solr 4.10源码

    这个源码包包含了Solr 4.10.4的所有源代码,对于开发者来说,这是一个深入了解Solr工作原理、定制功能以及进行二次开发的重要资源。 1. **Solr简介** Apache Solr是一个基于Lucene的全文检索服务,提供了一个高效...

    solr5的ik中文分词器源码

    通过研究"solr5的ik中文分词器源码",开发者不仅可以了解IK分词器的内部工作机制,还可以根据实际需求进行定制化开发,提升Solr在中文文本处理中的效能。同时,这也是一个学习自然语言处理和搜索引擎技术的好起点。

    solr6.6.0源码

    这个源码包包含了 Solr 的所有源代码,对于理解 Solr 的工作原理、进行二次开发或者定制化配置有着非常重要的意义。接下来,我们将深入探讨 Solr 6.6.0 中的一些关键知识点。 一、Solr 架构与组件 Solr 的核心架构...

    JAVA+Solr分词项目工程实例Java实用源码整理learns

    标题中的"JAVA+Solr分词项目工程实例Java实用源码整理learns"指的是一个基于Java编程语言并结合Solr搜索引擎的项目实例。这个项目主要关注于文本处理和信息检索,利用Solr的分词功能来提升搜索效率和准确性。Solr是...

    solr-4.5源码包

    在这个源码包中,我们可以深入理解Solr的工作原理以及其核心组件的实现。 首先,让我们了解Solr的基本架构。Solr基于Lucene库构建,Lucene是一个高性能、全文本检索库。Solr在其之上添加了分布式处理、集群管理、...

    JAVA+Solr分词项目工程实例Java源码

    这个项目工程实例是关于如何使用Java与Solr进行集成,实现分词搜索功能的示例。Solr提供了强大的文本分析、索引和查询能力,广泛应用于内容管理系统、电子商务网站、新闻门户等场景。 1. **Solr简介** - Solr是...

    solr源码及文档

    solr全文检索,里面包含文档,源代码,jar包,使用的是solr4.2,东西比较全,安装文档就能跑起来,,适合参考借鉴

    solr教程+实例

    Solr教程与实例详解 Apache Solr是一款开源的企业级全文搜索引擎,由Apache软件基金会开发,基于Java语言,具有高效、可扩展的特点。它为大型、分布式搜索应用提供了强大的支持,包括文档检索、拼写建议、高亮显示...

    全文检索(solr)实例

    本实例将详细介绍如何在本地环境中部署并运行一个Solr实例。 **一、Solr概述** Apache Solr是基于Java开发的,能够处理大量数据的高性能搜索平台。它提供了分布式、可扩展、实时和近实时搜索功能。Solr的核心特性...

    Solr项目源码及solr资源包

    - **Collection/Core**:Solr中的数据集,对应于数据库中的表,每个Core有自己的schema和配置。 - **Document**:索引的基本单位,类似于数据库中的记录。 - **Field**:文档中的属性,定义了数据类型和分析方式...

    lucene-solr源码,编译成的idea项目源码

    本人用ant idea命令花了214分钟,35秒编译的lucene-solr源码,可以用idea打开,把项目放在D:\space\study\java\lucene-solr路径下,再用idea打开就行了

    java进阶Solr从基础到实战

    在本套课程中,我们将全面的讲解Solr,从Solr基础到Solr高级,再到项目实战,基本上涵盖了Solr中所有的知识点。 主讲内容 章节一:Solr基础(上) 1. 环境搭建 2. 核心讲解 3. 数据导入 4. 各种中文分析器 章节二:...

    Solr-search过程源码分析

    在深入探讨Solr-search过程的源码分析时,我们聚焦于关键步骤与核心组件,以求全面理解Solr搜索机制的内部运作。Solr作为一款高性能、可伸缩的开源搜索平台,其搜索处理流程涉及多个层次的组件交互与数据处理,其中...

    solr(中文分词器)集群

    在Solr中使用IK分词器,是为了更好地处理中文文本的分词。IK分词器(Intelligent Chinese Analyzer for Solr)是针对Solr优化的中文分词工具,它支持多种分词模式,包括精确模式、全模式、关键词模式等,以满足不同...

    solr实现电商自定义打分

    Solr,全称为Apache Solr,是一款开源的全文搜索引擎,广泛应用于电商、新闻、文档检索等领域。它提供了高效、可扩展的搜索与分析能力。在电商领域,搜索结果的排序和打分对于用户体验至关重要,因为它直接影响到...

    solr实现的搜索引擎

    在Solr中,数据存储的单位是Collection。通过Solr Admin UI或API可以创建、删除和管理Collections。每个Collection由一个或多个Shard组成,Shard又由多个Replica复制,确保高可用性和容错性。 4. **索引数据** ...

    solr实现京东搜索

    在Solr中,可以通过字段查询来实现这一功能。例如,为每个商品设置一个“category”字段,用户在搜索时可以选择特定分类,Solr会返回该分类下的所有商品。此外,Solr的 faceting 功能可用于展示不同分类下的商品数量...

Global site tag (gtag.js) - Google Analytics