Skip to content

Instantly share code, notes, and snippets.

@reidev275
Last active December 9, 2019 19:07
Show Gist options
  • Save reidev275/e1e3519e67ff29664712da9cbe96b459 to your computer and use it in GitHub Desktop.
Save reidev275/e1e3519e67ff29664712da9cbe96b459 to your computer and use it in GitHub Desktop.
A dsl for filtering resources. Useful as a way to serialize and transfer filters
import { Filter } from "./filterDsl"
// a sample data model
interface JobPosting {
manager: string;
position: string;
}
//a filter of the data model
const query: Filter<JobPosting> = and(
equals("position", "Manager"),
oneOf("manager", "LIMOGES", "COLPACK")
);
//underlying data representation
console.log(query)
//{ kind: 'And',
// a: { kind: 'Equals', field: 'position', val: 'Manager' },
// b:
// { kind: 'Or',
// a: { kind: 'Equals', field: 'manager', val: 'LIMOGES' },
// b: { kind: 'Equals', field: 'manager', val: 'COLPACK' } } }
//sql created by interpreting the data model
console.log(interpret(query))
//([position] = 'Manager' and ([manager] = 'LIMOGES' or [manager] = 'COLPACK'))
export type Filter<A> =
| { kind: "Equals"; field: keyof A; val: any }
| { kind: "Greater"; field: keyof A; val: any }
| { kind: "Less"; field: keyof A; val: any }
| { kind: "And"; a: Filter<A>; b: Filter<A> }
| { kind: "Or"; a: Filter<A>; b: Filter<A> };
//core
export const equals = <A>(field: keyof A, val: any): Filter<A> => ({
kind: "Equals",
field,
val
});
export const greater = <A>(field: keyof A, val: any): Filter<A> => ({
kind: "Greater",
field,
val
});
export const less = <A>(field: keyof A, val: any): Filter<A> => ({
kind: "Less",
field,
val
});
export const and = <A>(a: Filter<A>, b: Filter<A>): Filter<A> => ({
kind: "And",
a,
b
});
export const or = <A>(a: Filter<A>, b: Filter<A>): Filter<A> => ({
kind: "Or",
a,
b
});
/*
* Derived operations
* These combine core language features to extend the language
*/
//greater than or equal
export const gte = <A>(field: keyof A, val: any): Filter<A> =>
or(greater(field, val), equals(field, val));
//less than or equal
export const lte = <A>(field: keyof A, val: any): Filter<A> =>
or(less(field, val), equals(field, val));
//combine 1 to many filters returning true if all are true (and)
export const all = <A>(...dsl: Filter<A>[]): Filter<A> =>
dsl.reduce((p, c) => and(p, c));
//combine 1 to many filters returning true if any are true (or)
export const any = <A>(...dsl: Filter<A>[]): Filter<A> =>
dsl.reduce((p, c) => or(p, c));
//essentially sql's in operator. Given a field and a collection of values
//this returns true if any are true.
export const oneOf = <A>(field: keyof A, ...vals: any[]): Filter<A> =>
any(...vals.map(x => equals(field, x)));
interpret { Filter } from "./filterDsl"
const interpret = <A>(dsl: Filter<A>): string => {
switch (dsl.kind) {
case "Equals":
return `[${dsl.field}] = '${dsl.val}'`;
case "Greater":
return `[${dsl.field}] > '${dsl.val}'`;
case "Less":
return `[${dsl.field}] < '${dsl.val}'`;
case "And":
return `(${interpret(dsl.a)} and ${interpret(dsl.b)})`;
case "Or":
return `(${interpret(dsl.a)} or ${interpret(dsl.b)})`;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment