当前位置:   article > 正文

postgresql全文检索之zhparser编译及使用

zhparser

Zhparsera源码;https://github.com/amutu/zhparser

概述:Zhparser是一个PostgreSQL扩展,用于中文全文搜索。它实现了一个基于简单中文分词(SCWS)的中文解析器。 SCWS 是 Simple Chinese Word Segmentation 的首字母缩写(即:简易中文分词系统)。这是一套基于词频词典的机械式中文分词引擎,它能将一整段的中文文本基本正确地切分成词。词是 中文的最小语素单位,但在书写时并不像英语会在词之间用空格分开,所以如何准确并快速分词一直是中文分词的攻关难点。

一、下载zhparser分词词典文件scws

wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2
#解压
tar -xf scws-1.2.3.tar.bz2
#切换目录
cd scws-1.2.3/
./configure
make install
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

二、下载zhparser插件

git clone https://github.com/amutu/zhparser.git
cd zhparser/
make && make install
  • 1
  • 2
  • 3

三、使用

CREATE EXTENSION zhparser;
#CREATE EXTENSION
CREATE TEXT SEARCH CONFIGURATION zhcfg (PARSER = zhparser);  
#CREATE TEXT SEARCH CONFIGURATION
#  添加名词(n)、动词(v)、形容词(a)、成语(i)、叹词(e)、习用语(l)和简称(j)七种分词策略:
ALTER TEXT SEARCH CONFIGURATION zhcfg ADD MAPPING FOR n,v,a,i,e,l,j WITH simple;
#ALTER TEXT SEARCH CONFIGURATION
highgo=# SELECT to_tsvector('zhcfg', '有组织推进战略导向的体系化基础研究');
                               to_tsvector                               
-------------------------------------------------------------------------
 '体系化':6 '基础':7 '导向':5 '战略':4 '推进':3 '有':1 '研究':8 '组织':2
(1 行记录)

  --1)创建使用zhparser作为解析器的全文搜索的配置

   create text search configuration zh_cn (PARSER = zhparser);

 -- 2)解析器配置
   -- zhparser.multi_short = f    短词复合
  --  zhparser.multi_duality = f  散字二元复合
 --   zhparser.multi_zmain = f    重要单字复合
 --   zhparser.multi_zall = f     全部单字复合
 --   zhparser.punctuation_ignore = f   忽略所有的标点等特殊符号
  --  zhparser.seg_with_duality = f   闲散文字自动以二字分词法聚合
  --  zhparser.dict_in_memory = f         将词典全部加载到内存里
 --   zhparser.extra_dicts = ‘dict_extra.txt,mydict.xdb’   用户自定义词典
--  此处先不配置采用默认的,后面根据业务场景的使用再进行特殊配置;默认全部off。

--词性配置
--  zhparser目前支持26种词性配置,即26个英文字母代表的词性;其词性标注来自北大词性标注版本。
--  1)将词性添加到词典使其支持此类词性

  highgo=# alter text search configuration zh_cn add mapping for a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z with simple;
 --注:①词性配置带有唯一约束;②这里的simple是PostgreSQL自带的词典模板,包含:Simple Dictionary、Synonym Dictionary、Thesaurus Dictionary、Ispell Dictionary、Snowball Dictionary等;词典的目的是为了忽略文本中重复出现次数多而又没啥具体意义的词,比如:的、了等;这些词在本模块称为“Stop Words”,可以称为可忽略词。
 -- 2)相关配置在数据库的体现,如下:
     --已存在的解析器
zhparser=# \dFp
 List of text search parsers
 Schema  |  Name  |  Description
-----------+----------+---------------------
pg_catalog | default  | default word parser
public   | zhparser |

zhparser=# \dFt 
                    List of text search templates

   Schema   |   Name    |        Description

------------+-----------+-----------------------------------------------------------

 pg_catalog | ispell    | ispell dictionary
 pg_catalog | simple    | simple dictionary: just lower case and check for stopword
 pg_catalog | snowball  | snowball stemmer
 pg_catalog | synonym   | synonym dictionary: replace word by its synonym
 pg_catalog | thesaurus | thesaurus dictionary: phrase by phrase substitution
  
 --默认的对应每一种语言的语义词干,共29种
