lucene更新索引_lucene和solr全文搜索从0到通使用记录

本文记录了作者从零开始学习Lucene和Solr的全过程,包括迷茫期、入门期、进阶期的各个阶段。在迷茫期,作者通过学习了解到Lucene是一个Java全文搜索库,而Solr提供了接口供其他程序使用。在入门期,作者通过创建索引、理解索引结构等操作熟悉了Solr的基本操作。进阶期涉及了增量更新、高级查询、权限控制和中文分词优化等,作者最终选择了使用Solr的增量更新配置来实现数据同步,并讨论了不同场景下的解决方案。
摘要由CSDN通过智能技术生成

以前只知道有lucene全文搜索这个开源软件,但一直没用过。这次一个项目正好用到,于是了解一下,发现网上介绍的文章大多比较旧,不适用,要么介绍比较浅。这里记录我从全不会到通的过程,以供借鉴。

迷茫期

首先打开lucene的官网,一堆英文,我的英文很一般,看了半天没有头绪,于是搜中文使用教程,先得有一个全面的了解。

看到的很多先介绍了一些名词:索引结构Document(文档)、分词器、Field域。虽然有图有介绍,还是有点懵。不过还是知道lucene是一个jar包,只要引到自己的项目中,通过调用相关类就可以使用了。

对于编程,看得再多,不如动手一试。建一个测试环境,用一下就会有很直观的认识。对于现在这项目使用的是node.js开发,但lucene是java,这是没法直接调用的。在网上搜了一下,node-lucenes是大拿封装好的,可以直接使用。但仔细一看,它只支持到node11,现在都一般都使用node12,node14也都出来了,而且此项目好多年没有更新了。不能建新项目使用过时的内容,以后维护会非常麻烦。肯定还有其它方法,也考虑过用node调用系统cmd,由系统cmd调lucene包,但总感觉不顺。如果能直接调用最好,不死心,再接着搜。

终于到找一篇文章说solr是lucene的封装,提供接口供其它程序使用,其中有http协议传输json格式内容。这下思路就清晰了,nodejs通过http协议调用solr,由solr完成lucene全文搜索工作。

于是到官网下载solr,发现首页原来就是和lucene并排放着,那么显眼,我竟然没留意,结果绕了一大圈,还是英文不行,能力限制了思考

0d7b783856cdf775b8b4287e17727f03.png

入门期

solr下载安装一路顺利,使用方法和tomcat类似。打开后台看了看,不知道干什么。

c0aecb643a99b6d0021ecf711e3b8bcc.png

于是搜了一篇入门文章(solr7.4 安装与使用 - Tony_ding - 博客园),照着做了一遍,才明白了索引结构core、Document(文档)、分词器、Field域这些概念。把新知纳入现有知识体系中是最高效的掌握,我对数据库了解很多,一比对就掌握了大概。

  1. core相当于数据库中的表空间; 通过命令行创建core: $ solr create -c CORE_NAME
  2. Document相当于表;
  3. field相当于字段;
  4. 分词器定义了怎么提取关键字。

solr启动命令: $ solr start -p 8984

由于lucene是国外的,怎么提取中文关键字是个问题,按照文章方法使用了ikanalyzer,当尝试分词"博客园"后,对solr有了直观的认识。

188790673a86b9f402fbc69afc4ee360.png

进阶期

到这个阶段,中文介绍就不多了。在网上找到一文章 搜索引擎技术系列教材 (八)- solr - Solr 入门教程 讲到了设置字段、创建索引、分页查询,其它的就需要去看官网了,不过看完后对solr有了更进一步的认识。

目前有的疑问是solr具体和node怎么对接?node创建一条数据每次都需要调用solr吗?上传文件需要使用tika获得文本后再使用?

solr7.7.0搜索引擎使用(三)(添加文件索引) 提到solr可以对pdf,txt,doc等文件生成索引,solr直接引入了tika,不需要考虑tika的问题了。按此文章提示操作,建立一个文件索引。

  • 步骤1.添加core,取名暂且为 coreFile 在bin下执行命令 ./solr create -c coreFile
  • 步骤2.准备要搜索的文件
  • 步骤3.添加搜索的数据源 注意,此时使用的class是solr.DataimportHandler
  • 步骤4.添加数据源文件,注意更换 baseDir为你自己的文件路径
  • 步骤5.添加字段索引
  • 步骤6.添加中文分词

