-
- painless的正则匹配
- painless查找操作符
- painless匹配操作符
- painless pattern matcher
- painless CharSequence
在上一篇文章elasticsearch painless最强教程中,已经介绍了一些painless的基本例子,喜欢动脑子的同学应该已经看到了plainless或者说是elasticsearch的script强大之处了。我在另外一篇文章如何使用logstash更新已有的elasticsearch记录里面介绍的用logstash更新数据的功能,在简单的需求下,完全可以用elasticsearch的painless script代替。但为什么我们还需要用logstsah,那是因为在elasticsearch中使用正则表达式去匹配,提取数据以完成数据transformation的功能,其代价是非常昂贵的。
painless除了上篇文章提到的基本功能之外,其实还支持正则表达式。但painless却是默认关闭正则表达式功能的。我们先看看官方是怎么说的:
Regexes are disabled by default because they circumvent Painless’s protection against long running and memory hungry scripts. To make matters worse even innocuous looking regexes can have staggering performance and stack depth behavior. They remain an amazing powerful tool but are too scary to enable by default. To enable them yourself set script.painless.regex.enabled: true in elasticsearch.yml. We’d like very much to have a safe alternative implementation that can be enabled by default so check this space for later developments!
可以看到,painless的最高原则是要运行得快,而用正则表达式去执行搜索的动作则会大量的消耗cpu和内存资源,有可能极大的降低painless的效率,所以官方是默认禁止这个功能的(其实在平时的query中,官方也不建议我们使用regex去搜索)。若要打开,我们必须手动的在elasticsearch的配置文件elasticsearch.yml中加入:
script.painless.regex.enabled: true
读者若要完成下面的例子,上面的这个配置是必须。
painless的正则匹配
painless通过以下的语法结构实现对正则表达式的支持:
/pattern/
:两个/
符号内的字面量会创建一个regex的pattern(匹配模式)。这也是painless创建匹配模式的唯一方式。该匹配模式是标准的java正则表达式。并且和其他的脚本语言的正则匹配类似,是可以带flag的。
Character |
Java Constant |
Example |
c
|
CANON_EQ |
'å' ==~ /å/c (open in hex editor to see)
|
i
|
CASE_INSENSITIVE |
'A' ==~ /a/i
|
l
|
LITERAL |
'[a]' ==~ /[a]/l
|
m
|
MULTILINE |
'a\nb\nc' =~ /^b$/m
|
s
|
DOTALL (aka single line) |
'a\nb\nc' =~ /.b./s
|
U
|
UNICODE_CHARACTER_CLASS |
'Ɛ' ==~ /\w/U
|
u
|
UNICODE_CASE |
'Ɛ' ==~ /ɛ/iu
|
x
|
COMMENTS (aka extended) |
'a' ==~ /a #comment/x
|
=~
: 查找操作符。如果文本的某个部分被匹配,则返回true,否则返回false。
==~
: 匹配操作符。如果文本整体匹配,则返回true,否则返回false。
painless查找操作符
我们可以通过查找操作符,寻找所有last名字里面包含有字面’b’的球员,并加上后缀match,反之,跳过。注意这里,=~
是不要求整体匹配的,是一个类似于java里contain()
的操作。
这里需要注意,无论是=~
还是==~
,左右两边一个是text,一个是pattern,只返回true or false.
painless匹配操作符
通过匹配操作符, 你可以精确的找到所有球员中名字以元音字母开头和结尾的人。
如下图,仅匹配到一人。
这里需要注意,无论是=~
还是==~
,左右两边一个是text,一个是pattern,只返回true or false。
painless pattern matcher
我们使用正则匹配,除了搜索,还经常会用到提取或替换的场景。这时,我们就需要使用pattern的matcher功能。
下图中,我们就用matcher的功能删除了所有球员last name中的元音字母。
这里只需要注意和上面提到的操作符不同的地方。我们是通过/pattern/.matcher(text).replaceAll
的方式来实现提取与转换的。因为底层是java的函数与实现,我们还可以用很熟悉的$1
或\2
的方式提取匹配项。比如:
POST hockey/player/_update_by_query
{
"script": {
"lang": "painless",
"inline": "ctx._source.last = /n([aeiou])/.matcher(ctx._source.last).replaceAll('$1')"
}
}
painless CharSequence
我们还可以在painless中使用charSequence
的replaceAll()
方法来进行更高级的提取和转换操作。因为该方法支持传入lambda表达式进行更高级的转换。例如:
POST hockey/player/_update_by_query
{
"script": {
"lang": "painless",
"inline": "ctx._source.last = ctx._source.last.replaceAll(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))"
}
}
在上例中,我们将所有的元音字母都转成大写。当然,如果你只想把第一个碰到的元音字母换成大写,可以用m -> m.group(1).toUpperCase(Locale.ROOT)
的方式。也可以用replaceFirst函数:
POST hockey/player/_update_by_query
{
"script": {
"lang": "painless",
"inline": "ctx._source.last = ctx._source.last.replaceFirst(/[aeiou]/, m -> m.group().toUpperCase(Locale.ROOT))"
}
}
具体painless里面支持哪些函数可以查看白名单