Dec 23rd, 2020: [ES] Nuevas incorporaciones a la familia de tipos keyword: constant_keyword y wildcard

Versión en inglés

En este post hablaremos de las recientes incorporaciones a la familia de tipos keyword: wildcard y constant_keyword.

Los campos de tipo wildcard estan optimizados para búsquedas con caracteres comodín (*o ?) o expresiones regulares. Casos habituales son los de seguridad, en que podríamos estar buscando un patrón en un proceso, o para ejecutar consultas tipo grep en líneas de logs que no se han modelado.

Este tipo se introdujo en la versión 7.9. y vamos a mostrar su funcionamiento con un ejemplo básico. Utilizaremos los datos de muestra de Kibana, “Sample web logs” en la versión 7.10.0. de Kibana.

Imaginemos que queremos obtener los distintos ficheros zip que los usuarios han desacargado de nuestros sitios web. Con los datos que obtenemos de muestra, podríamos ejecutar la siguiente consulta:

GET kibana_sample_data_logs/_search?filter_path=aggregations.zip-downloads.buckets.key
{
  "size": 0, 
  "_source": "request", 
  "query": {
    "wildcard": {
      "url.keyword": {
        "value": "*downloads*.ZIP",
        "case_insensitive": true
      }
    }
  },
  "aggs": {
    "zip-downloads": {
      "terms": {
        "field": "url.keyword",
        "size": 10
      }
    }
  }
}

Que utiliza el campo url.keyword, que ya existe en el índice kibana_sample_data_logs, para ejecutar esa consulta. Y nos devolverá:

{
  "aggregations" : {
    "zip-downloads" : {
      "buckets" : [
        {
          "key" : "https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.zip"
        },
        {
          "key" : "https://artifacts.elastic.co/downloads/apm-server/apm-server-6.3.2-windows-x86.zip"
        },
        {
          "key" : "https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-windows-x86_64.zip"
        }
      ]
    }
  }
}

A partir del índice kibana_sample_data_logs, podemos simplemente actulizar sus mapeos para añadir a los campos que ya existen, url (de tipo text) y url.keyword (de tipo keyword), un tercer campo url.wirldcard de tipo wildcard.

PUT kibana_sample_data_logs/_mappings
{
  "properties" : {
        "url" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            },
            "wildcard" : {
              "type" : "wildcard"
            }
          }
        }
      }
}

Para completar este nuevo campo, podemos ejecutar la siguiente actualización::

POST kibana_sample_data_logs/_update_by_query

Y ahora ya podremos ejecutar la misma consulta, usando el campo url.wildcard.

GET kibana_sample_data_logs/_search?filter_path=aggregations.zip-downloads.buckets.key
{
  "size": 0, 
  "_source": "request", 
  "query": {
    "wildcard": {
      "url.wildcard": {
        "value": "*downloads*.ZIP",
        "case_insensitive": true
      }
    }
  },
  "aggs": {
    "zip-downloads": {
      "terms": {
        "field": "url.keyword",
        "size": 10
      }
    }
  }
}

Lo que hemos hecho es reemplazar una consulta de tipo wildcard sobre un campo keyword por una consulta wildcard menos costosa sobre un campo de tipo wildcard.

¿Y cómo nos ayuda esto?

  • Si tenemos un campo con alta cardinalidad, las consultas de tipo wildcard con un * inicial son costosas sobre campos keyword, ya que no estan optimizados para ellas y es necesario escanear todos los registros para encontrar cuales coinciden. Los campos de tipo wildcard indexan el valor del campo usando ngrams, a la vez que almacenan la cadena completa. Combinando las dos estructuras de datos en la búsqueda aceleramos la búsqueda.
  • Las cadenas tienen un límite de tamaño. Lucene tiene un límite estricto en 32k en terms, y Elasticsearch impone por defecto incluso un límite menor. Eso implica que el contenido de ese campo no será indexado si llegamos a ese tamaño. Un stack trace puede ser largo, y, en casos como los de seguridad, esto puede crearnos puntos ciegos que no son aceptables.

