AQL is a Lucene-inspired query language for Go [currently targeted for arbitrary JSON]. It aims to provide the following features:
- Simple, recognizable syntax
- Expressive composition of boolean expressions
- A generic query front-end for multiple data sources (eventually)
Currently it is used for searching arbitrary JSON data.
import (
"log"
"github.com/flowchartsman/aql/jsonmatcher"
)
const json = `{
"date": "1970-01-02",
"number": 2,
"name": "Andy",
"description": "大懒虫"
}`
func main() {
m, err := NewMatcher(`name:"andy" AND date:><(1970-01-01,1980-01-01)`)
if err != nil {
log.Fatalf("error running query: %v", err)
}
result, err := m.Match([]byte(json))
if err != nil {
log.Fatalf("error during match: %v", err)
}
fmt.Println(result)
}
// output: true
In its simplest form, an AQL query is just a field identifier followed by a search term describing a value to match:
<field>:<value>
For example, given the following JSON:
{
"text":"hello"
}
The following query returns true:
text: "hello"
==true
Nested values are specified by a path, using dot-separated fields:
{
"level1": {
"level2": {
"level3": "here!"
}
}
}
level1.level2.level3:"here!"
== true
If the targetted field is an array, all values will be tested, and the query will return true if it finds one that matches:
{
"outer":{
"inner": [
"hello",
"world",
"from AQL"
]
}
}
outer.inner:"world"
==true
Intervening arrays will be transparently traversed, provided they contain objects or scalar values (deeply nested arrays are a WIP):
{
"outer": [
{
"inner": "hello"
},
{
"inner": [
"world"
]
},
{
"inner": "from AQL"
}
]
}
outer.inner:"world"
==true
Queries can also be combined using AND
and OR
or negated with NOT
/!
:
{
"text":"AQL",
"number":1
}
text:"AQL" AND number:1
== true
text:"Nope" OR number:1
== true
text:"AQL" AND !number:1
== false
Parenthetical grouping is also supported:
(text:"Nope" OR text:"AQL") AND number:1
==true
AQL recognizes several different types of terms:
Type | Examples | Description | Notes |
---|---|---|---|
string | "hello" |
a literal string | supports regular and unicode escaping |
integer | 1 |
an integer number | |
floating point | 1.0 |
a floating point number | |
timestamp | 1970-01-02 1970-01-02T00:00:00Z |
A string representing a moment in time, following the RFC3339 standard format | DateTime or FullDate values are supported |
CIDR | 192.168.0.0/16 |
a network block | |
boolean | true false |
a boolean literal value | |
regex | /^hello to \d{2} people$/ |
a regular expression for advanced string matching | uses Go regex syntax |
Note: Not all terms work with all operators, see the next section for details
AQL can also perform many different types of checks, depending on the type of data.
field:value
This is the basic equality check we've seen so far.
Supported Types | Examples | Notes |
---|---|---|
string | field:"value" |
searches for the exact string value provided |
integer | field:1 |
searches for a numeric value of the exact value provided |
float | field:1.0 |
searches for a numeric value of the exact value provided |
timestamp | field:1970-01-01 field:1970-01-02T15:53:33+00:00 |
searches for a string whith represnts this date. AQL attempts to detect a number of different possible time representations to make this check. For details, see here. Note that this check is currently for the exact timestamp specified, and other operations may be more useful for working with timestamps. |
boolean | field:true field:false |
searches for a JSON boolean of the exact value provided |
regex | field:/attack of the \d+ foot (?:cat|dog)/ |
matches a string where a dog or cat of any height attacks (uses Go regex syntax) |
IP/CIDR | field:192.168.1.0/24 |
matches string values that correspond to network addresses. AQL will attempt to extract an IP address or CIDR block from the text and match the provided address against it. If the provided address is an IP address, AQL will check to see if any values match it, or if it finds CIDR blocks, whether they contain it. Correspondingly, if a CIDR block is provided, AQL will match if an extracted IP address is in that CIDR block. If both values are in CIDR notation, AQL will match if they overlap. |
AQL also supports two special operators, exists
and null
.
|exists|field:exists
|matches if the field exists in the document in any surveyed location. This is actually a unary operation and not a special value, so it cannot be combined with other values in an equality set (not that you'd want to.)
|null|field:null
|matches if the field is explicitly null (or if all resolutions of the field in a nested structure are null). It behaves similarly to exists
, but it is more strict.
field:(value1, value2, ...)
Analagous to a SQL IN query, this is basically a shorthand for field:value1 OR field:value2 OR ...
Supported Types | Examples |
---|---|
string | field:("value1", "value2", "value3") |
integer | field:(1,2) |
float | field:(1.1, 2.2) |
timestamp | field:(1970-01-01, 1970-01-02T15:53:33Z) |
Values in an equality set can be of any type.
field:>value
field:>=value
field:<value
field:<=value
These operations operate on numbers or those values which can be meaningfully compared numerically.
Supported Types | Examples | Notes |
---|---|---|
integer | field:>1 |
searches for a numeric value greater than 1 |
float | field:<2.5 |
searches for a numeric value less than 2.5 |
timestamp | field:<=1970-01-01 |
Attempts to match a timestamp in one of the recognized formats that occurs on or before midnight, UTC, January 1, 1970 |
field:>=1970-01-02T15:53:33−05:00 |
Attempts to match a timestamp in one of the recognized formats that occurs on or after 3:53 PM, EST, January 1, 1970 |
field:><(value1, value2)
This operation attempts to search for a value which falls between the two provided terms. Note: only supports two terms.
Supported Types | Examples | Notes |
---|---|---|
integer | field:><(1, 2) |
searches for a numeric value greater than 1 and less than 2 |
float | field:><(2.1, 2.2) |
searches for a numeric value less than 2.5 |
timestamp | field:><(1970-01-01, 1970-01-02) |
Attempts to match a timestamp in one of the recognized formats that occurs between midnight, January 1, 1970 and midnight, January 2, 1970 |
PRs welcome. Please file issues if your PR addresses a bug.
Please make sure to update tests as appropriate.
- Pluggable string-based user-provided types
- Pluggable user-provided operators
- Flexible query backend with selectable language features (backend doesn't support clause)