-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Expand file tree
/
Copy pathmatches-where.ts
More file actions
95 lines (76 loc) · 3.05 KB
/
Copy pathmatches-where.ts
File metadata and controls
95 lines (76 loc) · 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import type { JsonObject } from 'type-fest'
import { WHERE_OPERATORS, type WhereOperator } from './where-operators.ts'
type OperatorObject = Partial<Record<WhereOperator, unknown>>
function isJSONObject(value: unknown): value is JsonObject {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
function getKnownOperators(value: unknown): WhereOperator[] {
if (!isJSONObject(value)) return []
const ops: WhereOperator[] = []
for (const op of WHERE_OPERATORS) {
if (op in value) {
ops.push(op)
}
}
return ops
}
export function matchesWhere(obj: JsonObject, where: JsonObject): boolean {
for (const [key, value] of Object.entries(where)) {
if (key === 'or') {
if (!Array.isArray(value) || value.length === 0) return false
let matched = false
for (const subWhere of value) {
if (isJSONObject(subWhere) && matchesWhere(obj, subWhere)) {
matched = true
break
}
}
if (!matched) return false
continue
}
const field = (obj as Record<string, unknown>)[key]
if (isJSONObject(value)) {
const knownOps = getKnownOperators(value)
if (knownOps.length > 0) {
if (field === undefined) return false
const op = value as OperatorObject
if (knownOps.includes('lt') && !((field as any) < (op.lt as any))) return false
if (knownOps.includes('lte') && !((field as any) <= (op.lte as any))) return false
if (knownOps.includes('gt') && !((field as any) > (op.gt as any))) return false
if (knownOps.includes('gte') && !((field as any) >= (op.gte as any))) return false
if (knownOps.includes('eq') && !((field as any) === (op.eq as any))) return false
if (knownOps.includes('ne') && !((field as any) !== (op.ne as any))) return false
if (knownOps.includes('in')) {
const inValues = Array.isArray(op.in) ? op.in : [op.in]
if (!inValues.some((v) => (field as any) === (v as any))) return false
}
if (knownOps.includes('contains')) {
if (typeof field !== 'string') return false
if (!field.toLowerCase().includes(String(op.contains).toLowerCase())) return false
}
if (knownOps.includes('startsWith')) {
if (typeof field !== 'string') return false
if (!field.toLowerCase().startsWith(String(op.startsWith).toLowerCase())) return false
}
if (knownOps.includes('endsWith')) {
if (typeof field !== 'string') return false
if (!field.toLowerCase().endsWith(String(op.endsWith).toLowerCase())) return false
}
continue
}
// An array matches if at least one of its items matches
if (Array.isArray(field)) {
const matched = field.some((item) => isJSONObject(item) && matchesWhere(item, value))
if (!matched) return false
continue
}
if (isJSONObject(field)) {
if (!matchesWhere(field, value)) return false
}
continue
}
if (field === undefined) return false
return false
}
return true
}