QueryEngine turns a raw query string into a structured SearchQuery. It uses a list of PatternItems 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 PatternItems. 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.