Lo que hemos conseguido es que la misma búsqueda que teníamos usando el campo de tipo keyword nos funcione con el nuevo campo de tipo wildcard field. En este ejemplo simple la velocidad sería similar, ya que no tenemos ni muchos datos ni alta cardinalidad. Aunque nos permite ver cómo funciona.

¿Cuándo deberíamos usar el tipo wildcard? ¿Cuál es el compromiso? En este post no entraremos en más detalles. Para profundizar más, estos son excelentes recursos:

Paramos ahora a la segunda incorporación a la familia de tipos keyword, el campo de tipo constant_keyword, que está disponible desde la versión 7.7.0..

Este es un campo que podemos usar en los casos en los que necesitemos acelerar las búsquedas de filtros.

Por lo general, cuantos más documentos coincidan con un filtro, más cara será esa consulta. Por ejemplo, si enviamos todos los logs aplicativos recopilados por diferentes equipos de desarrollo al mismo índice, es posible que luego los filtremos siempre en función del equipo. Si sabemos que este es un filtro habitual para nuestro caso de uso, podríamos decidir ingerir datos en diferentes índices, en función del valor del campo equipo en cada log. Eso nos daría consultas más rápidas, ya que solo llegarían a índices con datos coincidentes para ese equipo.

Y podemos ir un paso más allá. Quizás no queremos cambiar la lógica de nuestros clientes, la forma en que consultan estos datos. Nos gustaría que funcionen las mismas consultas, pero aún así filtrar de forma más eficiente los índices que tienen datos que no coinciden.

Si creamos esos índices con un campo constant_keyword, y establecemos el valor constante en los mapeos, las queries que se envien a todos los índices harán uso de este vampo para descartar los índices que no tienen datos coincidentes.

Veremos como funciona de nuevo con un ejemplo básico.

Crearemos dos índices para almacenar registros de dos equipos diferentes, A y B. En cada índice, definimos el campo team con un valor constante, A o B.

PUT my-logs-team-a
{
  "mappings" : {
      "properties" : {
        "@timestamp" : {
          "type" : "date"
        },
        "message" : {
          "type" : "text"
        },
        "team" : {
          "type" : "constant_keyword",
          "value": "A"
        }
      }
    }
}
PUT my-logs-team-b
{
  "mappings" : {
      "properties" : {
        "@timestamp" : {
          "type" : "date"
        },
        "message" : {
          "type" : "text"
        },
        "team" : {
          "type" : "constant_keyword",
          "value": "B"
        }
      }
    }
}

Y ahora indexaremos un documento en cada índice. Podemos incluir el vaor del campo team en el documento a ingestar:

POST my-logs-team-a/_doc/
{
  "@timestamp": "2020-12-04T10:00:13.637Z",
  "message": "239.23.215.100 - - [2018-08-10T13:09:44.504Z] \"GET /apm HTTP/1.1\" 200 8679 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\"",
  "team": "A"
}

O no incluir el valor de ese campo, y tomará el definido en el mapping:

POST my-logs-team-b/_doc/
{
  "@timestamp": "2020-12-04T10:01:12.654Z",
  "message": "34.52.49.238 - - [2018-08-10T12:23:20.235Z] \"GET /apm HTTP/1.1\" 200 117 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\""
}

Ahora podremos ejecutar las consultas de una manera más eficiente sin seleccionar los índices que contienen los valores que queremos filtrar. Podemos buscar en my-logs-team- *, y dejar que Elasticsearch haga su magia:

GET my-logs-team-*/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "team": "B"
          }
        }
      ]
    }
  }
}

Si ejecutamos la consulta en el "Search Profiler" de Kibana, podemos ver que cuando buscamos el equipo B, estamos ejecutando una consulta de tipo match_none en el índice team-a. Acelerando así la operación de filtrado.

Si creáramos los índices usando un tipo keyword y ejecutarámos la misma prueba, veríamos cómo ambos índices ejecutan la misma consulta de tipo term, incluso cuando uno de ellos no dará resultados.

¡Prueba los dos nuevos tipos de "keyword" y cuéntanos qué tal!

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.