Sitemap

Utilizando Regex no Painless

4 min readSep 9, 2023

--

Estamos a algum tempo trabalhando com o Elasticsearch, utilizando ele principalmente para gerar estáticas da Sophie (para quem não sabe é um chatbot desenvolvido pela Woopi). Antes estávamos utilizando o Power BI, que apesar de ser uma ferramenta poderosa, nos trazia um custo maior, e a dificuldade para atualizar a base e de forma mais ágil. Hoje nossa base esta atualizando de forma mais ágil quase que em tempo de execução, permitindo maior agilidade e satisfação da equipe de PO e cliente.

Claro que passamos por vários desafios e muitos aprendizados, pois não tínhamos nenhum conhecimento da ferramenta. Durante esse processo de aprendizado do ELK tivemos diversas dificuldades, principalmente para encontrar a melhor forma de usa-lo para nossos propósitos. O principal é ter métricas das sessões dos usuários com o chatbot. Como por exemplo: quantas interações ocorreram, qual o tempo de resposta, qual a categoria das interações, entre outros.

Mas para chegar nessas métricas, é necessário alguma transformação na nossa base. Como por exemplo o tempo de resposta, onde calculamos a diferença do momento que o usuário responde com o momento que a Sophie responde. Esse cálculo é realizado cada vez que chega o registro no Elasticsearch, para isso utilizamos o Ingest Pipeline, umas das ferramentas que é disponibilizada. Com ele é possível realizar diversas formas de transformações. E uma das formas é o uso de scripts, a recomendada e default é linguagem Painless (confesso que não tentei mais nenhuma outra).

É uma linguagem simples e intuitiva similar ao Java, mas intuitivo para coisas simples. Problema quando é necessário algo um pouco mais complexo, a documentação é extremamente pobre. Fora a dificuldade de encontrar exemplos, alias bons exemplos, além de entender os malditos erros que ocorrem, nem sempre é claro. Mas meu último problema foi tentar extrair algumas informações da forma como a Sophie responde. A solução mais adequada para o problema é usar regex.

Agora procure bons exemplos de como usar o regex no Painless, não vai encontrar. Na documentação é sugerido usar o Pattern Class e não encontra nem informações das demais funções, é lançado para uma página do Pattern Class do Java, na qual metade dos comandos não funcionam.

Get Maiko De Andrade’s stories in your inbox

Join Medium for free to get updates from this writer.

O problema é simples em uma string a Sophie retorna algo como:

Lorem ipsum dolor sit amet. [comando name=exemplo]. Ab quasi quidem est similique [link name=outroexemplo] voluptatem et doloremque omnis a [comando name=outroexemplo] voluptatem magni.

E depois contabilizar quantos comandos e quais os comandos. Primeiro testar o Pattern que será utilizando. Para isso utilizei o Regex101, como pode ser visto abaixo:

Press enter or click to view image in full size
Testando a expressão regular

O resultado foi:

Resultado

Tendo a expressão regular, agora é criar o script no Pipeline, usando o Painless.

// Primeiro é obter o campo do registro.
// Não use String e nunca o def
String text = ctx["response"];

// Iniciando as variáveis
int find=0; // Contador de comandos encontrados
List commands=new ArrayList(); // Armazena os comandos encontrados

// Inicia a pesquisa unsando o Matcher
// Note o (?<name>\w+) aqui ele irá dar um nome para o grupo encontrado.
Matcher m = /s:integration\s*name=(?<name>\w+)/.matcher(text);

// Ficará em loop para cada vez que encontrar a expressão regular
// na string
while(m.find()) {
find +=1;

// Aquela parte da expressão ?<name>
// usaremos aqui
commands.add(m.namedGroup("name"));
}

// Salvamos os valores no registro
ctx["find"] = find;
ctx["commands"] = commands;

O resultado será:

{
_id: ObjectId("62d968518e73bd834e9d1278"),
find: 2,
commands: ["exemplo", "outroexemplo"],
response: "Lorem ipsum dolor sit amet. [comando name=exemplo]. Ab quasi quidem est similique [link name=outroexemplo] voluptatem et doloremque omnis a [comando name=outroexemplo] voluptatem magni."
}

Outra forma de pegar os grupos da expressão regular:

// Outra forma de pegar os grupos
while(m.find()) {
find +=1;

// Usando o loop.
for(def i=0; i < m.groupCount(); i++){
commands.add(m.group(i));
}
}

Parece simples, mas demorei um certo tempo para entender como funcionam as coisas. Muitas das coisas foram na tentativa e erro. O comando find() por exemplo, não encontrei como funciona. É apenas um boolean, mas ele também avança para a próxima expressão encontrada. Os exemplos que encontrei usam apenas um if.

O uso do ?<name> para nomear o grupo foi algo que não tinha usado ainda, facilitou para obter o nome dos comandos, pode ser usado nos casos de terem outros grupos.

Fiz esse pequeno exemplo para auxiliar na compressão do Painless, já que a documentação é pobre e temos poucos exemplos. Exitem alguns macetes no desenvolvimento, que espero escrever outra hora. Mas por enquanto é isso.

--

--