一、多表、多数据源 数据导入的问题
之前介绍过通过data-config.xml 导入数据的问题,实际开发过程中可能会遇到多表、甚至是多数据源的问题,以下我根据实际业务场景说下该如何解决该问题。
只需要,更改data-config.xml的写法就行了。
<dataConfig>
<!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->
<dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
<dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
<document name = "insert_test">
<entity name="video" transformer="DateFormatTransformer"
query="SELECT
CONCAT(v.vdoid,'video') as id,
v.vdoid AS vdoid,
v.catid AS catid,
v.catids AS catids,
v.proid AS proid,
v.title AS title,
ifnull(v.content, '') AS content,
v.num AS num,
v.vdo_type AS vdoType,
v.play_url AS playUrl,
LEFT (
FROM_UNIXTIME(v.publish_time),
10
) AS publishTime,
v. STATUS AS STATUS,
v.keywords AS keywords,
v.coverpic_url AS coverpicUrl,
v.end_time AS endTime,
v.start_time AS startTime,
FROM_UNIXTIME(
v.update_time,
'%Y-%m-%d %H:%m:%s'
) AS updateTime,
v.isTop AS isTop,
parent_id AS parentId,
p.proid AS proid,
ifnull(p. NAME, '') AS proName,
ifnull(p.org, '') AS proOrg,
ifnull(p.title, '') AS proTitle,
ifnull(p.department, '') AS proDepartment,
ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
FROM
db_yxj_content.t_video v
LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
WHERE
v. STATUS = 0
AND (
(
v.vdo_type = 1
AND (
classify_flag = 2
OR length(v.play_url) > 0
)
)
OR v.vdo_type = 10
)">
<field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
<field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
</entity>
</document>
</dataConfig>
dataSource : 数据源的配置,这里用的是mysql ,如果是多数据源需要根据数据库的连接属性,配置多个相对应的dataSource
sql: document 下的entity 中 sql 的写法,需要把 数据库名称写上,如上写法 数据库名.表名:db_yxj_content.t_video
二、全量导入、增量导入的问题
之前介绍过通过页面操作数据导入的方法。接下来介绍一下 ,如何配置 增量以及全量导入。
1.全量导入
全量导入很简单,只要成功配置好data-config.xml 就可以通过点击Execute 按钮 完成全量导入。
2.增量导入
这里主要介绍下 增量导入的问题。顾名思义,增量导入即只导入部分数据,这在实际开发中一定是用的到的,因为在我们上线前,可以把数据一次性导入solr完成数据同步,但是上线后新产生的数据或者某些数据被删除或者修改,则需要动过增量导入的方式同步到solr中,data-config.xml配置如下:
<dataConfig>
<!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->
<dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
<dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
<document name = "insert_test">
<entity name="video" transformer="DateFormatTransformer"
query="SELECT
CONCAT(v.vdoid,'video') as id,
v.vdoid AS vdoid,
v.catid AS catid,
v.catids AS catids,
v.proid AS proid,
v.title AS title,
ifnull(v.content, '') AS content,
v.num AS num,
v.vdo_type AS vdoType,
v.play_url AS playUrl,
LEFT (
FROM_UNIXTIME(v.publish_time),
10
) AS publishTime,
v. STATUS AS STATUS,
v.keywords AS keywords,
v.coverpic_url AS coverpicUrl,
v.end_time AS endTime,
v.start_time AS startTime,
FROM_UNIXTIME(
v.update_time,
'%Y-%m-%d %H:%m:%s'
) AS updateTime,
v.isTop AS isTop,
parent_id AS parentId,
p.proid AS proid,
ifnull(p. NAME, '') AS proName,
ifnull(p.org, '') AS proOrg,
ifnull(p.title, '') AS proTitle,
ifnull(p.department, '') AS proDepartment,
ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
FROM
db_yxj_content.t_video v
LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
WHERE
v. STATUS = 0
AND (
(
v.vdo_type = 1
AND (
classify_flag = 2
OR length(v.play_url) > 0
)
)
OR v.vdo_type = 10
)"
deltaImportQuery ="SELECT
CONCAT(v.vdoid,'video') as id,
v.vdoid AS vdoid,
v.catid AS catid,
v.catids AS catids,
v.proid AS proid,
v.title AS title,
ifnull(v.content, '') AS content,
v.num AS num,
v.vdo_type AS vdoType,
v.play_url AS playUrl,
LEFT (
FROM_UNIXTIME(v.publish_time),
10
) AS publishTime,
v. STATUS AS STATUS,
v.keywords AS keywords,
v.coverpic_url AS coverpicUrl,
v.end_time AS endTime,
v.start_time AS startTime,
FROM_UNIXTIME(
v.update_time,
'%Y-%m-%d %H:%m:%s'
) AS updateTime,
v.isTop AS isTop,
parent_id AS parentId,
p.proid AS proid,
ifnull(p. NAME, '') AS proName,
ifnull(p.org, '') AS proOrg,
ifnull(p.title, '') AS proTitle,
ifnull(p.department, '') AS proDepartment,
ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
FROM
db_yxj_content.t_video v
LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
WHERE
v.vdoid = '${dih.delta.id}'"
deltaQuery ="SELECT
v.vdoid AS id
FROM
db_yxj_content.t_video v
LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
WHERE
v. STATUS = 0
AND (
(
v.vdo_type = 1
AND (
classify_flag = 2
OR length(v.play_url) > 0
)
)
OR v.vdo_type = 10
)
AND ( FROM_UNIXTIME(
v.update_time,
'%Y-%m-%d %H:%m:%s'
) > '${dataimporter.video.last_index_time}'
OR
FROM_UNIXTIME(
v.publish_time,
'%Y-%m-%d %H:%m:%s'
) > '${dataimporter.video.last_index_time}'
)
">
<field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
<field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
</entity>
</document>
</dataConfig>
网上对以下字段的解释很多,我这里说下自己的理解:
query:全量查询执行的sql ,所以一般不需要带上条件
deltaImportQuery :增量插入,这里需要带上条件,条件的写法很固定,需要通过 deltaQuery 执行后得到的结果 进行数据插入
deltaQuery :主要服务与deltaImportQuery 在做增量插入的时候,通过该sql 查询出需要增量数据的ID,有两点需要注意,1~这里必须查出ID并且只能叫ID 2~这个sql 的条件的写法也很固定, '${dataimporter.video.last_index_time}' 指的是dataimport.properties 中的 时间,这个时间是指,最后一次执行增量或者全量导入的时间,只要数据发生变化了,dataimport.properties 中的时间会自动更新。
通过以上三个字段的介绍,应该能明白,solr的增量导入 依据的其实就是,数据库的变化(前提是数据库中需要有update_time 字段,每次数据发生变化要更新该字段,我这里的update_time 是long 类型的unix十进制时间所以写法,需要根据自己实际情况改变)比较最后一次改变数据的时间,做增量数据更改。
三、一个库 中多个搜索的问题
实际开发过程中,我们不可能为每一个搜索都创建一个core ,我们的数据大都放到同一个core 中,这样便于维护。那么怎么保证搜索视频的时候不会把文章搜索出来呢。其实很简单,还是看data-config.xml配置如下:
<dataConfig>
<!-- <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio" user="yxj" password="yxj@2015" /> -->
<dataSource name ="db_yxj_content" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_content" user="xxx" password="xxx" />
<dataSource name ="db_yxj_user" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://123.59.41.168:3306/db_yxj_user" user="xxx" password="xxx" />
<document name = "insert_test">
<entity name="video" transformer="DateFormatTransformer"
query="SELECT
CONCAT(v.vdoid,'video') as id,
v.vdoid AS vdoid,
v.catid AS catid,
v.catids AS catids,
v.proid AS proid,
v.title AS title,
ifnull(v.content, '') AS content,
v.num AS num,
v.vdo_type AS vdoType,
v.play_url AS playUrl,
LEFT (
FROM_UNIXTIME(v.publish_time),
10
) AS publishTime,
v. STATUS AS STATUS,
v.keywords AS keywords,
v.coverpic_url AS coverpicUrl,
v.end_time AS endTime,
v.start_time AS startTime,
FROM_UNIXTIME(
v.update_time,
'%Y-%m-%d %H:%m:%s'
) AS updateTime,
v.isTop AS isTop,
parent_id AS parentId,
p.proid AS proid,
ifnull(p. NAME, '') AS proName,
ifnull(p.org, '') AS proOrg,
ifnull(p.title, '') AS proTitle,
ifnull(p.department, '') AS proDepartment,
ifnull(CONCAT(p. NAME, p.title, ' ', p.org), '') AS tempContent
FROM
db_yxj_content.t_video v
LEFT JOIN db_yxj_user.t_professor p ON v.proid = p.proid
WHERE
v. STATUS = 0
AND (
(
v.vdo_type = 1
AND (
classify_flag = 2
OR length(v.play_url) > 0
)
)
OR v.vdo_type = 10
)">
<field column='publishTime' dateTimeFormat='yyyy-MM-dd' />
<field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
</entity>
<entity name="article" transformer="DateFormatTransformer"
query="SELECT
CONCAT(art.artid,'article') as id,
art.artid as artid,
art.art_date as artDate,
art.num as num,
art.title as artTitle,
art.brief as brief,
art.minpic_url as minpicUrl,
art.content as artContent,
art.update_time as artUpdateTime
FROM
db_yxj_content.t_article art
WHERE
art. STATUS = 0">
<field column='updateTime' dateTimeFormat='yyyy-MM-dd HH:mm:ss' />
</entity>
</document>
</dataConfig>
如上,可以看到document 下的有多个entity ,其实每个entity 代表的都是不同的数据组。不过虽然数据都被用组分开了,但是查询的时候 还会出现,另外一个问题,那就是 ,通过模糊查询把,文章视频都查了出来,但是我们这次查询其实只是想查询到,视频数据或者文章数据,那么该如何避免该问题呢?
如下:文章和视频都被查询了出来
通过为设置copy字段,并创建索引的方式,配置managed-schema 如下:
<!-- t_video 普通字段不需要建立索引-->
<field name="vdoid" type="plong" indexed="true" stored="true"/>
<field name="catid" type="plong" indexed="false" stored="true"/>
<field name="catids" type="string" indexed="false" stored="true"/>
<field name="proid" type="plong" indexed="false" stored="true"/>
<field name="title" type="string" indexed="false" stored="true"/>
<field name="num" type="pint" indexed="false" stored="true"/>
<field name="publishTime" type="pdate" indexed="false" stored="true"/>
<field name="coverpicUrl" type="string" indexed="false" stored="true"/>
<field name="endTime" type="plong" indexed="false" stored="true"/>
<field name="updateTime" type="pdate" indexed="false" stored="true"/>
<field name="content" type="text_general" indexed="false" stored="true"/>
<field name="playUrl" type="string" indexed="false" stored="true"/>
<field name="vdoType" type="pint" indexed="false" stored="true"/>
<field name="startTime" type="plong" indexed="false" stored="true"/>
<field name="parentId" type="string" indexed="false" stored="true"/>
<field name="keywords" type="string" indexed="false" stored="true"/>
<!-- guan lian -->
<field name="tempContent" type="string" indexed="false" stored="true"/>
<field name="proName" type="string" indexed="false" stored="true"/>
<field name="proOrg" type="string" indexed="false" stored="true"/>
<field name="proDepartment" type="string" indexed="false" stored="true"/>
<field name="proTitle" type="string" indexed="false" stored="true"/>
<!-- copyField就引出了solr的一个全文检索的概念,如果我要实现一个搜索,而我要搜索的属性有很多个那么应该使用如下配置方法-->
<copyField source="title" dest="text_video"/>
<copyField source="proName" dest="text_video"/>
<copyField source="proOrg" dest="text_video"/>
<copyField source="content" dest="text_video"/>
<!-- 搜索用建立索引 不需要返回-->
<field name="text_video" type="text_ik" indexed="true" stored="false" multiValued="true" />
<!-- t_article 普通字段不需要建立索引-->
<field name="artid" type="plong" indexed="true" stored="true" />
<field name="artDate" type="string" indexed="false" stored="true"/>
<field name="artNum" type="pint" indexed="false" stored="true"/>
<field name="artTitle" type="string" indexed="false" stored="true"/>
<field name="brief" type="string" indexed="false" stored="true"/>
<field name="minpicUrl" type="string" indexed="false" stored="true"/>
<field name="artContent" type="text_general" indexed="false" stored="true"/>
<field name="artUpdateTime" type="plong" indexed="false" stored="true"/>
<!-- copyField就引出了solr的一个全文检索的概念,如果我要实现一个搜索,而我要搜索的属性有很多个那么应该使用如下配置方法-->
<copyField source="artTitle" dest="text_article"/>
<copyField source="brief" dest="text_article"/>
<copyField source="artContent" dest="text_article"/>
<!-- 搜索用建立索引 不需要返回-->
<field name="text_article" type="text_ik" indexed="true" stored="true" multiValued="true" />
通过以上配置。可以看到,我把需要索引的字段,通过copyField 的方式 copy 到 新建立的一个field中,如上配置中分别是text_video、text_article,我把这两个file 叫做搜索域,并且根据实际情况设置 type 我用了IK分词器,设置stored 是否返回,为这里只是为了测试设置成true,为了减少开销可以设置为false。并且只对搜索域字段创建索引,可以看到其他字段我都没有创建索引,因为我只会对该字段进行搜索,我觉得这样有两点好处 1.避免不必要的索引开销 2. 规避不需要的域的数据。
okay~这样基本上,不管是导入还是查询,都能运用到日常的生产开发中了。
四、 定时创建全量索引和增量索引 ,以及DIH的配置 参数相关
好,各位有没有发现,如上问题解决完后,还是不能真正的投产,因为,我们不可能通过solr 的管理页面,每次去认为的维护数据,你可能想到了,生产发布前,进行全量导入,之后数据的维护通过定时任务或者,每次对数据增删改查的时候,通过接口对solr进行同步维护。以上肯定可以解决关于数据维护的问题,但是成本太高了,如此优秀的solr 肯定有办法帮你解决这个问题。那就是 solr本身定时器的使用,不过有点繁琐,solr 5以后的版本 定时器的维护也停止了,所以现有的jar包没办法兼容solr7的定时器的使用。需要对jar 进行改造,改造后的jar 可以在如下链接下载:
https://download.csdn.net/download/csdn_fan321/10222478
现在说下如何使用该jar包。这里默认你看过我之前写的博客。一下路径都是按照之前的配置来的。
1.首先需要在E:solrhome 下创建文件夹conf ,并且创建文件dataimport.properties
写入如下内容,我只用到了 ,增量维护的部分,全量可以定时导入的,具体可以到网上看下,只是配置问题。
#################################################
# #
# 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=testCore
# solr server name or IP address
# [defaults to localhost if empty]
server=localhost
# solr server port
# [defaults to 80 if empty]
# 你得solr 的启动的端口号
port=8888
# application name/context
# [defaults to current ServletContextListener's context (app) name]
# webapp的名称
webapp=solr
# URL params [mandatory]
# remainder of URL
# 请求的参数
params=/dataimport?command=delta-import&clean=false&commit=true&optimize=false&wt=json&indent=on&verbose=false&debug=true
# schedule interval
# number of minutes between two runs
# [defaults to 30 if empty]
# 执行的频率,分钟
interval=1
如上配置,简单易懂,我对请求参数 即DIH的配置 单独讲解下,和页面上复选框其实是一一对应的。
entity:entity是document下面的标签(data-config.xml)。使用这个参数可以有选择的执行一个或多个entity 。使用多个entity参数可以使得多个entity同时运行。如果不选择此参数那么所有的都会被运行。
clean:选择是否要在索引开始构建之前删除之前的索引,默认为true
commit:选择是否在索引完成之后提交。默认为true
optimize:是否在索引完成之后对索引进行优化。默认为true
debug:是否以调试模式运行,适用于交互式开发(interactive development mode)之中。
indent:on或者true是打开。false 关闭。返回的数据 是 缩进的。
2.tomcat 的 webappssolrWEB-INF 下 web.xml ,web-app标签下 添加如下代码:
<listener>
<listener-class>org.apache.solr.handler.dataimport.scheduler.ApplicationListener</listener-class>
</listener>
3.需要将刚才下载的jar(solr-dataimportscheduler-1.1.0.jar) 放入到.tomcat 的 webappssolrWEB-INFlib 下
4.重启tomcat ,你能看到
定时器被执行了,影响的行数为0
duang~ 搞定
五、关于ID字段 如何避免同一核中多个搜索避免ID重复的问题
还有个问题在这里跟大家分享下,就是在solr实际使用中,发现 ID 字段是必须设置的。就是说每个entity 中的数据,都必须有ID ,如果不指定,那么会自动添加UUID 。这个ID 也非常重要,在做数据导入的时候,就是根据ID字段 鉴别记录是否存在,如果存在solr不会反复插入数据,他会在之前的数据上更新替换,这也是 增量导入时候 为了防止数据重复添加的依据,那么问题来了。同一个core 下 多个entity 的情况下,每个entity 都有ID这个字段,那么极有可能出现ID冲突,因为使用mysql的用户大部分都是用的mysql 的自增字段做的ID,那么就会出现在增量更新文章的时候,发现把相同ID的视频记录覆盖了的情况,这是我们必须避免的问题。我采用的是 别名的方式 回避这个问题。其实之前data-config.xml配置文件中的 sql 已经体现了。举例如下:
正常sql :select id,userName,sex from user;
优化的sql: select CONCAT(u.id,'user') as id,u.id as uid,u.userName,u.sex from user u;
这么做就是用 id+标识 当做solr的id使用,如果你需要拿到真的user的id 那就使用uid咯
————————————————
版权声明:本文为CSDN博主「阿胆Amazing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wakuangyun/article/details/80684568