1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Mongo queries framework
19 # The `queries` framework is used to build Mongo queries as JsonObject with
22 # Using the `queries` framework we can get from this:
25 # var exists = new JsonObject
26 # exists["$exists"] = true
28 # var query = new JsonObject
29 # query["login"] = "Morriar"
30 # query["email"] = exists
32 # collection.find(query)
38 # collection.find((new MongoMatch).eq("login", "Morriar").exists("email", true))
41 # The framework provides three classes used to map the MongoDB query API:
42 # * `MongoMatch` the base query that can be used with most Mongo services
43 # * `MongoPipeline` the array of queries that is expected by `MongoCollection::aggregate`
44 # * `MongoGroup` the group query for a `MongoPipeline`
46 # More on this features can be found in the official MongoDB documentation:
47 # https://docs.mongodb.com/manual/reference/operator/
54 # `MongoMatch` is used with most of the Mongo services like `find`, `find_all`,
57 # Building a query can be done with the fluent interface:
60 # var query = (new MongoMatch).
61 # eq("login", "Morriar").
63 # exists("email", true).
64 # is_in("status", [1, 2, 3, 4])
67 # Fore more help on how to use the query operators of MongoDB please
68 # refer to the official MongoDB documentation:
69 # https://docs.mongodb.com/manual/reference/operator/query/
73 # Define a custom operaton for `field`
75 # If no `field` is specified, append the operator the the root object:
80 # Else, append the operator to the field:
82 # {field: {$<name>: <value>} }
84 fun op
(name
: String, field
: nullable String, value
: nullable Jsonable): MongoMatch do
86 var q
= new JsonObject
95 # Match documents where `field` equals `value`
97 # https://docs.mongodb.com/manual/reference/operator/query/eq/#op._S_eq
100 # {field: {$eq: value} }
102 fun eq
(field
: String, value
: nullable Jsonable): MongoMatch do
107 # Match documents where `field` not equals `value`
109 # https://docs.mongodb.com/manual/reference/operator/query/ne/#op._S_ne
112 # {field: {$ne: value} }
114 fun ne
(field
: String, value
: nullable Jsonable): MongoMatch do
115 op
("ne", field
, value
)
119 # Match documents where `field` is greater than `value`
121 # https://docs.mongodb.com/manual/reference/operator/query/gt/#op._S_gt
124 # {field: {$gt: value} }
126 fun gt
(field
: String, value
: nullable Jsonable): MongoMatch do
127 op
("gt", field
, value
)
131 # Match documents where `field` is greater or equal to `value`
133 # https://docs.mongodb.com/manual/reference/operator/query/gte/#op._S_gte
136 # {field: {$gte: value} }
138 fun gte
(field
: String, value
: nullable Jsonable): MongoMatch do
139 op
("gte", field
, value
)
143 # Match documents where `field` is less than `value`
145 # https://docs.mongodb.com/manual/reference/operator/query/lt/#op._S_lt
148 # {field: {$lt: value} }
150 fun lt
(field
: String, value
: nullable Jsonable): MongoMatch do
151 op
("lt", field
, value
)
155 # Match documents where `field` is less or equal to `value`
157 # https://docs.mongodb.com/manual/reference/operator/query/lte/
160 # {field: {$lte: value} }
162 fun lte
(field
: String, value
: nullable Jsonable): MongoMatch do
163 op
("lte", field
, value
)
167 # Match documents where `field` exists or not
169 # https://docs.mongodb.com/manual/reference/operator/query/exists/#op._S_exists
172 # {field: {$exists: boolean} }
175 # When `exists` is true, `$exists` matches the documents that contain the
176 # field, including documents where the field value is null.
177 # If <boolean> is false, the query returns only the documents that do not
179 fun exists
(field
: String, exists
: Bool): MongoMatch do
180 op
("exists", field
, exists
)
184 # Match documents where `field` matches `pattern`
186 # To read more about the available options, see:
187 # https://docs.mongodb.com/manual/reference/operator/query/regex/#op._S_regex
190 # {field: {$regex: 'pattern', $options: '<options>'} }
193 # Provides regular expression capabilities for pattern matching strings in queries.
194 # MongoDB uses Perl compatible regular expressions (i.e. "PCRE" ).
195 fun regex
(field
: String, pattern
: String, options
: nullable String): MongoMatch do
196 var q
= new JsonObject
197 q
["$regex"] = pattern
198 if options
!= null then q
["$options"] = options
203 # Match documents where `field` is in `values`
205 # https://docs.mongodb.com/manual/reference/operator/query/in/
208 # { field: { $in: [<value1>, <value2>, ... <valueN> ] } }
211 # `$in` selects the documents where the value of a field equals any value
212 # in the specified array.
213 fun is_in
(field
: String, values
: Array[nullable Jsonable]): MongoMatch do
214 op
("$in", field
, new JsonArray.from
(values
))
218 # Match documents where `field` is not in `values`
220 # https://docs.mongodb.com/manual/reference/operator/query/nin/
223 # { field: { $nin: [<value1>, <value2>, ... <valueN> ] } }
226 # `$nin` selects the documents where:
227 # * the field value is not in the specified array or
228 # * the field does not exist.
229 fun is_nin
(field
: String, values
: Array[nullable Jsonable]): MongoMatch do
230 op
("$nin", field
, new JsonArray.from
(values
))
235 # Mongo pipelines are arrays of aggregation stages
237 # With the `MongoCollection::aggregate` method, pipeline stages appear in a array.
238 # Documents pass through the stages in sequence.
241 # db.collection.aggregate( [ { <stage> }, ... ] )
244 # The MongoPipeline fluent interface can be used to bluid a pipeline:
246 # var pipeline = (new MongoPipeline).
247 # match((new MongoMatch).eq("game", "nit")).
248 # group((new MongoGroup("$game._id")).sum("nitcoins", "$game.nitcoins")).
249 # sort((new MongoMatch).eq("nitcoins", -1)).
253 # The pipeline can then be used in an aggregation query:
255 # collection.aggregate(pipeline)
258 # For more information read about MongoDB pipeline operators from the MongoDB
259 # official documentation: https://docs.mongodb.com/manual/reference/operator/aggregation/
263 # Add a stage to the pipeline
265 # https://docs.mongodb.com/manual/reference/operator/aggregation/#stage-operators
267 # Each stage is registered as:
269 # { $<stage>: <json> }
271 fun add_stage
(stage
: String, json
: Jsonable): MongoPipeline do
272 var obj
= new JsonObject
273 obj
["${stage}"] = json
280 # https://docs.mongodb.com/manual/reference/operator/aggregation/project/#pipe._S_project
282 # Passes along the documents with only the specified fields to the next stage
286 # { $project: { <specifications> } }
289 # The specified fields can be existing fields from the input documents or
290 # newly computed fields.
291 fun project
(projection
: JsonObject): MongoPipeline do return add_stage
("project", projection
)
295 # https://docs.mongodb.com/manual/reference/operator/aggregation/match/
297 # Filters the documents to pass only the documents that match the specified
298 # condition(s) to the next pipeline stage.
301 # { $match: { <query> } }
303 fun match
(query
: MongoMatch): MongoPipeline do return add_stage
("match", query
)
307 # https://docs.mongodb.com/manual/reference/operator/aggregation/sort/
309 # Sorts all input documents and returns them to the pipeline in sorted order.
312 # { $sort: { <projection> } }
314 fun sort
(projection
: JsonObject): MongoPipeline do return add_stage
("sort", projection
)
318 # https://docs.mongodb.com/manual/reference/operator/aggregation/skip/
320 # Skips over the specified number of documents that pass into the stage and
321 # passes the remaining documents to the next stage in the pipeline.
324 # { $skip: { <number> } }
327 # If `number == null` then no skip stage is generated
328 fun skip
(number
: nullable Int): MongoPipeline do
329 if number
== null then return self
330 return add_stage
("skip", number
)
335 # https://docs.mongodb.com/manual/reference/operator/aggregation/limit/
337 # Limits the number of documents passed to the next stage in the pipeline.
340 # { $limit: { <number> } }
343 # If `number == null` then no limit stage is generated
344 fun limit
(number
: nullable Int): MongoPipeline do
345 if number
== null then return self
346 return add_stage
("limit", number
)
351 # https://docs.mongodb.com/manual/reference/operator/aggregation/group/
353 # Groups documents by some specified expression and outputs to the next stage
354 # a document for each distinct grouping.
356 # The output documents contain an `_id` field which contains the distinct
359 # The output documents can also contain computed fields that hold the values
360 # of some accumulator expression grouped by the `$group`'s `_id` field.
361 # `$group` does not order its output documents.
364 # { $group: { <group> } }
366 fun group
(group
: MongoGroup): MongoPipeline do return add_stage
("group", group
)
369 # Mongo pipeline group stage
371 # https://docs.mongodb.com/manual/reference/operator/aggregation/group/#pipe._S_group
373 # Groups documents by some specified expression and outputs to the next stage a
374 # document for each distinct grouping.
377 # var group = (new MongoGroup("$game._id")).sum("nitcoins", "$game.nitcoins")
379 # var pipeline = (new MongoPipeline).group(group)
382 # The output documents contain an `_id` field which contains the distinct group by key.
383 # The output documents can also contain computed fields that hold the values of
384 # some accumulator expression grouped by the `$group`‘s `_id` field.
385 # `$group` does not order its output documents.
387 # The `$group` stage has the following prototype form:
390 # { $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
393 # The `_id` field is mandatory; however, you can specify an `_id` value of null
394 # to calculate accumulated values for all the input documents as a whole.
396 # The remaining computed fields are optional and computed using the `<accumulator>`
403 # See `MongoGroup::group`.
406 init do self["_id"] = id
410 # Each accumulator is registered as:
412 # <field>: { <accumulator> : <expression> }
414 private fun acc
(name
: String, field
: String, expression
: nullable Jsonable): MongoGroup do
415 var q
= new JsonObject
416 q
["${name}"] = expression
421 # Calculates and returns the sum of numeric values
423 # https://docs.mongodb.com/manual/reference/operator/aggregation/sum/#grp._S_sum
426 # { $sum: <expression> }
429 # `$sum` ignores non-numeric values.
430 fun sum
(field
: String, expression
: Jsonable): MongoGroup do
431 return acc
("sum", field
, expression
)
434 # Returns the average value of the numeric values
436 # https://docs.mongodb.com/manual/reference/operator/aggregation/avg/
439 # { $avg: <expression> }
442 # `$avg` ignores non-numeric values.
443 fun avg
(field
: String, expression
: Jsonable): MongoGroup do
444 return acc
("avg", field
, expression
)
447 # Returns the maximum value
449 # https://docs.mongodb.com/manual/reference/operator/aggregation/max/
452 # { $max: <expression> }
455 # `$max` compares both value and type, using the specified BSON comparison
456 # order for values of different types.
457 fun max
(field
: String, expression
: Jsonable): MongoGroup do
458 return acc
("max", field
, expression
)
461 # Returns the minimum value
463 # https://docs.mongodb.com/manual/reference/operator/aggregation/min/
466 # { $min: <expression> }
469 # `$min` compares both value and type, using the specified BSON comparison
470 # order for values of different types.
471 fun min
(field
: String, expression
: Jsonable): MongoGroup do
472 return acc
("min", field
, expression
)
475 # Return the first value
477 # https://docs.mongodb.com/manual/reference/operator/aggregation/first/
480 # { $first: <expression> }
483 # Returns the value that results from applying an expression to the first
484 # document in a group of documents that share the same group by key.
486 # Only meaningful when documents are in a defined order.
487 fun first
(field
: String, expression
: Jsonable): MongoGroup do
488 return acc
("first", field
, expression
)
491 # Return the last value
493 # https://docs.mongodb.com/manual/reference/operator/aggregation/last/
496 # { $last: <expression> }
499 # Returns the value that results from applying an expression to the last
500 # document in a group of documents that share the same group by key.
502 # Only meaningful when documents are in a defined order.
503 fun last
(field
: String, expression
: Jsonable): MongoGroup do
504 return acc
("last", field
, expression
)
509 # https://docs.mongodb.com/manual/reference/operator/aggregation/push/
512 # { $push: <expression> }
515 # Returns an array of all values that result from applying an expression to
516 # each document in a group of documents that share the same group by key.
517 fun push
(field
: String, expr
: Jsonable): MongoGroup do
518 return acc
("push", field
, expr
)
521 # Push to a unique array
523 # https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/
526 # { $addToSet: <expression> }
529 # Returns an array of all unique values that results from applying an
530 # expression to each document in a group of documents that share the same
533 # Order of the elements in the output array is unspecified.
534 fun addToSet
(field
: String, expr
: Jsonable): MongoGroup do
535 return acc
("addToSet", field
, expr
)