forked from eugeneware/jsonquery
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsonquery.coffee
More file actions
202 lines (152 loc) · 3.75 KB
/
jsonquery.coffee
File metadata and controls
202 lines (152 loc) · 3.75 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
Stream = require('stream')
# dereference value being matched against
lookup = (needle, haystack) ->
keys = needle.split '.'
val = haystack
for key in keys
return if val is undefined
val = val[key]
val
# equality that also does comparison against RegExps
eq = (a, b) ->
if b instanceof RegExp
b.test a
else if Array.isArray(a)
a.indexOf(b) != -1
else
b == a
# checking the type against mongoDB BSON types
checkType = (val, typeId) ->
switch typeId
when 1 # Double
typeof val == 'number'
when 2 # String
typeof val == 'string'
when 3 # Object
typeof val == 'object'
when 4 # Array
val instanceof Array
when 8 # Boolean
typeof val == 'boolean'
when 10 # Null
typeof val == 'object' and not val
else
false
# match predicate expression against document
match = (predicate, haystack) ->
matches = 0
matchCount = Object.keys(predicate).length
for n, v of predicate
# LHS $and, $or, $not
if n[0] == '$'
matches += operator n, v, haystack
# complex RHS predicate (eg. $in, $gt)
else if v and v.constructor == Object
matches++ if valOpMatch(lookup(n, haystack), v, haystack)
# simple lookup
else
matches++ if eq(lookup(n, haystack), v)
matches == matchCount
# process complex RHS predicates (eg. $in, $gt)
valOpMatch = (val, predicate, haystack) ->
matchCount = Object.keys(predicate).length
matches = 0
for n, v of predicate
# keys must be an operator
if n[0] == '$'
matches++ if valOp(n, val, v, haystack)
matches == matchCount
# operators on the RHS of queries
valOp = (op, val, args, haystack) ->
switch op
when '$in'
matchCount = 0
matches = 0
for part in args
matches++ if eq(val, part)
matches > 0
when '$nin'
not valOp('$in', val, args, haystack)
when '$all'
matchCount = args.length
matches = 0
if val
for part in args
for v in val
if eq(v, part)
matches++
break
matches == matchCount
when '$elemMatch'
match args, val
when '$or'
matches = 0
for part in args
matches++ if valOpMatch(val, part, haystack)
matches > 0
when '$and'
matchCount = args.length
matches = 0
for part in args
matches++ if valOpMatch(val, part, haystack)
matches == matchCount
when '$not'
not valOpMatch(val, args, haystack)
when '$gt'
val > args
when '$gte'
val >= args
when '$ne'
val != args
when '$lt'
val < args
when '$lte'
val <= args
when '$mod'
(val % args[0]) == args[1]
when '$size'
val instanceof Array and val.length == args
when '$exists'
if args
val isnt undefined
else
val is undefined
when '$type'
checkType val, args
else
false
# operations on the LHS of queries
operator = (op, predicate, haystack) ->
switch op
when '$or'
matches = 0
for part in predicate
matches++ if match(part, haystack)
matches > 0
when '$and'
matchCount = predicate.length
matches = 0
for part in predicate
matches++ if match(part, haystack)
matches == matchCount
when '$not'
not match(predicate, haystack)
else
false
queryStream = (query) ->
s = new Stream
s.writable = true
s.readable = true
s.write = (buf) ->
if match query, buf
s.emit('data', buf)
s.end = (buf) ->
if arguments.length then s.write(buf)
s.writeable = false
s.emit('end')
s.destroy = ->
s.writeable = false
s
module.exports = queryStream
module.exports.match = (haystack, predicate) ->
match predicate, haystack