File: //usr/share/opensearch-dashboards/node_modules/lucene/lib/lucene.grammar
/*
* Lucene Query Grammar for PEG.js
* ========================================
*
* This grammar supports many of the constructs contained in the Lucene Query Syntax.
*
* Supported features:
* - conjunction operators (AND, OR, ||, &&, NOT, AND NOT, OR NOT)
* - prefix operators (+, -)
* - quoted values ("foo bar")
* - named fields (foo:bar)
* - range expressions (foo:[bar TO baz], foo:{bar TO baz})
* - regex expressions (/^f?o[1-5]o/)
* - proximity search expressions ("foo bar"~5)
* - boost expressions (foo^5, "foo bar"^5)
* - fuzzy search expressions (foo~, foo~0.5)
* - parentheses grouping ( (foo OR bar) AND baz )
* - field groups ( foo:(bar OR baz) )
*
* The grammar will create a parser which returns an AST for the query in the form of a tree
* of nodes, which are dictionaries. There are three basic types of expression dictionaries:
*
* A node expression generally has the following structure:
*
* {
* 'left' : dictionary, // field expression or node
* 'operator': string, // operator value
* 'right': dictionary, // field expression OR node
* 'field': string // field name (for field group syntax) [OPTIONAL]
* 'parenthesized': boolean // whether or not the expression was placed in parentheses
* // in the input
* }
*
*
* A field expression has the following structure:
*
* {
* 'field': string, // field name
* 'term': string, // term value
* 'quoted': boolean, // whether or not the value was quoted in the input string
* 'regex': boolean, // whether or not the value is a regex expression
* 'prefix': string // prefix operator (+/-) [OPTIONAL]
* 'boost': float // boost value, (value > 1 must be integer) [OPTIONAL]
* 'similarity': float // similarity value, (value must be > 0 and < 1) [OPTIONAL]
* 'proximity': integer // proximity value [OPTIONAL]
* }
*
*
* A range expression has the following structure:
*
* {
* 'field': string, // field name
* 'term_min': string, // minimum value (left side) of range
* 'term_max': string, // maximum value (right side) of range
* 'inclusive': string // inclusive ([...]) or exclusive ({...}) or mixed ({...],[...})
* }
*
* Other Notes:
*
* - For any field name, unnamed/default fields will have the value "<implicit>".
* - Wildcards (fo*, f?o) and fuzzy search modifiers (foo~.8) will be part of the term value.
* - Escaping is not supported and generally speaking, will break the parser.
* - Conjunction operators that appear at the beginning of the query violate the logic of the
* syntax, and are currently "mostly" ignored. The last element will be returned.
*
* For example:
* Query: OR
* Return: { "operator": "OR" }
*
* Query: OR AND
* Return: { "operator": "AND" }
*
* Query: OR AND foo
* Return: { "left": { "field": "<implicit>", "term": "foo" } }
*
* To test the grammar, use the online parser generator at http://pegjs.majda.cz/online
*
*/
start
= _* node:node+
{
return node[0];
}
/ _*
{
return {};
}
/ EOF
{
return {};
}
node
= operator:operator_exp EOF
{
return {
'operator': operator,
};
}
/ start:operator_exp left:group_exp operator:operator_exp* right:node*
{
var node = {
'start': start,
'left': left,
};
var right =
right.length == 0
? null
: right[0]['right'] == null
? right[0]['left']
: right[0];
if (right != null) {
node['operator'] = operator == '' ? '<implicit>' : operator[0];
node['right'] = right;
}
return node;
}
/ operator:operator_exp right:node
{
return right;
}
/ left:group_exp operator:operator_exp* right:node*
{
var node = {
'left':left
};
var right =
right.length == 0
? null
: right[0]['right'] == null
? right[0]['left']
: right[0];
if (right != null) {
node['operator'] = operator == '' ? '<implicit>' : operator[0];
node['right'] = right;
}
return node;
}
group_exp
= field_exp:field_exp _*
{
return field_exp;
}
/ paren_exp
paren_exp
= "(" _* node:node+ ")" _*
{
node[0]['parenthesized'] = true;
return node[0];
}
field_exp
= fieldname:fieldname? range:range_operator_exp
{
range['field'] =
fieldname == null || fieldname.label == ''
? "<implicit>"
: fieldname.label;
range['fieldLocation'] =
fieldname == null || fieldname.label == ''
? null
: fieldname.location;
return range;
}
/ fieldname:fieldname node:paren_exp
{
node['field']= fieldname.label;
node['fieldLocation'] = fieldname.location;
return node;
}
/ fieldname:fieldname? term:term
{
var fieldexp = {
'field':
fieldname == null || fieldname.label == ''
? "<implicit>"
: fieldname.label,
'fieldLocation':
fieldname == null || fieldname.label == ''
? null
: fieldname.location,
};
for(var key in term)
fieldexp[key] = term[key];
return fieldexp;
}
fieldname
= fieldname:unquoted_term [:] _*
{
return {
label: fieldname.label,
location: fieldname.location
}
}
term
= op:prefix_operator_exp? term:quoted_term proximity:proximity_modifier? boost:boost_modifier? _*
{
var result = {
'term': term,
'quoted': true,
'regex' : false,
'termLocation': location()
};
if('' != proximity)
{
result['proximity'] = proximity;
}
if('' != boost)
{
result['boost'] = boost;
}
if('' != op)
{
result['prefix'] = op;
}
return result;
}
/ op:prefix_operator_exp? term:regex_term _*
{
var result = {
'term': term,
'quoted': false,
'regex': true,
'termLocation': location()
};
return result;
}
/ op:prefix_operator_exp? term:unquoted_term similarity:fuzzy_modifier? boost:boost_modifier? _*
{
var result = {
'term': term.label,
'quoted': false,
'regex': false,
'termLocation': location()
};
if('' != similarity)
{
result['similarity'] = similarity;
}
if('' != boost)
{
result['boost'] = boost;
}
if('' != op)
{
result['prefix'] = op;
}
return result;
}
rterm_char
= "\\" sequence:EscapeSequence { return '\\' + sequence; }
/ '.' / [^ \t\r\n\f\{\}()"/^~\[\]]
ranged_term
= term:rterm_char+
{
return term.join('');
}
unquoted_term
= term:term_char+
{
return {
label: term.join(''),
location: location(),
};
}
term_char
= "\\" sequence:EscapeSequence { return '\\' + sequence; }
/ '.' / [^: \t\r\n\f\{\}()"/^~\[\]]
quoted_term
= '"' chars:DoubleStringCharacter* '"' { return chars.join(''); }
regex_term
= '/' chars:RegexCharacter+ '/' { return chars.join('') }
DoubleStringCharacter
= !('"' / "\\") char:. { return char; }
/ "\\" sequence:EscapeSequence { return '\\' + sequence; }
RegexCharacter
= !('/' / "\\") char:. { return char; }
/ "\\" sequence:EscapeSequence { return '\\' + sequence; }
EscapeSequence
= "+"
/ "-"
/ "!"
/ "("
/ ")"
/ "{"
/ "}"
/ "["
/ "]"
/ "^"
/ "\""
/ "?"
/ ":"
/ "\\"
/ "&"
/ "|"
/ "'"
/ "/"
/ "~"
/ "*"
/ " "
proximity_modifier
= '~' proximity:int_exp
{
return proximity;
}
boost_modifier
= '^' boost:decimal_or_int_exp
{
return boost;
}
fuzzy_modifier
= '~' fuzziness:decimal_exp?
{
return fuzziness == '' || fuzziness == null ? 0.5 : fuzziness;
}
decimal_or_int_exp
= decimal_exp
/ int_exp
decimal_exp
= '0.' val:[0-9]+
{
return parseFloat("0." + val.join(''));
}
int_exp
= val:[0-9]+
{
return parseInt(val.join(''));
}
range_operator_exp
= '[' term_min:ranged_term _* 'TO' _+ term_max:ranged_term ']'
{
return {
'term_min': term_min,
'term_max': term_max,
'inclusive': 'both'
};
}
/ '{' term_min:ranged_term _* 'TO' _+ term_max:ranged_term '}'
{
return {
'term_min': term_min,
'term_max': term_max,
'inclusive': 'none'
};
}
/ '[' term_min:ranged_term _* 'TO' _+ term_max:ranged_term '}'
{
return {
'term_min': term_min,
'term_max': term_max,
'inclusive': 'left'
};
}
/ '{' term_min:ranged_term _* 'TO' _+ term_max:ranged_term ']'
{
return {
'term_min': term_min,
'term_max': term_max,
'inclusive': 'right'
};
}
operator_exp
= _* operator:operator _+
{
return operator;
}
/ _* operator:operator EOF
{
return operator;
}
operator
= 'OR NOT'
/ 'AND NOT'
/ 'OR'
/ 'AND'
/ 'NOT'
/ '||'
/ '&&'
prefix_operator_exp
= _* operator:prefix_operator
{
return operator;
}
prefix_operator
= '+'
/ '-'
/ '!'
_ "whitespace"
= [ \t\r\n\f]+
EOF
= !.