Current section
Files
Jump to
Current section
Files
README.md
<!-- badges -->
[](https://hex.pm/packages/kql)
[](https://hexdocs.pm/kql)
[](./LICENSE)
# KQL
You're reading the main branch's readme. Please visit
[hexdocs](https://hexdocs.pm/kql) for the latest published documentation.
<!-- MDOC !-->
Parser for a simplified version of the [Kibana query language](https://www.elastic.co/docs/explore-analyze/query-filter/languages/kql) into an AST.
## What is supported?
- Comparison operators: `>`, `>=`, `<`, `<=`, `:`
- Quoted and unquoted values, including escape characters: `make:"foo bar"`, `make:foo\ bar`
- Not, and, & or operators: `NOT make:foo`, `make:foo AND model:bar`, `make:foo OR model:bar`
- Grouping expressions with `()`: `make:foo OR (make:bar AND model:bar)`
- UTF-8 values: `make:苹果`
- Glob values: `make:foo*`
- Value lists: `make: (foo OR bar)`, `make: [foo, bar]`
- Nested fields, dotted or braced: `first.second: foo` and
`first: { second: foo and third: bar }` both produce a `nested` node per path
segment, wrapping the expression at the leaf
- Quoted field names: `"first.second": foo` is a literal field name — quoting
makes the dots characters rather than path separators
## What is missing?
- Matching multiple fields (glob values in field name): `make*:foo`
## Installation
```elixir
def deps do
[
{:kql, "~> 0.2.0"}
]
end
```
## Examples
```elixir
iex> KQL.parse("make:foo")
{:ok, %{
"ast" => %{
"field" => "make",
"operator" => "=",
"type" => "comparison",
"value" => %{
"type" => "value",
"term" => "foo",
"glob" => false,
"quoted" => false
}
},
"meta" => %{
"original_query" => "make:foo",
"version" => "0.2.0"
}
}}
```
Globs are supported too:
```elixir
iex> KQL.parse("make:A* AND model:*X")
{:ok, %{
"ast" => %{
"type" => "and",
"terms" => [%{
"type" => "comparison",
"field" => "make",
"operator" => "=",
"value" => %{
"type" => "value",
"term" => "A*",
"glob" => true,
"quoted" => false
}
},
%{
"type" => "comparison",
"field" => "model",
"operator" => "=",
"value" => %{
"type" => "value",
"term" => "*X",
"glob" => true,
"quoted" => false
}
}]
},
"meta" => %{
"original_query" => "make:A* AND model:*X",
"version" => "0.2.0"
}
}}
```
A dotted field name is a path, producing one `nested` node per dot with the
comparison at the leaf:
```elixir
iex> KQL.parse("car.make:alfa")
{:ok, %{
"ast" => %{
"type" => "nested",
"path" => "car",
"term" => %{
"type" => "comparison",
"field" => "make",
"operator" => "=",
"value" => %{
"type" => "value",
"term" => "alfa",
"glob" => false,
"quoted" => false
}
}
},
"meta" => %{
"original_query" => "car.make:alfa",
"version" => "0.2.0"
}
}}
```
The braced form wraps a whole expression instead of a single comparison, so
`car:{make:alfa}` and `car.make:alfa` produce the same AST:
```elixir
iex> KQL.parse("car:{make:alfa AND year>=2020}")
{:ok, %{
"ast" => %{
"type" => "nested",
"path" => "car",
"term" => %{
"type" => "and",
"terms" => [%{
"type" => "comparison",
"field" => "make",
"operator" => "=",
"value" => %{
"type" => "value",
"term" => "alfa",
"glob" => false,
"quoted" => false
}
},
%{
"type" => "comparison",
"field" => "year",
"operator" => ">=",
"value" => %{
"type" => "value",
"term" => "2020",
"glob" => false,
"quoted" => false
}
}]
}
},
"meta" => %{
"original_query" => "car:{make:alfa AND year>=2020}",
"version" => "0.2.0"
}
}}
```
Quoting a field name makes it literal, so its dots are characters rather than
path separators and the result stays a plain `comparison`:
```elixir
iex> KQL.parse(~S|"car.make":alfa|)
{:ok, %{
"ast" => %{
"type" => "comparison",
"field" => "car.make",
"operator" => "=",
"value" => %{
"type" => "value",
"term" => "alfa",
"glob" => false,
"quoted" => false
}
},
"meta" => %{
"original_query" => "\"car.make\":alfa",
"version" => "0.2.0"
}
}}
```