Skip to content

marianmeres/condition-parser

Repository files navigation

@marianmeres/condition-parser

NPM version JSR version License: MIT

Human friendly search conditions notation parser. Somewhat similar to Gmail "Search email" input.

The parsed output is designed to match condition-builder dump format, so the two play nicely together.

Installation

deno

deno add jsr:@marianmeres/condition-parser

nodejs

npm i @marianmeres/condition-parser

Usage

import { ConditionParser } from "@marianmeres/condition-parser";

Examples

The core parsable expression:

// for the default "equals" (short "eq") operator
"key:value"
// or with custom operator
"key:operator:value"

is parsed internally as

{ key: "key", operator: "operator", value: "value" }

You can join multiple ones with and or or. The default and can be omitted, so:

"foo:bar baz:bat or hey:ho 'let\'s':go"

is equivalent to

"foo:bar and baz:bat or hey:ho and 'let\'s':go"

You can use parentheses to logically group the expressions. You can use escaped quotes (or colons) inside the identifiers:

`"my key":'my \: operator':"my \" value with quotes" and (foo:<:bar or baz:>:bat)`

Also, you can mix in arbitrary unparsable content — around a contiguous parseable middle — which will be preserved. Both leading and trailing free-text fragments are collected into unparsed (single-space joined):

const result = ConditionParser.parse(
    "this is a:b and (c:d or e:f) free text",
    options: Partial<ConditionParserOptions> // read below
);

// result is now:
{
    parsed: [
        {
            expression: { key: "a", operator: "eq", value: "b" },
            operator: "and"
        },
        {
            condition: [
                { expression: [{ key: "c", operator: "eq", value: "d" }], operator: "or" },
                { expression: [{ key: "e", operator: "eq", value: "f" }], operator: "or" }
            ],
            operator: "and"
        }
    ],
    unparsed: "this is free text",
    meta:   { /* unique keys/operators/values/expressions */ },
    errors: []   // non-empty on syntactic problems; see API.md
}

// ConditionParser.parse options (all optional):
// - defaultOperator: string (default "eq") - operator when not specified
// - debug: boolean (default false) - enable debug logging
// - transform: (ctx) => ctx - transform each parsed expression
// - preAddHook: (ctx) => ctx|null|undefined - return falsy to DROP the expression

For strict validation (e.g. form input), check both:

const ok = errors.length === 0 && unparsed.trim() === "";

See API.md for complete API documentation, including the Breaking Changes section for 1.8.0.

In friends harmony with condition-builder

See condition-builder for more.

import { ConditionParser } from "@marianmeres/condition-parser";
import { Condition } from "@marianmeres/condition-builder";

const userSearchInput = '(folder:"my projects" or folder:inbox) foo bar';

const options = {
	renderKey: (ctx) => `"${ctx.key.replaceAll('"', '""')}"`,
	renderValue: (ctx) => `'${ctx.value.toString().replaceAll("'", "''")}'`,
};

const { parsed, unparsed } = ConditionParser.parse(userSearchInput);

const c = new Condition(options);

c.and("user_id", "eq", 123).and(
	Condition.restore(parsed, options).and("text", "match", unparsed),
);

assertEquals(
	`"user_id"='123' and (("folder"='my projects' or "folder"='inbox') and "text"~*'foo bar')`,
	c.toString(),
);

Related

@marianmeres/condition-builder

About

Human friendly search conditions notation parser. Somewhat similar to Gmail "Search email" input.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors