Module: Backlex::Filter
- Defined in:
- lib/backlex/filter.rb
Overview
Static condition constructors — a Ruby port of the leaf/logical helpers in query.ts. Compose them and pass to QueryBuilder#where. Everything compiles to the canonical JSON Condition the REST API speaks.
rows = client.from("orders").query
.where(Backlex::Filter.and_(
Backlex::Filter.eq("status", "active"),
Backlex::Filter.gte("total", 100),
Backlex::Filter.rel("customer", Backlex::Filter.eq("tier", "gold")), # -> "customer.tier"
Backlex::Filter.gte("placed_at", Backlex::Filter.now(sub: { "months" => 1 })),
))
.select("id", "total", "customer.name")
.order_by("-placed_at", "id")
.limit(50)
.list
Class Method Summary collapse
- .and_(*conds) ⇒ Object
- .between(f, lo, hi) ⇒ Object
- .comparison?(hash) ⇒ Boolean
- .contains(f, v) ⇒ Object
- .empty(f) ⇒ Object
- .ends_with(f, v) ⇒ Object
- .eq(f, v) ⇒ Object
- .gt(f, v) ⇒ Object
- .gte(f, v) ⇒ Object
- .icontains(f, v) ⇒ Object
- .in_(f, vs) ⇒ Object
- .is_null(f, is_null = true) ⇒ Object
- .leaf(field, op, value) ⇒ Object
- .lt(f, v) ⇒ Object
- .lte(f, v) ⇒ Object
- .nempty(f) ⇒ Object
- .neq(f, v) ⇒ Object
- .nin(f, vs) ⇒ Object
-
.normalize(raw) ⇒ Object
Turn any accepted filter shape into the canonical Condition: handles $and/$or/$not (and their _ aliases) and implicit equality ({ “status” => “active” } -> { “status” => { “_eq” => “active” } }).
- .not_(cond) ⇒ Object
-
.now(add: nil, sub: nil) ⇒ Object
Relative-date value, e.g.
- .or_(*conds) ⇒ Object
- .prefix_keys(cond, head) ⇒ Object
-
.rel(head, *conds) ⇒ Object
Traverse a relation one hop: every leaf key produced by
condsis prefixed with “head.”. - .starts_with(f, v) ⇒ Object
Class Method Details
.and_(*conds) ⇒ Object
43 |
# File 'lib/backlex/filter.rb', line 43 def and_(*conds); { "$and" => conds }; end |
.between(f, lo, hi) ⇒ Object
34 |
# File 'lib/backlex/filter.rb', line 34 def between(f, lo, hi); leaf(f, "_between", [lo, hi]); end |
.comparison?(hash) ⇒ Boolean
99 100 101 |
# File 'lib/backlex/filter.rb', line 99 def comparison?(hash) !hash.empty? && hash.keys.all? { |k| k.to_s.start_with?("_") } end |
.contains(f, v) ⇒ Object
38 |
# File 'lib/backlex/filter.rb', line 38 def contains(f, v); leaf(f, "_contains", v); end |
.empty(f) ⇒ Object
36 |
# File 'lib/backlex/filter.rb', line 36 def empty(f); leaf(f, "_empty", true); end |
.ends_with(f, v) ⇒ Object
41 |
# File 'lib/backlex/filter.rb', line 41 def ends_with(f, v); leaf(f, "_ends_with", v); end |
.eq(f, v) ⇒ Object
26 |
# File 'lib/backlex/filter.rb', line 26 def eq(f, v); leaf(f, "_eq", v); end |
.gt(f, v) ⇒ Object
28 |
# File 'lib/backlex/filter.rb', line 28 def gt(f, v); leaf(f, "_gt", v); end |
.gte(f, v) ⇒ Object
29 |
# File 'lib/backlex/filter.rb', line 29 def gte(f, v); leaf(f, "_gte", v); end |
.icontains(f, v) ⇒ Object
39 |
# File 'lib/backlex/filter.rb', line 39 def icontains(f, v); leaf(f, "_icontains", v); end |
.in_(f, vs) ⇒ Object
32 |
# File 'lib/backlex/filter.rb', line 32 def in_(f, vs); leaf(f, "_in", vs); end |
.is_null(f, is_null = true) ⇒ Object
35 |
# File 'lib/backlex/filter.rb', line 35 def is_null(f, is_null = true); leaf(f, "_null", is_null); end |
.leaf(field, op, value) ⇒ Object
22 23 24 |
# File 'lib/backlex/filter.rb', line 22 def leaf(field, op, value) { field => { op => value } } end |
.lt(f, v) ⇒ Object
30 |
# File 'lib/backlex/filter.rb', line 30 def lt(f, v); leaf(f, "_lt", v); end |
.lte(f, v) ⇒ Object
31 |
# File 'lib/backlex/filter.rb', line 31 def lte(f, v); leaf(f, "_lte", v); end |
.nempty(f) ⇒ Object
37 |
# File 'lib/backlex/filter.rb', line 37 def nempty(f); leaf(f, "_nempty", true); end |
.neq(f, v) ⇒ Object
27 |
# File 'lib/backlex/filter.rb', line 27 def neq(f, v); leaf(f, "_neq", v); end |
.nin(f, vs) ⇒ Object
33 |
# File 'lib/backlex/filter.rb', line 33 def nin(f, vs); leaf(f, "_nin", vs); end |
.normalize(raw) ⇒ Object
Turn any accepted filter shape into the canonical Condition: handles $and/$or/$not (and their _ aliases) and implicit equality ({ “status” => “active” } -> { “status” => { “_eq” => “active” } }). Idempotent.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/backlex/filter.rb', line 75 def normalize(raw) return {} unless raw.is_a?(Hash) a = raw["$and"] || raw["_and"] return { "$and" => a.map { |c| normalize(c) } } if a.is_a?(Array) o = raw["$or"] || raw["_or"] return { "$or" => o.map { |c| normalize(c) } } if o.is_a?(Array) if raw.key?("$not") || raw.key?("_not") return { "$not" => normalize(raw["$not"] || raw["_not"]) } end out = {} raw.each do |k, v| out[k] = if v.is_a?(Hash) && comparison?(v) then v elsif v.is_a?(Hash) then v # unknown object shape — pass through else { "_eq" => v } end end out end |
.not_(cond) ⇒ Object
45 |
# File 'lib/backlex/filter.rb', line 45 def not_(cond); { "$not" => cond }; end |
.now(add: nil, sub: nil) ⇒ Object
Relative-date value, e.g. Filter.now(sub: { “months” => 1 }).
55 56 57 58 59 60 |
# File 'lib/backlex/filter.rb', line 55 def now(add: nil, sub: nil) opts = {} opts["add"] = add if add opts["sub"] = sub if sub { "$now" => opts } end |
.or_(*conds) ⇒ Object
44 |
# File 'lib/backlex/filter.rb', line 44 def or_(*conds); { "$or" => conds }; end |
.prefix_keys(cond, head) ⇒ Object
62 63 64 65 66 67 68 69 70 |
# File 'lib/backlex/filter.rb', line 62 def prefix_keys(cond, head) return { "$and" => cond["$and"].map { |c| prefix_keys(c, head) } } if cond["$and"].is_a?(Array) return { "$or" => cond["$or"].map { |c| prefix_keys(c, head) } } if cond["$or"].is_a?(Array) return { "$not" => prefix_keys(cond["$not"], head) } if cond["$not"].is_a?(Hash) out = {} cond.each { |k, v| out["#{head}.#{k}"] = v } out end |
.rel(head, *conds) ⇒ Object
Traverse a relation one hop: every leaf key produced by conds is prefixed with “head.”. Multiple conds are ANDed first.
49 50 51 52 |
# File 'lib/backlex/filter.rb', line 49 def rel(head, *conds) inner = conds.length == 1 ? conds[0] : { "$and" => conds } prefix_keys(inner, head) end |
.starts_with(f, v) ⇒ Object
40 |
# File 'lib/backlex/filter.rb', line 40 def starts_with(f, v); leaf(f, "_starts_with", v); end |