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

solr中的同义词配置以及关键源码解读

    博客分类:
  • solr
阅读更多

由于工作中需要做同义词,今天看了看solr的实现以及源码,记个笔记。我看的solr的版本是5.5.3.

在solr的schema.xml中(5.x的版本是managed-schema文件)已经有实例了,截图如下:

    <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100"> <!-- 切记,只有TextField中才可以设置type=index或者type=query的两个analyzer,我看过源码,其他的时候不起作用-->
      <analyzer type="index">
        <tokenizer class="solr.StandardTokenizerFactory"/>
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
        <!-- in this example, we will only use synonyms at query time
        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
        -->
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.StandardTokenizerFactory"/>  <!--使用的分词器的工厂-->
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />  <!--添加停止词过滤器工厂-->
        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>  <!-- 添加同义词过滤器工厂 -->
        <filter class="solr.LowerCaseFilterFactory"/> <!--添加转化为小写的过滤器工厂-->
      </analyzer>
    </fieldType>

 关键就是配置的SynonyFilterFactory,我们看看他的源码:

SynonymFilterFactory类时继承自TokenFilterFactory类,后者为所有的TokenFilter工厂的抽象类,上图中的LowerCaseFilterFactory也是继承自这个类。TokenFilterFactory这个抽象类最关键的是create(TokenStream)方法,即根据tokenizer的操作继续添加操作,这个倒是很容易理解,在TokenizerFactory中也有个create方法,不过是没有参数的,因为此时还没有生成tokenStream。

明白了TokenFilterFactory之后,再看一下SynonymFilterFactory类的结构,直接看他的构造方法吧:

  public SynonymFilterFactory(Map<String,String> args) {//map即在配置中的参数,比如上面的synonyms,ignoreCase,expand
    super(args);
    ignoreCase = getBoolean(args, "ignoreCase", false);//ignoreCase表示再分词匹配的时候要不要忽略大小写
    synonyms = require(args, "synonyms");//同义词词典的位置
    format = get(args, "format");//解析同义词词典的时候使用的格式化对象,即怎么从同义词词典中解读同义词
    expand = getBoolean(args, "expand", true);//这个也是在解读同义词词典的时候的参数,用语言不好描述,等用到再说

    analyzerName = get(args, "analyzer");//这个是在词典表中读取一个字符串要进行分词,这个指定使用的分词器
    tokenizerFactory = get(args, "tokenizerFactory");//这个和上面的意思一样,只不过使用的是工厂模式
    if (analyzerName != null && tokenizerFactory != null) {
      throw new IllegalArgumentException("Analyzer and TokenizerFactory can't be specified both: " +
                                         analyzerName + " and " + tokenizerFactory);
    }

。。。//忽略不重要的参数
  }

 然后我们看看对同义词词典的加载,在org.apache.lucene.analysis.synonym.SynonymFilterFactory.inform(ResourceLoader)方法中有个loadSynonyms方法,顾名思义,就是加载同义词词典表的方法

 protected SynonymMap loadSynonyms(ResourceLoader loader, String cname, boolean dedup, Analyzer analyzer) throws IOException, ParseException {//第二个参数是使用的格式化对象的名字,第三个是加载表的时候要不要排除重复的,第四个是使用的分词器
    CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
        .onMalformedInput(CodingErrorAction.REPORT)
        .onUnmappableCharacter(CodingErrorAction.REPORT);

    SynonymMap.Parser parser;
    Class<? extends SynonymMap.Parser> clazz = loader.findClass(cname, SynonymMap.Parser.class);
    try {
      parser = clazz.getConstructor(boolean.class, boolean.class, Analyzer.class).newInstance(dedup, expand, analyzer);//这个是用来生成最终的SynonyMap,里面最关键的是一个FST和一个类似于HashMap的BytesRefHash.
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    List<String> files = splitFileNames(synonyms);//可以传多个同义词词典文件
    for (String file : files) {
      decoder.reset();
      try (final Reader isr = new InputStreamReader(loader.openResource(file), decoder)) {//读取同义词词典文件
        parser.parse(isr);//解析,将同义词进入fst,最关键的就是这个方法。
      }
    }
    return parser.build();//建造一个SynonymMap
  }

 现在最关键的就是一个parser.parse方法了,这里的parser是SolrSynonymParser类,继承自Builder类,用于构造fst

  public void parse(Reader in) throws IOException, ParseException {
    LineNumberReader br = new LineNumberReader(in);//读取一行
    try {
      addInternal(br);//调用addInternal方法
    。。。//去掉无用代码
  }

  private void addInternal(BufferedReader in) throws IOException {
    String line = null;
    while ((line = in.readLine()) != null) {
      if (line.length() == 0 || line.charAt(0) == '#') {//注释的一行
        continue; // ignore empty lines and comments
      }
      
      // TODO: we could process this more efficiently.
      String sides[] = split(line, "=>");//根据=>分开,
      if (sides.length > 1) { // 如果当前根据=>之后的个数大于1,即aa=>bb格式的同义词词典
        if (sides.length != 2) {
          throw new IllegalArgumentException("more than one explicit mapping specified on the same line");
        }
        String inputStrings[] = split(sides[0], ",");//左边的部分,用,分开,
        CharsRef[] inputs = new CharsRef[inputStrings.length];
        for (int i = 0; i < inputs.length; i++) {
          inputs[i] = analyze(unescape(inputStrings[i]).trim(), new CharsRefBuilder());//对左边的部分使用置顶的分词器处理
        }
        //对右边的部分做和左边同样的操作
        String outputStrings[] = split(sides[1], ",");
        CharsRef[] outputs = new CharsRef[outputStrings.length];
        for (int i = 0; i < outputs.length; i++) {
          outputs[i] = analyze(unescape(outputStrings[i]).trim(), new CharsRefBuilder());
        }
        // these mappings are explicit and never preserve original
        for (int i = 0; i < inputs.length; i++) {//循环左边的词,每一个词都添加所有的右边的词作为同义词,即如果配置的是a=>b,c,添加到synonymMap的是a->b,a->c,但是不会添加b->c,b->a,c->a。
          for (int j = 0; j < outputs.length; j++) {
            add(inputs[i], outputs[j], false);
          }
        }
      } else {//这个是没有=>的形式,仅仅有a,b,c,这些组成同义词
        String inputStrings[] = split(line, ",");
        CharsRef[] inputs = new CharsRef[inputStrings.length];
        for (int i = 0; i < inputs.length; i++) {
          inputs[i] = analyze(unescape(inputStrings[i]).trim(), new CharsRefBuilder());
        }
        if (expand) {//通过if-else的对比expand的意思是要不要反方向的添加,比如a,b,c,如果不是expand,就会只记录b->a,c->a,不会记录a->b,a->c,b->c,c->b。
          // all pairs
          for (int i = 0; i < inputs.length; i++) {//这两个for循环,将形成所有的同义词对,比如上面的
            for (int j = 0; j < inputs.length; j++) {
              if (i != j) {
                add(inputs[i], inputs[j], true);//add是添加到BytesRefHash的数组里面返回在数组中的位置,并记录到fst中,这样从fst中根据term获得时先获得的是在bytesRefHash的位置(可以是多个),然后再根据位置获得同义词,
              }
            }
          }
        } else {
          // all subsequent inputs map to first one; we also add inputs[0] here
          // so that we "effectively" (because we remove the original input and
          // add back a synonym with the same text) change that token's type to
          // SYNONYM (matching legacy behavior):
          for (int i = 0; i < inputs.length; i++) {
            add(inputs[i], inputs[0], false);//如果不是expand,那么只会将同义词映射到第一个词上。
          }
        }
      }
    }
  }

至此已经搞懂了同义词的用法,不过对于fst还是没有涉及到,不过已经可以使用了 同义词了。还有问题,如何更新同义词词典呢,总不能要更新后重启吧,还有就是词典保留在solr中修改起来特别麻烦,如何能更加方便的动态修改词典呢,这个留在下一个博客中,只要稍微做些修改,就能实现动态的添加词典,动态的做同义词。

 

 

 

分享到:
评论

相关推荐

    solr的同义词库

    solr的同义词库,文档是从其他地方拿的,放到这里供大家下载,共2万1千条

    solr-6.2.0源码

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

    solr软件包扩展词典可停词配置学习和开发文档

    在这个"solr软件包扩展词典可停词配置学习和开发文档"中,我们将深入探讨Solr在Linux环境下的安装、扩展词典的使用以及停词配置的关键知识点。 **1. Solr的Linux环境安装** 在Linux环境下安装Solr,首先确保系统...

    solr5的ik中文分词器源码

    在处理中文文本时,一个关键的组件就是中文分词器,而IK(Intelligent Chinese)分词器是Solr中常用的中文分词工具之一。本文将深入探讨"solr5的ik中文分词器源码"的相关知识点。 1. **IK分词器概述**: IK分词器...

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

    这里我们将围绕"solr-9.0.0-src.tgz"这个源码包,详细探讨其主要组成部分、核心功能以及开发过程中的关键知识点。 1. **Solr架构** Solr的架构基于Lucene,一个强大的全文搜索引擎库。它提供了分布式、可扩展、高...

    配置好的solr启动环境

    描述中提到“直接修改web.xml设置一下solr core路径”,这是Solr中的关键配置步骤。web.xml文件是Tomcat的部署描述符,用于配置应用的行为。在Solr中,每个独立的搜索实例被称为一个"Core",每个Core有自己的配置...

    solr6.6.0源码

    通过深入研究 Solr 6.6.0 的源码,开发者可以了解其内部工作原理,掌握如何优化索引构建、查询性能,以及如何利用其高级特性,如分面搜索、地理位置搜索、复杂查询语法等。这有助于开发出更加高效、定制化的搜索引擎...

    solr搜索服务器安装配置

    5. **配置日志**:Solr支持日志记录,可以在`conf/log4j.properties`文件中配置日志级别和输出方式。 6. **配置安全**:为了提高安全性,可以配置身份验证和授权机制。具体配置方法参见官方文档。 #### 三、Solr...

    图解Solr5.3.1与MySQL配置【原创】

    - **synonyms.txt**:同义词配置。 - **spellings.txt**:拼写检查配置。 #### 八、测试与验证 完成配置后,我们需要进行一些测试操作来验证Solr是否能够正确地从MySQL中导入数据,并提供准确的搜索结果。 1. **...

    Solr安装与配置

    确保提交的文件名与之前创建的分词文档相匹配,并且索引字段的名称在 Solr 配置和分词文档中一致。 完成以上步骤后,你就可以通过 Solr 的 Web 界面进行搜索测试,验证分词配置是否生效。如果你遇到乱码问题,检查 ...

    solr5.4开发环境完整配置

    在Solr中,mmseg4j 可以通过添加相应的 jar 包来集成,以便对中文内容进行索引和搜索。 配置 Solr 5.4 开发环境的第一步是下载 Solr 的发行包。访问 Apache 官方网站获取最新版本的 Solr 5.4,解压缩到你选择的目录...

    solr所需配置文件

    在Solr中,配置文件是至关重要的,它们决定了Solr的行为和性能。以下将详细介绍`solr所需配置文件`及其作用。 1. **solrconfig.xml**: 这是Solr的核心配置文件,定义了索引和查询的处理方式。例如,它包含了索引...

    solr5配置中文分词

    在Solr中,Analyzer是处理文本输入的组件,它负责将输入的文本转换为可搜索的术语。对于中文,我们需要一个能理解并处理中文语法和词汇的分词器。IK Analyzer是一个流行的中文分词工具,专为Java设计,适用于Lucene...

    手把手教你 对 solr8 配置用户登录验证 涉及到的配置文件

    在 Solr 8 中,为了确保系统的安全性和数据的隐私性,配置用户登录验证是非常重要的步骤。本文将详细介绍如何对手动配置 Solr 8 的用户登录验证。 首先,我们需要了解 Solr 的安全组件——Jetty 容器。Solr 默认...

    solr 3.4 中文分词配置问题

    在Solr中,通常会使用第三方分词库,如ikanalyzer、jieba分词或者pkuseg等,这些库能处理中文的复杂性,如多音字、成语和词语的组合。 在配置Solr的中文分词时,有以下几个关键步骤: 1. **安装分词库**:下载并...

    solr所需要配置的资源.zip

    在本压缩包中,包含了Solr运行所需的一些关键组件,包括Tomcat服务器、IK分词器以及Solr自身的配置文件。下面将详细介绍这些组件及其在Solr中的作用。 首先,Tomcat是一个流行的Java应用服务器,常被用作部署...

    solr-5.3.0-src.tgz源码

    当前资源为Apache官方提供的solr-5.3.0-src.tgz源码,包括Lucene和solrj的源码,方便大家学习使用!此外还整理了一套有关solr-5.3.0的window和linux版包以及官方说明文档apache-solr-ref-guide-5.3.pdf,有需要的...

Global site tag (gtag.js) - Google Analytics