zhparser=# \dFd  
                             List of text search dictionaries

   Schema   |      Name       |                        Description

------------+-----------------+-----------------------------------------------------------
 pg_catalog | arabic_stem     | snowball stemmer for arabic language
 pg_catalog | armenian_stem   | snowball stemmer for armenian language
 pg_catalog | basque_stem     | snowball stemmer for basque language
 pg_catalog | catalan_stem    | snowball stemmer for catalan language
 pg_catalog | danish_stem     | snowball stemmer for danish language
 pg_catalog | dutch_stem      | snowball stemmer for dutch language
 pg_catalog | english_stem    | snowball stemmer for english language
 pg_catalog | finnish_stem    | snowball stemmer for finnish language

   --各种语言的搜索配置,共29种

zhparser=# \dF 
              List of text search configurations
   Schema   |    Name    |              Description
-----------+------------+---------------------------------------
 pg_catalog | arabic     | configuration for arabic language
 pg_catalog | armenian   | configuration for armenian language
 pg_catalog | basque     | configuration for basque language
 pg_catalog | catalan    | configuration for catalan language
 pg_catalog | danish     | configuration for danish language
 pg_catalog | dutch      | configuration for dutch language
 pg_catalog | simple     | simple configuration
 pg_catalog | tamil      | configuration for tamil language
 pg_catalog | turkish    | configuration for turkish language
 pg_catalog | yiddish    | configuration for yiddish language
 public     | zh_cn      |

--针对各种语言搜索配置的语义词干词性,太多了,只展示我们自己配置的
zhparser=# \dF+  
Text search configuration "public.zh_cn"
Parser: "public.zhparser"
 Token | Dictionaries
-------+--------------
 a     | simple
 b     | simple
 c     | simple
 d     | simple
 e     | simple
 f     | simple
 g     | simple
 h     | simple
 i     | simple
 j     | simple
 k     | simple
 l     | simple
 m     | simple
 n     | simple
 o     | simple
 p     | simple
 q     | simple
 r     | simple
 s     | simple
 t     | simple
 u     | simple
 v     | simple
 w     | simple
 x     | simple
 y     | simple
 z     | simple

--测试
create table test_zhparser(pn_name varchar(1000));
insert into test_zhparser values ('三次学习会议精神');
create index idx_pname on test_zhparser using gin(to_tsvector('zh_cn', pn_name));
zhparser=# select  pn_name from test_zhparser where to_tsvector('zh_cn', pn_name) @@ plainto_tsquery('zh_cn', '导向基础研究');
         pn_name
------------------------------------
 有组织推进战略导向的体系化基础研究
 前沿导向的探索性基础研究
 市场导向的应用性基础研究

--解析函数
 --to_tsvector([config regconfig,]document text)  
 --用于将文本(查询字段)转换为tsvector数据类型;
 --将文本文档解析为标记,将标记减少为单词,并返回一个tsvector,列出单词及其在文档中的位置。文档将根据指定的或默认的文本搜索配置进行处理。
zhparser=# SELECT to_tsvector('english', 'a fat  cat sat on a mat - it ate a fat rats');
                     to_tsvector
-----------------------------------------------------
 'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4

zhparser=# SELECT to_tsvector('zh_cn', 'a fat  cat sat on a mat - it ate a fat rats');
                                     to_tsvector
--------------------------------------------------------------------------------------
 '-':8 'a':1,6,11 'ate':10 'cat':3 'fat':2,12 'it':9 'mat':7 'on':5 'rats':13 'sat':4

zhparser=# SELECT to_tsvector('english', '有组织推进战略导向的体系化基础研究');
              to_tsvector
----------------------------------------
 '有组织推进战略导向的体系化基础研究':1

zhparser=# SELECT to_tsvector('zh_cn', '有组织推进战略导向的体系化基础研究');
                                       to_tsvector
