mirror of
https://github.com/papra-hq/papra.git
synced 2026-01-24 10:45:55 -06:00
@papra/search-parser
A search query parser library for building GitHub-style search syntax with filters, logical operators, and full-text search. You can play with the parser in the demo application.
Features
- TypeScript-first: Fully typed API and AST structures.
- Dependency-free: No external dependencies, lightweight and fast.
- Error-resilient: Best-effort parsing with detailed issue reporting.
- Rich syntax support: Logical operators (AND, OR, NOT), grouping with parentheses, and field-based filters.
- Configurable limits: Control maximum depth and token count to prevent abuse.
- Optimization: Simplifies the parsed expression tree by removing redundancies, and basic boolean algebra simplifications.
Installation
pnpm add @papra/search-parser
# or
npm install @papra/search-parser
# or
yarn add @papra/search-parser
Usage
import { parseSearchQuery } from '@papra/search-parser';
// Simple text search
parseSearchQuery({ query: 'foobar' });
// { expression: { type: 'text', value: 'foobar' }, issues: [] }
// Filter query
parseSearchQuery({ query: 'tag:invoice' });
// { expression: { type: 'filter', field: 'tag', operator: '=', value: 'invoice' }, issues: [] }
// Complex query with operators
parseSearchQuery({ query: '(tag:invoice OR tag:receipt) AND createdAt:>2024-01-01' });
// {
// expression: {
// type: 'and',
// operands: [
// {
// type: 'or',
// operands: [
// { type: 'filter', field: 'tag', operator: '=', value: 'invoice' },
// { type: 'filter', field: 'tag', operator: '=', value: 'receipt' },
// ],
// },
// { type: 'filter', field: 'createdAt', operator: '>', value: '2024-01-01' },
// ],
// },
// issues: [],
// }
Query Syntax
Text Search
my invoice
"quoted text"
Filters
tag:invoice # Equality (same as tag:=invoice)
createdAt:>2024-01-01 # Comparison operators: >, <, >=, <=, =
Logical Operators
tag:invoice AND status:active
tag:invoice OR tag:receipt
NOT tag:personal
-tag:personal # Negation shorthand
Grouping
(tag:invoice OR tag:receipt) AND status:active
Optimization
You can enable optimization to simplify the parsed expression tree:
// The query has redundant ANDs and double negations
const query = 'tag:invoice AND (tag:receipt AND (tag:invoice AND NOT (NOT foo)))';
parseSearchQuery({ query, optimize: false });
// {
// expression: {
// type: 'and',
// operands: [
// { type: 'filter', field: 'tag', operator: '=', value: 'invoice' },
// {
// type: 'and',
// operands: [
// { type: 'filter', field: 'tag', operator: '=', value: 'receipt' },
// {
// type: 'and',
// operands: [
// { type: 'filter', field: 'tag', operator: '=', value: 'invoice' },
// {
// type: 'not',
// operand: {
// type: 'not',
// operand: { type: 'text', value: 'foo' },
// },
// },
// ],
// },
// ],
// },
// ],
// },
// issues: [],
// }
parseSearchQuery({ query, optimize: true });
// {
// expression: {
// type: 'and',
// operands: [
// { type: 'filter', field: 'tag', operator: '=', value: 'invoice' },
// { type: 'filter', field: 'tag', operator: '=', value: 'receipt' },
// { type: 'text', value: 'foo' },
// ],
// },
// issues: [],
// }
API
function parseSearchQuery(options: {
query: string;
maxDepth?: number; // Default: 10
maxTokens?: number; // Default: 200
optimize?: boolean; // Default: true
}): ParsedQuery;
type ParsedQuery = {
expression: Expression;
issues: Issue[];
};
Error Handling
The parser returns issues for malformed queries while doing best-effort parsing:
import { ERROR_CODES } from '@papra/search-parser';
const result = parseSearchQuery({ query: '(tag:invoice' });
// {
// expression: { type: 'filter', ... },
// issues: [{ code: 'unmatched-opening-parenthesis', message: '...' }]
// }
License
This project is licensed under the MIT License. See the LICENSE file for details.
Credits
This project is crafted with ❤️ by Corentin Thomasset. If you find this project helpful, please consider supporting my work.