文章中没说清的地方:

  1. 将解压后的solr-8.5.1contribanalysis-extraslucene-libs下的lucene-analyzers-smartcn-8.5.1.jar放到Tomcat8webappssolrWEB-INFlib下。
  2. solrconfig.xml中加入DIH jar包依赖。 内容是:
    <lib dir="${solr.install.dir:../../..}/dist/" regex="solr-dataimporthandler-.*.jar" />
    <lib dir="${solr.install.dir:../../..}/contrib/extraction/lib/" regex=".*.jar"/>

extraction/lib目录下主要引用的是tika包,它可以把word等文档转成文字。我刚开始因为没有加这个目录,导入建索引一直没有成功。

测试:将本地的50个数据文件导入到solr并创建index

e974de888772f164bd12ba3862a56642.png

测试:查找文件名

8944de0794ae2b69093af192b616b129.png

补充:如果是txt文件需要保证内容是UTF-8编码,默认txt文件是的编码是GBK,上传之后最好进行转码。

建好测试,开始还想着怎么导文件怎么提取word文本,原来一切如此简单。现在要测试问题如下:

  1. 如果新增一个文件会不会自动增加索引?
    答案 :不会,需要用到增量更新
  2. 搜索找到文件后,如何和原平台对接,如何找到数据库对应此文件备注等内容?
    答案:不能直接对接文件,需要对接数据库
  3. 如何增加权限,每人只可搜索自己上传的文件,这需要怎么处理?

针对第一个问题解决方法有:

  1. DataImportHandler下使用ContentStreamDataSource数据源接收POST参数实现导入数据,可以实时,但请求时间会变长;
  2. delta-import实现增量导入,定时执行任务,不能做到实时;或使用apache-solr-dataimportscheduler;
  3. ExtractingRequestHandler方法,需要上传文件,但本项目中文件在同一服务器上,不需要上传。

不知道应该使用哪种方法好,最终看到 携程酒店订单Elasticsearch实战 (搜索引擎怎么选?携程酒店订单Elasticsearch实战 - 51CTO.COM)上面的分析:

实时扫描数据库
初看这是一种很低效的方案,但是在结合以下实际场景后,它却是一种简单、稳定、高效的方案:
1、零耦合。相关应用无需做任何改动,不会影响业务处理效率和稳定性。
2、批量写 Elastic Search。由于扫描出来的都是成批的数据,可以批量写入 Elastic Search,
避免 Elastic Search 由于过多单个请求,频繁刷新缓存。
3、存在大量毫秒级并发的写。扫描数据库时无返回数据意味着额外的数据库性能消耗,
我们的场景写的并发和量都非常大,所以这种额外消耗可以接受。
4、不删除数据。扫描数据库无法扫描出删除的记录,但是订单相关的记录都需要保留,
所以不存在删除数据的场景。

为了零耦合,了终决定使用第二种方法。第一种方案每次增删改都要调用会很麻烦,而且随着业务逻辑增加会更加明显。

个人感觉ElasticSearch在增量这块比solr做的好,大项目还是要用ElasticSearch,目前本项目不大,单机运行,使用solr应该足够了 。

高级期 :增量更新

原来content表中增加“LAST_INDEX_TIME”、“IS_DELETE”字段

增量更新配置data-config.xml文件:

<!--  
  transformer 格式转化:HTMLStripTransformer 索引中忽略HTML标签
  query:查询所有未删除记录数据,主要用在full-import全量导入时候
  deltaQuery:根据dataimporter.properties每次刷新的last_index_time,实现刷新从上次last_index_time至今的数据,从而增量索引主键ID查询处理,注意这个只能返回ID字段
  deletedPkQuery:增量索引删除access=-1主键ID
  deltaImportQuery:增量查询从上次刷新时间到现在数据且未删除的access>=0,进行增量更新发布索引文件 
-->
<dataConfig>
    <dataSource name="fileDataSource" type="BinFileDataSource"/>
    <dataSource name="mysqlDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/weihuidb?characterEncoding=UTF-8" user="weihui" password="weihui"  />
    <document>
        <entity name="t_file" pk="id" dataSource="mysqlDataSource"
          query="select * from File where access>=0 and isDirectory=false"
          deltaQuery="select id from File where access>=0 and isDirectory=false and createTime> '${dataimporter.last_index_time}'"   
          deletedPkQuery="select id from File where access=-1"
          deltaImportQuery="select * from File where access>=0 and id = '${dih.delta.id}'" 
        >
           <field column="id" name="id" />
          <field column="name" name="name" />
          <field column="ownerName" name="ownerName" />
          <field column="path" name="path" />
          <field column="taskName" name="taskName" />
          <field column="department" name="department" />
          <field column="file_id" name="file_id" />
          <field column="access" name="access" />
          <entity name="pdf" processor="TikaEntityProcessor" url="${t_file.path}" format="text" dataSource="fileDataSource">
            <field column="text" name="text"/>
          </entity>
        </entity>
    </document>
</dataConfig>

下载jar包 apache-solr-dataimportscheduler.jar到 solr 项目的WEB-INFlib 目录下

修改web.xml文件配置监听,在servlet节点前增加:

<listener>           
<listener-class>
org.apache.solr.handler.dataimport.scheduler.ApplicationListener           
</listener-class>     
</listener>

solr-data-importscheduler.jar中提取出dataimport.properties放入在server/solr/conf目录下。(conf 目录需要自己新建),并根据自己的需要进行修改;比如我的配置如下:

#Tue Jul 21 12:10:50 CEST 2010
metadataObject.last_index_time=2010-09-20 11:12:47
last_index_time=2010-09-20 11:12:47


#################################################
#  #
#  dataimport scheduler properties  #
#  #
#################################################

# to sync or not to sync
# 1 - active; anything else - inactive
syncEnabled=1

# which cores to schedule
# in a multi-core environment you can decide which cores you want syncronized
# leave empty or comment it out if using single-core deployment
syncCores=weihui

# solr server name or IP address
# [defaults to localhost if empty]
server=localhost

# solr server port
# [defaults to 80 if empty]
port=9039

# application name/context
# [defaults to current ServletContextListener's context (app) name]
webapp=solr

# URL params [mandatory]
# remainder of URL
params=/dataimport?command=delta-import&clean=false&commit=true

# schedule interval
# number of minutes between two runs
# [defaults to 30 if empty]
interval=10

 #  schedule interval
 #  number of minutes between two runs
 #  [defaults to 30 if empty]
 # 自动增量更新时间间隔,单位为 min,默认为 30 min
 interval=5
 ​
 # 重做索引时间间隔,单位 min,默认 7200,即 5 天
 reBuildIndexInterval = 7200
 ​
 # 重做索引的参数
 reBuildIndexParams=/dataimport?command=full-import&clean=true&commit=true
  
 ​
 # 重做索引时间间隔的开始时间
 reBuildIndexBeginTime=1:30:00

到此,我们就可以实现数据库自动增量导入了;

补充:使用这方法更新时一直报错,所以先使用系统的定时任务更新,定时调用下面连接即可:

http://127.0.0.1:8983/solr/new_core/dataimport?command=delta-import&clean=false&commit=true

注:127.0.0.1:8983是solr服务地址,new_core是创建core的名称。

高级期:高级查询/权限/过滤

每人只可搜索自己上传的文件,这个问题怎么处理呢?

目前可以想到的就是在select的参数中加以限定。

1. 查询所有 http:// localhost:8080/solr/pri mary/select?q=*:*
多字段或关系AND
TITLE:("中国人" AND "美国人" AND "英国人")
多字段不包含的关系 NOT
这个语法就是我吃苦的地方,之前已经当多值or那样去查,结果不是,要写成
TITLE:(* NOT "上网费用高" NOT "宽带收费不合理" )
查询一个范围 BETWEEN
NUM:[-90 TO 360 ] OR CREATED_AT:[" + date1 + " TO " + date2 + "]
2. 限定返回字段
http://localhost:8080/solr/primary/select?q=*:*&fl=productId
表示:查询所有记录,只返回productId字段
3. 分页
http://localhost:8080/solr/primary/select?q=*:*&fl=productId&rows=6&start=0
表示:查询前六条记录,只返回productId字段
4. 增加限定条件
http://localhost:8080/solr/primary/select?q=*:*&fl=productId&rows=6&start=0&fq=category:2002&fq=namespace:d&fl=productId+category&fq=en_US_city_i:1101
表示:查询category=2002、 en_US_city_i=110以及namespace=d的前六条记录,只返回productId和category字段
5. 添加排序
http://localhost:8080/solr/primary/select?q=*:*&fl=productId&rows=6&start=0&fq=category:2002&fq=namespace:d&sort=category_2002_sort_i+asc
表示:查询category=2002以及namespace=d并按 category_2002_sort_i升序排序的前六条记录,只返回productId字段
6. facet查询
现实分组统计结果
http://localhost:8080/solr/primary/select?q=*:*&fl=productId&fq=category:2002&facet=true&facet.field=en_US_county_i&facet.field=en_US_hotelType_s&facet.field=price_p&facet.field=heatRange_i
http://localhost:8080/solr/primary/select?q=*:*&fl=productId&fq=category:2002&facet=true&facet.field=en_US_county_i&facet.field=en_US_hotelType_s&facet.field=price_p&facet.field=heatRange_i&facet.query=price_p:[300.00000+TO+*]

这里就要使用到facet查询

92df1f253a5f4f07d49cbbef2e052ca6.png

上面是比较直接的Faceted Search例子,品牌、产品特征、卖家,均是 Facet 。而Apple、Lenovo等品牌,就是 Facet values 或者说 Constraints ,而Facet values所带的统计值就是 Facet count/Constraint count

建议将访问角色(是,其复数)存储为文档元数据.这里所需的字段access_roles是一个多面值的多值字符串字段.

Doc1: access_roles:[user_jane, manager_vienna] // Jane and the Vienna branch manager may see it
Doc2: access_roles:[user_john, manager_vienna, special_team] // Jane, the Vienna branch manager and a member of special team may see it

拥有文档的用户是该文档的默认访问角色.

要更改文档的访问角色,请编辑access_roles.

当Jane搜索时,她所属的访问角色将成为查询的一部分. Solr将仅检索与用户访问角色匹配的文档.

当维也纳办事处(manager_vienna)的经理Jane(user_jane)搜索时,她的搜索结果如下:

q=mainquery
&fq=access_roles:user_jane
&fq=access_roles:manager_vienna
&facet=on
&facet.field=access_roles

它在access_roles中获取包含user_jane OR manager_vienna的所有文档; Doc1和Doc2.

当Bob,(user_bob),特殊团队(specia_team)的成员搜索时,

q=mainquery
&fq=access_roles:user_bob
&fq=access_roles:special_team
&facet=on
&facet.field=access_roles

它为他取得了Doc2.

高级期:优化中文选词

solr自带的中文选词不好用,有些词如地名选不上,而且不能加自定义词,所以改为用ikanalyzer选词。

下载地址:https://pan.baidu.com/s/1Dbma2vAepBSsCag_EztTTw

下载解压后 把两个jar文件复制到solr-8.5.1serversolr-webappwebappWEB-INFlib中

在solr-8.5.1serversolr-webappwebappWEB-INFclasses目录下新建一个classes目录,把下面三个文件复制进去

managed-schema.xml 添加如下代码:

<fieldType name="text_cn" class="solr.TextField">
<analyzer type="index" useSmart="false"
class="org.wltea.analyzer.lucene.IKAnalyzer" />
<analyzer type="query" useSmart="true"
class="org.wltea.analyzer.lucene.IKAnalyzer" />
</fieldType>

高级期:windows环境下部署

因项目需要,只能在windows环境下部署,·采用solr 做成windows服务 文章中说的方法,记录如下。

  1. 下载NSSM这个工具,地址是http://www.nssm.cc/download,复制NSSM.exe到solr的bin目录下,按shift键右键bin文件夹,选择菜单“从此处打开命令窗口”启动cmd命令窗口;
  2. 输入 nssm install solr
  3. 好solr的启动文件 solr.cmd,启动参数Arguments 里面填写 start -f -p 8983-f是必须填写的)
  4. 要删除该服务可以用windows自带的命令,sc delete <服务名> ,注意要用超级管理员启动cmd

参考:

  • 增量导入数据——DeltaImport
  • 通过配置apache solr的last_index_time实现dataimport导入功能支持增量更新delta-import索引功能
  • solr 的全量更新与增量更新_数据库_weixin_30481087的博客-CSDN博客
  • Solr的学习使用之(七)Solr高级查询facet、facet.pivot简介 - OnTheRoad_Lee
  • http://www.voidcn.com/article/p-kpkgpxdg-btr.html

附:ContentStreamDataSource使用方法

<dataConfig>
<dataSource name="streamsrc" type="ContentStreamDataSource" loggerLevel="TRACE" />
<document>
    <entity
        stream="true"
        name="streamxml"
        dataSource="streamsrc1"
        processor="XPathEntityProcessor"
        rootEntity="true"
        forEach="/books/book"
        transformer="TemplateTransformer" >
            <field column="load" template="some static payload"/>
            <field column="b_title" xpath="/books/book/name"/>
    </entity>
</document>
</dataConfig>

curl -X POST 
http://xxx.yyy.zzz/xmlimport 
-H 'content-type: multipart/form-data; boundary=----    WebKitFormBoundary7MA4YWxkTrZu0gW' 
-F 'stream.body=<?xml version="1.0" encoding="utf-8"?>
<books>
 <book>
 <name>NAME1</name>
 </book>
 <book>
<name>NAME2</name>
</book>
</books>' 
-F commit=true 
-F debug=true 
-F clean=false 
-F command=full-import
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值