------------------------------------------------------------------------------------------
 '体系':8 '体系化':7 '基础':9 '导向':5 '战略':4 '推进':3 '有':1 '的':6 '研究':10 '组织':2
  -- 以上示例中,生成的tsvector不包含单词a、on或it,单词rats变为rat,标点符号-被忽略,这些词在官方称为“Stop Words”,就是可忽略词;a、on、it已被忽略,的却没有被忽略,这是词典配置策略问题;请参考最后的《Stop Words》章节。
 --将查询条件转换为tsquery数据类型的to_tsquery、plasto_tsquery、phraseto_tsquery和websearch_to_tsquery函数
   to_tsquery([config regconfig,]querytext文本)
    -- 从querytext创建一个tsquery值,该值必须由tsquery运算符&(AND)、|(OR)、!分隔的单个标记组成!(NOT)和<->(FOLLOWED BY),可以使用括号分组;换句话说,to_tsquery的输入必须遵循tsquery输入的一般规则。
   plainto_tsquery([ config regconfig, ] querytext text)
   --将未格式化的文本querytext转换为tsquery值,文本与to_tsvector一样被解析和规范化,然后将&(and)运算符插入保留单词之间。
   phraseto_tsquery([config regconfig,]querytext文本)
    -- 类似plainto_tsquery ,只是它在保留单词之间插入<->(FOLLOWED BY)运算符,而不是&(AND)运算符;此外,可忽略词不是简单地丢弃,而是通过插入<N>运算符而不是<->运算符来解析。
   ④websearch_to_tsquery([config regconfig,]querytext text--使用另一种语法从querytext创建tsquery值,其中简单的无格式文本是有效的;与plainto_tsquery和phraseto_tsquery不同,它还识别某些运算符。、

zhparser=# select to_tsquery('zh_cn', '有组织推进战略导向的体系化基础研究');
                               to_tsquery                       
--------------------------------------------------------------------------------------------------------
'有' <-> '组织' <-> '推进' <-> '战略' <-> '导向' <-> '的' <-> '体系化' <-> '体系' <-> '基础' <-> '研究'

zhparser=# select plainto_tsquery('zh_cn', '有组织推进战略导向的体系化基础研究');

                              plainto_tsquery                       
--------------------------------------------------------------------------------------

'有' & '组织' & '推进' & '战略' & '导向' & '的' & '体系化' & '体系' & '基础' & '研究‘

zhparser=# select phraseto_tsquery('zh_cn', '有组织推进战略导向的体系化基础研究');

                               phraseto_tsquery                     
---------------------------------------------------------------------------------------------------------
'' <-> '组织' <-> '推进' <-> '战略' <-> '导向' <-> '' <-> '体系化' <-> '体系' <-> '基础' <-> '研究'

zhparser=# select websearch_to_tsquery('zh_cn', '有组织推进战略导向的体系化基础研究');

                               websearch_to_tsquery             
---------------------------------------------------------------------------------------------------------
'' <-> '组织' <-> '推进' <-> '战略' <-> '导向' <-> '' <-> '体系化' <-> '体系' <-> '基础' <-> '研究‘

--高亮显示
zhparser=# select ts_headline('zh_cn',pn_name,plainto_tsquery('zh_cn', '导向性基础研究')) from test_zhparser where to_tsvector('zh_cn', pn_name) @@ plainto_tsquery('zh_cn', ' 导向基础研究');

                   ts_headline
-------------------------------------------------------------
有组织推进战略<b>导向</b>的体系化体系<b>基础</b><b>研究</b>
前沿<b>导向</b>的探索性探索<b>基础</b><b>研究</b>
市场<b>导向</b>的应用性应用<b>基础</b><b>研究</b>

zhparser=# select ts_headline(  'zh_cn', pn_name, plainto_tsquery('zh_cn', '导向性基础研究'), 'StartSel=''<font color="red">'',  StopSel=</font>' ) from test_zhparser where to_tsvector('zh_cn', pn_name) @@ plainto_tsquery('zh_cn', ' 导向基础研究');
                              ts_headline
-------------------------------------------------------------------------------------------------------------------

有组织推进战略<font color="red">导向</font>的体系化体系<font color="red">基础</font><font color="red">研究</font>
前沿<font color="red">导向</font>的探索性探索<font color="red">基础</font><font color="red">研究</font>
市场<font color="red">导向</font>的应用性应用<font color="red">基础</font><font color="red">研究</font>

ts_headline([ config regconfig, ] document text, query tsquery [, options text ]) returns text
 --options支持以下选项:
  -- MaxWords,MinWords(整数):这些数字决定要输出的最长和最短标题;默认值为35和15。
  -- ShortWord(整数):此长度或以下的单词将在标题的开头和结尾处删除,除非它们是查询词,默认值为3。
  -- HighlightAll(布尔值):如果为true,则整个文档将用作标题,忽略前面的三个参数;默认值为false。
  -- MaxFragments(整数):要显示的最大文本片段数;默认值为零。
  -- StartSel、StopSel(字符串):用于分隔文档中出现的查询词的字符串,以将其与其他摘录词区分开来;默认值为“<b>”和“</b>”,可适用于HTML输出。
   --FragmentDelimiter(字符串):当显示多个片段时,这些片段将由该字符串分隔;默认值为“…”。

--排序
 -- 根据文档与查询条件的相关性,对查询结果进行排序,优先显示最相关的结果。提供了两个排序函数ts_rank和ts_rank_cd,相关性包含了频次、词位、接近度、结构信息等维度。其实相关性的概念是非常模糊的,而且与实际的业务场景是紧密相关的。这两个内置函数只是例子,我们可以编写自己的排序函数或者与其他相关因素叠加,来实现匹配自己业务场景的需求。

 -- 频次:搜索条件出现的次数
 -- 词位:搜索条件在文本中出现的位置,从头开始算,可忽略词不计算位置
 -- 接近度:搜索条件和文本位置的接近程度
 -- 结构信息:搜索条件在文本中的重要程度
 -- 通过以下例子可以展示相关性的几个维度标准,如下:

--zhparser.multi_short='on'
--zhparser.multi_duality='on'
--zhparser.multi_zmain='on'
--zhparser.multi_zall='on'

--自定义词库 
 -- 两种方式:
 --  1)解析器配置:zhparser.extra_dicts = 'dict_extra.txt,mydict.xdb'
 --  2)zhparser自带的表zhprs_custom_word,插入自定义词语,同步后,重新创建连接即可生效;

