QueryEngine
turns a raw query string into a structured SearchQuery
. It uses a list of PatternItem
s to recognise special characters, handle quoting, define required/forbidden words, tag searches and wildcards. The default engine supports a powerful but compact query syntax similar to many search engines.
Each PatternItem
defines a charCode
(the character it matches) and a set of flags that instruct the parser how to handle that character:
"
(double‑quote) is a collector.#
defines tags.-
defines negation.\
escapes the next special token or character.*
to .
in regex). In the default engine, *
is replaced by a special sentinel and later converted to .*
.The static getter QueryEngine.defaultEngine
returns an engine with the following patterns:
Pattern | Name | Role |
---|---|---|
" (0x0022) |
hold | Collector; marks the start and end of a quoted term. Words inside are treated as one unit and required. |
\\ (0x005C) |
ignore | Escape; ignores the special meaning of the next pattern or character. |
Space (0x0020) | space | Splitter; separates tokens. |
- (0x002D) |
cannotHave | DefinesNotAllowed; the following term is forbidden. |
# (0x0023) |
tag | DefinesTag; the following term matches a tag name. |
* (0x002A) |
wild | Wildcard; replaced with a sentinel and later converted to .* in the regular expression. |
The engine maps patterns by their charCode
, so order does not matter. The parser loops through each character of the query string and consults the engine to decide how to handle it.
Call buildQuery(String queryString)
to convert a string into a SearchQuery
. The engine creates a new QueryBuilder
, iterates over the code units of queryString
and for each character does the following:
PatternItem
with matching charCode
. If none exists, treat the character as a literal.isCollector
, toggle quote mode. While in quote mode, only escape patterns have any effect; all other characters (including whitespace and other special tokens) are appended to the current term.ignoreNextPattern
or ignoreNextChar
, skip processing of the next character or pattern (escape semantics).isSplitter
, flush the current term into the appropriate bucket (optional/required/forbidden/tag) based on the parser’s state flags.definesTag
, mark the next term as belonging to SearchQuery.tags
. The term before the tag (if any) remains unaffected.definesRequired
or definesNotAllowed
, set flags so that the next term will be placed in the require
or cannot
bucket instead of optional
.charReplacement
) to the current term.Once the loop finishes, flush any remaining term. The builder then deduplicates repeated words, assigns point values (more points for repeated occurrences) and constructs the SearchQuery
.
You can create your own QueryEngine
by supplying a different list of PatternItem
s. For example, you might add a new prefix to search by category or change the escape character. When building your own engine, ensure that each pattern has a unique charCode
and that collector patterns have balanced isCollector
flags. Passing your custom engine to Search.from(queryString, engine: myEngine)
will parse queries according to your rules.