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
90 self["${name}"] = value
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
))
236 # https://docs.mongodb.com/manual/reference/operator/query/or/#op._S_or
238 # The `$or` operator performs a logical OR operation on an array of two or
239 # more `expressions` and selects the documents that satisfy at least one of
242 # The `$or` has the following syntax:
245 # { field: { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] } }
247 fun lor
(field
: nullable String, expressions
: Array[Jsonable]): MongoMatch do
248 op
("or", field
, new JsonArray.from
(expressions
))
254 # https://docs.mongodb.com/manual/reference/operator/query/and/#op._S_and
256 # The `$and` operator performs a logical AND operation on an array of two or
257 # more `expressions` and selects the documents that satisfy all of the `expressions`.
259 # The `$and` has the following syntax:
262 # { field: { $and: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] } }
264 fun land
(field
: nullable String, expressions
: Array[Jsonable]): MongoMatch do
265 op
("and", field
, new JsonArray.from
(expressions
))
271 # https://docs.mongodb.com/manual/reference/operator/query/not/#op._S_not
273 # `$not` performs a logical NOT operation on the specified `expression` and
274 # selects the documents that do not match the `expression`.
275 # This includes documents that do not contain the field.
277 # The $not has the following syntax:
280 # { field: { $not: { <expression> } } }
282 fun lnot
(field
: nullable String, expression
: Jsonable): MongoMatch do
283 op
("not", field
, expression
)
289 # https://docs.mongodb.com/manual/reference/operator/query/nor/#op._S_nor
291 # `$nor` performs a logical NOR operation on an array of one or more query
292 # expression and selects the documents that fail all the query expressions
295 # The $nor has the following syntax:
298 # { field: { $nor: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] } }
300 fun lnor
(field
: nullable String, expressions
: Array[Jsonable]): MongoMatch do
301 op
("nor", field
, new JsonArray.from
(expressions
))
307 # https://docs.mongodb.com/manual/reference/operator/query/all/#op._S_all
309 # `$all` selects the documents where the value of a field is an array that
310 # contains all the specified elements.
313 # { field: { $all: [ <value1>, <value2>, ... ] } }
315 fun all
(field
: nullable String, values
: Array[Jsonable]): MongoMatch do
316 op
("all", field
, new JsonArray.from
(values
))
320 # Array element match
322 # https://docs.mongodb.com/manual/reference/operator/query/elemMatch/#op._S_elemMatch
324 # `$elemMatch` matches documents that contain an array field with at least
325 # one element that matches all the specified query criteria.
328 # { field: { $elemMatch: <query> } }
330 fun elem_match
(field
: nullable String, query
: Jsonable): MongoMatch do
331 op
("elemMatch", field
, query
)
337 # https://docs.mongodb.com/manual/reference/operator/query/size/#op._S_size
339 # `$size` matches any array with the number of elements specified by the argument
342 # { field: { $size: <size> } }
344 fun size
(field
: nullable String, size
: Int): MongoMatch do
345 op
("size", field
, size
)
350 # Mongo pipelines are arrays of aggregation stages
352 # With the `MongoCollection::aggregate` method, pipeline stages appear in a array.
353 # Documents pass through the stages in sequence.
356 # db.collection.aggregate( [ { <stage> }, ... ] )
359 # The MongoPipeline fluent interface can be used to bluid a pipeline:
361 # var pipeline = (new MongoPipeline).
362 # match((new MongoMatch).eq("game", "nit")).
363 # group((new MongoGroup("$game._id")).sum("nitcoins", "$game.nitcoins")).
364 # sort((new MongoMatch).eq("nitcoins", -1)).
368 # The pipeline can then be used in an aggregation query:
370 # collection.aggregate(pipeline)
373 # For more information read about MongoDB pipeline operators from the MongoDB
374 # official documentation: https://docs.mongodb.com/manual/reference/operator/aggregation/
378 # Add a stage to the pipeline
380 # https://docs.mongodb.com/manual/reference/operator/aggregation/#stage-operators
382 # Each stage is registered as:
384 # { $<stage>: <json> }
386 fun add_stage
(stage
: String, json
: Jsonable): MongoPipeline do
387 var obj
= new JsonObject
388 obj
["${stage}"] = json
395 # https://docs.mongodb.com/manual/reference/operator/aggregation/project/#pipe._S_project
397 # Passes along the documents with only the specified fields to the next stage
401 # { $project: { <specifications> } }
404 # The specified fields can be existing fields from the input documents or
405 # newly computed fields.
406 fun project
(projection
: JsonObject): MongoPipeline do return add_stage
("project", projection
)
410 # https://docs.mongodb.com/manual/reference/operator/aggregation/match/
412 # Filters the documents to pass only the documents that match the specified
413 # condition(s) to the next pipeline stage.
416 # { $match: { <query> } }
418 fun match
(query
: MongoMatch): MongoPipeline do return add_stage
("match", query
)
422 # https://docs.mongodb.com/manual/reference/operator/aggregation/sort/
424 # Sorts all input documents and returns them to the pipeline in sorted order.
427 # { $sort: { <projection> } }
429 fun sort
(projection
: JsonObject): MongoPipeline do return add_stage
("sort", projection
)
433 # https://docs.mongodb.com/manual/reference/operator/aggregation/skip/
435 # Skips over the specified number of documents that pass into the stage and
436 # passes the remaining documents to the next stage in the pipeline.
439 # { $skip: { <number> } }
442 # If `number == null` then no skip stage is generated
443 fun skip
(number
: nullable Int): MongoPipeline do
444 if number
== null then return self
445 return add_stage
("skip", number
)
450 # https://docs.mongodb.com/manual/reference/operator/aggregation/limit/
452 # Limits the number of documents passed to the next stage in the pipeline.
455 # { $limit: { <number> } }
458 # If `number == null` then no limit stage is generated
459 fun limit
(number
: nullable Int): MongoPipeline do
460 if number
== null then return self
461 return add_stage
("limit", number
)
466 # https://docs.mongodb.com/manual/reference/operator/aggregation/group/
468 # Groups documents by some specified expression and outputs to the next stage
469 # a document for each distinct grouping.
471 # The output documents contain an `_id` field which contains the distinct
474 # The output documents can also contain computed fields that hold the values
475 # of some accumulator expression grouped by the `$group`'s `_id` field.
476 # `$group` does not order its output documents.
479 # { $group: { <group> } }
481 fun group
(group
: MongoGroup): MongoPipeline do return add_stage
("group", group
)
485 # https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
487 # Deconstructs an array field from the input documents to output a document
489 # Each output document is the input document with the value of the array
490 # field replaced by the element.
493 # { $unwind: <field path> }
495 fun unwind
(path
: String): MongoPipeline do return add_stage
("unwind", path
)
498 # Mongo pipeline group stage
500 # https://docs.mongodb.com/manual/reference/operator/aggregation/group/#pipe._S_group
502 # Groups documents by some specified expression and outputs to the next stage a
503 # document for each distinct grouping.
506 # var group = (new MongoGroup("$game._id")).sum("nitcoins", "$game.nitcoins")
508 # var pipeline = (new MongoPipeline).group(group)
511 # The output documents contain an `_id` field which contains the distinct group by key.
512 # The output documents can also contain computed fields that hold the values of
513 # some accumulator expression grouped by the `$group`‘s `_id` field.
514 # `$group` does not order its output documents.
516 # The `$group` stage has the following prototype form:
519 # { $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
522 # The `_id` field is mandatory; however, you can specify an `_id` value of null
523 # to calculate accumulated values for all the input documents as a whole.
525 # The remaining computed fields are optional and computed using the `<accumulator>`
532 # See `MongoGroup::group`.
535 init do self["_id"] = id
539 # Each accumulator is registered as:
541 # <field>: { <accumulator> : <expression> }
543 private fun acc
(name
: String, field
: String, expression
: nullable Jsonable): MongoGroup do
544 var q
= new JsonObject
545 q
["${name}"] = expression
550 # Calculates and returns the sum of numeric values
552 # https://docs.mongodb.com/manual/reference/operator/aggregation/sum/#grp._S_sum
555 # { $sum: <expression> }
558 # `$sum` ignores non-numeric values.
559 fun sum
(field
: String, expression
: Jsonable): MongoGroup do
560 return acc
("sum", field
, expression
)
563 # Returns the average value of the numeric values
565 # https://docs.mongodb.com/manual/reference/operator/aggregation/avg/
568 # { $avg: <expression> }
571 # `$avg` ignores non-numeric values.
572 fun avg
(field
: String, expression
: Jsonable): MongoGroup do
573 return acc
("avg", field
, expression
)
576 # Returns the maximum value
578 # https://docs.mongodb.com/manual/reference/operator/aggregation/max/
581 # { $max: <expression> }
584 # `$max` compares both value and type, using the specified BSON comparison
585 # order for values of different types.
586 fun max
(field
: String, expression
: Jsonable): MongoGroup do
587 return acc
("max", field
, expression
)
590 # Returns the minimum value
592 # https://docs.mongodb.com/manual/reference/operator/aggregation/min/
595 # { $min: <expression> }
598 # `$min` compares both value and type, using the specified BSON comparison
599 # order for values of different types.
600 fun min
(field
: String, expression
: Jsonable): MongoGroup do
601 return acc
("min", field
, expression
)
604 # Return the first value
606 # https://docs.mongodb.com/manual/reference/operator/aggregation/first/
609 # { $first: <expression> }
612 # Returns the value that results from applying an expression to the first
613 # document in a group of documents that share the same group by key.
615 # Only meaningful when documents are in a defined order.
616 fun first
(field
: String, expression
: Jsonable): MongoGroup do
617 return acc
("first", field
, expression
)
620 # Return the last value
622 # https://docs.mongodb.com/manual/reference/operator/aggregation/last/
625 # { $last: <expression> }
628 # Returns the value that results from applying an expression to the last
629 # document in a group of documents that share the same group by key.
631 # Only meaningful when documents are in a defined order.
632 fun last
(field
: String, expression
: Jsonable): MongoGroup do
633 return acc
("last", field
, expression
)
638 # https://docs.mongodb.com/manual/reference/operator/aggregation/push/
641 # { $push: <expression> }
644 # Returns an array of all values that result from applying an expression to
645 # each document in a group of documents that share the same group by key.
646 fun push
(field
: String, expr
: Jsonable): MongoGroup do
647 return acc
("push", field
, expr
)
650 # Push to a unique array
652 # https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/
655 # { $addToSet: <expression> }
658 # Returns an array of all unique values that results from applying an
659 # expression to each document in a group of documents that share the same
662 # Order of the elements in the output array is unspecified.
663 fun addToSet
(field
: String, expr
: Jsonable): MongoGroup do
664 return acc
("addToSet", field
, expr
)