zhparser=# SELECT * FROM ts_parse('zhparser', '支付宝使用很方便');
 tokid | token
-------+-------
   118 | 支付
   110 |118 | 使用
   100 |118 | 方便

zhparser=# insert into zhparser.zhprs_custom_word values('支付宝');
zhparser=#  select sync_zhprs_custom_word();
zhparser=# SELECT * FROM ts_parse('zhparser', '支付宝使用很方便');
 tokid | token
-------+--------
   120 | 支付宝
   118 | 使用
   100 |118 | 方便

zhparser=# SELECT * FROM ts_parse('zhparser', '请支付宝贝订单');
 tokid | token 
-------+-------
   118 |118 | 支付
   110 | 宝贝
   110 | 订单

--自定义词库虽然添加了“支付宝”,但是此处还是将“支付”、“宝贝”分开了,体现了zhparser的作用。

zhparser=# select * from  zhparser.zhprs_custom_word;
   word | tf  | idf | attr
----------+------+-----+------
 资金压力|  1 |  1 | @
 支付宝  |  1 |  1 | @

-- 此处将“@”改为“!”也可以使其不起作用,也可以where条件删除

zhparser=# delete from zhparser.zhprs_custom_word;
zhparser=#  select sync_zhprs_custom_word();
zhparser=# SELECT * FROM ts_parse('zhparser', '我不喜欢支付宝支付');
 
tokid | token
-------+-------
   114 |100 |118 | 喜欢
   118 | 支付
   110 |118 | 支付

--分词粒度

  --解析器配置:
  -- zhparser.multi_short = f    短词复合
 --  zhparser.multi_duality = f  散字二元复合
 --  zhparser.multi_zmain = f    重要单字复合
 --  zhparser.multi_zall = f     全部单字复合
  -- zhparser.punctuation_ignore = f   忽略所有的标点等特殊符号
  -- zhparser.seg_with_duality = f   闲散文字自动以二字分词法聚合
 --  默认值全部为off,效果如下:

