Filter Parser

Filter Elasticsearch results with human-readable expressions — ranges, wildcards, AND/OR/NOT, nested fields, geo proximity. Full syntax reference.

On this page

The Filter Parser turns human-readable expressions into Elasticsearch boolean queries. You write active:true AND price:100..500; Sigmie compiles it into the right combination of term, range, and bool queries.

use Sigmie\Parse\FilterParser;
use Sigmie\Mappings\NewProperties;
 
$props = new NewProperties;
$props->keyword('category');
$props->bool('active');
$props->number('stock');
 
$parser = new FilterParser($props());
$query = $parser->parse('active:true AND category:"sports"');

In newSearch() and newQuery(), you almost never instantiate the parser directly — you pass a filter string to filters() or parse():

$sigmie->newSearch('products')
->properties($props)
->filters('active:true AND stock>0 AND price:100..500')
->queryString('laptop')
->get();

Why use the parser

  • Reads like a sentenceactive:true AND price:100..500 instead of nested bool arrays.
  • Type-aware — validated against your property definitions.
  • Errors early — invalid syntax raises a ParseException before hitting Elasticsearch.

Exact match

category:"sports"
color:'red'
name:'John Doe'
name:"John Doe"

Single and double quotes are interchangeable.

Numbers

Numbers don’t need quotes:

price:100
stock:50

Booleans

active:true
published:false

Boolean values are lowercase, no quotes.

Field exists

email:* # has any value
NOT email:* # has no value

Multiple values (IN)

Match any value from an array:

category:["sports", "action", "horror"]
status:["active", "pending"]

Whitespace inside an array is trimmed. Empty arrays match nothing.

Wildcards

phone:'*650' # ends with 650
phone:'2353*' # starts with 2353
title:'*manager*' # contains "manager"

Ranges

Comparison operators

price>=100
price<=200
stock>0
created_at>="2023-05-01"

Operators: >, <, >=, <=.

Inclusive range

price:100..500
created_at:"2023-01-01".."2023-12-31"

price:100..500 is equivalent to price>=100 AND price<=500.

Logical operators

active:true AND category:"sports" AND stock>0
category:"action" OR category:"horror"
NOT category:"sports"
active:true AND NOT stock:0

Group with parentheses:

active:true AND (category:"action" OR category:"horror") AND stock>0

Note: Multiple clauses without an operator throw ParseException:

color:'red' size:'large' # error
color:'red' AND size:'large' # OK

Object properties

For flattened object fields, use dot notation:

contact.active:true
contact.name:"John Doe"
user.profile.settings.notifications:true
$props->object('contact', function (NewProperties $p) {
$p->bool('active');
$p->name('name');
$p->email('email');
});
 
$parser->parse('contact.active:true AND contact.name:"Alice"');

Nested fields

For arrays of objects (mapped as nested()), use curly braces. All conditions inside the braces must match the same array element:

contact:{ active:true }
contact:{ name:"John Doe" AND verified:true }
$props->nested('vehicles', function (NewProperties $p) {
$p->keyword('make');
$p->keyword('model');
});
 
$parser->parse("vehicles:{ make:'Powell Motors' AND model:'Canyonero' }");

This finds documents with a vehicle whose make is “Powell Motors” and model is “Canyonero” — not documents with one vehicle named “Powell Motors” and a separate vehicle modeled “Canyonero”.

Deep nesting

contact:{ address:{ city:"Berlin" AND marker:"X" } }

Object vs nested syntax

Same data, different mappings — different syntax:

Mapping Syntax
object() contact.active:true AND contact.city:"Berlin"
nested() contact:{ active:true AND city:"Berlin" }

nested preserves the relationship; object flattens everything to root.

Geo-location

Filter by proximity to a point:

location:1km[51.49,13.77]
location:5mi[40.7128,-74.0060]
contact:{ location:1km[51.16,13.49] AND active:true }

The format is field:distance[lat,lon]. Supported units: km, mi, m, yd, ft, nmi, cm, in.

Note: Zero distance returns nothing, even on an exact coordinate match. Use a small positive distance:

location:0km[51.16,13.49] # returns nothing
location:1m[51.16,13.49] # OK

Empty values

database:""
tags:[]

Empty arrays match no documents.

Escaping and special characters

Quotes inside strings

description:"She said \"Hello World\""
title:'It\'s working'

Dashes, spaces, parentheses

Values with spaces, dashes, or special characters need quotes:

status:'in-progress'
category:"crime & drama"
job_title:"Chief Executive Officer (CEO)"
industry:["Renewables & Environment"]

Error handling

Invalid syntax raises ParseException:

use Sigmie\Parse\ParseException;
 
try {
$query = $parser->parse('color:"red" color:"blue"'); // missing operator
} catch (ParseException $e) {
// log, surface to the user, etc.
}

Common causes:

  • Missing logical operator between clauses.
  • Referencing a field that isn’t in your mappings.
  • Mismatched parentheses or brackets.
  • Excessive nesting (the parser has a depth limit).

Field validation

The parser validates field names against your property definitions:

$props = new NewProperties;
$props->keyword('category');
 
$parser = new FilterParser($props());
 
$parser->parse('category:"sports"'); // OK
$parser->parse('subject_service:{ id:"23" }'); // error — field unknown
 
if (!empty($parser->errors())) {
// handle errors
}

Syntax cheatsheet

Operation Syntax Example
Exact match field:"value" category:"sports"
Number field:123 price:100
Boolean field:true active:true
Field exists field:* email:*
IN field:[v1,v2] status:["active","pending"]
Wildcard field:'*pat*' phone:'*650'
Range field:min..max price:100..500
Greater than field>value stock>0
Less than field<value price<100
AND a AND b active:true AND stock>0
OR a OR b cat:"a" OR cat:"b"
NOT NOT a NOT category:"books"
Object obj.field:value contact.active:true
Nested field:{condition} contact:{active:true}
Geo field:dist[lat,lon] location:1km[51,13]