cql2-filters-parser
v0.9.14
Published
A browser-oriented implementation of OGC CQL2 filters in TypeScript
Downloads
22
Readme
Open Geospatial Consortium (OGC) Common Query Language (CQL2) filter tooling
A browser-oriented implementation of OGC CQL2 filters in TypeScript. The goal of this library is to enable Javascript based application (Web apps, Node.js, etc) to search STAC catalogs.
OGC CQL2 Filters playground. Sourcemaps are available, feel free to look under the hood.
Installation
Getting cql2-filters-parser from npm:
npm install cql2-filters-parserBrowsers can load the library directly from unpkg.com:
import { parse } from "https://unpkg.com/cql2-filters-parser"cql2-filters-parser library is published as ES module and is dependency free.
Usage
Both CQL2 Text and JSON encodings are supported. Parsing functions return an expression in tree structure. Expressions have toText and toJSON methods, which produce encoding in string or object respectively.
parseText(string)parses CQL2 TextparseJSON(object)parses CQL2 JSONparse(input)wraps parsing functions + simple heuristic
Using library in node module
import { parse, parseJSON, parseText } from "cql2-filters-parser";
const filter = parse("2+3");
console.log(filter.encoding); // Text
console.log(filter.expression.toJSON()); // { op: '+', args: [ 2, 3 ] }
console.log(filter.expression.toText()); // 2 + 3Using from terminal
The npm run parse "my cql" script will parse and print both Text and JSON results
npm run parse "depth BETWEEN 100.0 AND 150.0"Will output:
CQL2 Text:
depth BETWEEN 100 AND 150
CQL2 JSON:
{
"op": "between",
"args": [
{
"property": "depth"
},
100,
150
]
}Links
- Common Query Language (CQL2) Standard page
- The standard itself
- BNF
- JSON Schema
- Examples - Folder with Text and JSON examples.
Implemented classes
- Basic CQL2
- Advanced Comparison Operators
- Case-insensitive Comparison
- Accent-insensitive Comparison
- Basic Spatial Functions
- Basic Spatial Functions with additional Spatial Literals
- Spatial Functions
- Temporal Functions
- Array Functions
- Arithmetic Expressions
High level design
---
Note: Mermaid diagrams don't work in npm. Please head over to GitHub.
title: CQL2 filter parser high level design
---
flowchart TD
JS[JavaScript] --> IN[Input]
SI["Standard input (stdin)"] --> IN[Input]
FI[File input] --> IN[Input]
IN --> |" parse(input) "| D{Detect encoding}
%% CQL2 Text
D --> | CQL2 Text | TS[Text scanner]
TS --> | Tokens | TP[Text parser]
%% CQL2 JSON
D --> | CQL2 JSON | JSONP["JSON.parse()"]
JSONP --> | JS object | JP[JSON parser]
JP --> E
%% Results
TP --> E["Expression tree (AST)"]
E --> TT[".toText()"]
E --> VIS[".accept(visitor)"]
E --> TJ[".toJSON()"]
%% Visitor
VIS --> | extend | Val[Validation]
VIS --> | extend | UI[UI]
VIS --> | extend | Eval[Evaluation]Visitor
Expression tree also has accept(visitor, context) method that receive a visitor object as input. The visitor object should implement all visit functions. This enable producing output when traversing the expression tree. The optional context parameter enabled passing additional information for the visitor, such as current path to the expression.
import { parse } from "cql2-filters-parser";
const minimalArithmeticVisitor = {
visitBinaryExpression: (expr) => {
return [
"Left side is " + expr.left.accept(minimalArithmeticVisitor),
"Operator is " + expr.operator.accept(minimalArithmeticVisitor),
"Right side is " + expr.right.accept(minimalArithmeticVisitor),
].join(". ");
},
visitLiteralExpression: (expr) => `${expr.type} ${expr.toText()}`,
visitOperatorExpression: (expr) => expr.label,
};
const expr = parseText("2 + 3");
console.log(filter.expression.accept(minimalArithmeticVisitor)); // Left side is number 2. Operator is addition. Right side is number 3More Visitor examples can be found in Expression.test.ts suite, or check out ReactVisitor for an advanced implementation.
Dependencies
The parsers runtime are dependency free. I'd like to publish the parsers as a separate module, but ATM that's not the case. In development there are multiple dependencies, but trying to keep them to minimum.
Playground is built with React.
Development
PRs welcome.
Folder structure
src- library root.parse.ts- main parse function.main.ts- for CLI.entities- Expression classes, Tokens, operator's metadata.parser- Text and JSON parsers.types- folder with types that don't belong directly to either parser or entities folders.
examples- ATM only one: filter builder react app.ci- Continues integration related files.
First time only
- Clone the repository
- Install dependencies
npm install
Parsers development
Test driven development is advised, using npm run test. Vitest is the test runner.
Playground development
The Playground is powered by Vite and React, and can be found in examples folder. Run npm run dev to start dev server. For real experience use npm run build && npm run preview.
Checking everything
In no particular order:
- Make sure you created a new branch, and commit changes
- Run all checks (lint, type check, tests) -
npm run checks. - ESlint + Prettier -
npm run lint. - TypeScript type check -
npm run type-check. - Tests -
npm run testsfor continues running test suite on fule change, or run just once usingnpm run tests:once. - Code coverage -
npm run test:coverage- coverage is not enforced, but for parsers I try to have close to 100%. Exceptions are edge cases, which should throw. - Increment version - Each PR must increment version properly using
npm version <major | minor | patch>. The CI test (npm run test:ci) enforces that.
When done, push branch and create a PR. Github Actions will run checks.
Publishing
Done manually. npm run publish:dry will run checks, create fresh build and display which files will be bundled. If everything's OK, npm publish will publish. Note that version must be incremented.