zhparser=# select to_tsvector('zh_cn', '前沿导向的探索性基础研究');
               to_tsvector
-------------------------------------------------------
 '前沿':1 '基础':5 '导向':2 '探索性':4 '的':3 '研究':6
  
--进行如下设置:

--zhparser.multi_short='on'
--zhparser.multi_duality='on'
--zhparser.multi_zmain='on'
--zhparser.multi_zall='on'
  再次执行,解析结果如下:
zhparser=# select to_tsvector('zh_cn', '前沿导向的探索性基础研究');
                               to_tsvector
-----------------------------------------------------------------------------------------------------
 '前':2 '前沿':1 '向':6 '基':15 '基础':14 '导':5 '导向':4 '性':13 '探':11 '探索':9 '探索性':8 '沿':3 '的':7 '研':18 '研究':17 '础':16 '究':19 '索':12 '索性':10
  由此可见,解析粒度更细了,但是这并不一定业务需要;粒度越细意味着耗时越长;需要根据业务具体调节。

--Stop Words
-- Stop Words 是非常常见的词,几乎出现在每个文档中,没有实际价值。因此,在全文搜索的上下文中可以忽略它们。例如,每个英文文本都包含像a和the这样的单词,因此将它们存储在索引中是无用的。
-- 使用PostgreSQL默认配置效果如下:
zhparser=# SELECT to_tsvector('english', 'a fat  cat sat on a mat - it ate a fat rats');
                 to_tsvector
-----------------------------------------------------
 'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4

zhparser=# SELECT to_tsvector('zh_cn', '有组织推进战略导向的体系化基础研究');
                      to_tsvector
------------------------------------------------------------------------------------------
 '体系':8 '体系化':7 '基础':9 '导向':5 '战略':4 '推进':3 '有':1 '的':6 '研究':10 '组织':2
  --这里english使用了默认配置的词典库,词典库中包含a、on、it这些词;下面我们通过配置词典来实现自动忽略“的”的效果。
 -- Stop Words 词典存放在$SHAREDIR/tsearch_data目录下,如下:

[root@localhost tsearch_data]# pwd
/opt/highgo/hgdb-see-4.5.8/share/postgresql/tsearch_data

[root@localhost tsearch_data]# ls

danish.stop    english.stop  german.stop            hunspell_sample_long.affix  hunspell_sample_num.dict  italian.stop    portuguese.stop  spanish.stop        thesaurus_sample.ths  xsyn_sample.rules
dict.utf8.xdb  finnish.stop  hungarian.stop         hunspell_sample_long.dict   ispell_sample.affix       nepali.stop     rules.utf8.ini   swedish.stop        turkish.stop
dutch.stop     french.stop   hunspell_sample.affix  hunspell_sample_num.affix   ispell_sample.dict        norwegian.stop  russian.stop     synonym_sample.syn  unaccent.rules

#查看默认忽略的单词
[root@localhost tsearch_data]# more english.stop

  --进行以下配置:

zhparser=# alter text search configuration zh_cn drop mapping for a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z ;
[root@localhost tsearch_data]# touch chinese.stop
[root@localhost tsearch_data]# vim chinese.stop 
--添加"的"并报错存

zhparser=# CREATE TEXT SEARCH DICTIONARY chinese_dict (TEMPLATE = pg_catalog.simple,STOPWORDS = chinese);
zhparser=# alter text search configuration zh_cn add mapping for a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z with chinese_dict;

--重启数据库,再次测试效果:

zhparser=# SELECT to_tsvector('zh_cn', '有组织推进战略导向的体系化基础研究');
                           to_tsvector
-----------------------------------------------------------------------------------
 '体系':8 '体系化':7 '基础':9 '导向':5 '战略':4 '推进':3 '有':1 '研究':10 '组织':2
--检索内容中的“的”已经被忽略
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360

Author: HG-QIURU

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/981646
推荐阅读
相关标签
  

闽ICP备14008679号