Skip to content

Commit

Permalink
Support $toLong operator (#705)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Londoño <dlondono@azimutenergia.co>
Co-authored-by: Pascal Corpet <pascal@bayesimpact.org>
  • Loading branch information
3 people committed May 25, 2021
1 parent e46c39b commit 26d82f9
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Missing_Features.rst
Expand Up @@ -33,7 +33,7 @@ If I miss to include a feature in the below list, Please feel free to add to the
* Projection operator $map
* Array operators ($isArray, $indexOfArray, …)
* `$mergeObjects <https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/>`_
* Some type conversion operators ($convert, $toLong, …)
* Some type conversion operators ($convert, …)
* Operators within the query language (find):
* `$jsonSchema <https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/>`_
* `$text <https://docs.mongodb.com/manual/reference/operator/query/text/>`_ search
Expand Down
13 changes: 13 additions & 0 deletions mongomock/aggregate.py
Expand Up @@ -693,6 +693,19 @@ def _handle_type_convertion_operator(self, operator, values):
'You need to import the pymongo library to support decimal128 type.'
)

if operator == '$toLong':
try:
parsed = self.parse(values)
except KeyError:
return None
if decimal_support:
if isinstance(parsed, decimal128.Decimal128):
return helpers.to_long(parsed.to_decimal())
return helpers.to_long(parsed)
raise NotImplementedError(
'You need to import the pymongo library to support decimal128 type.'
)

# Document: https://docs.mongodb.com/manual/reference/operator/aggregation/toDecimal/
if operator == '$toDecimal':
if not decimal_support:
Expand Down
10 changes: 9 additions & 1 deletion mongomock/helpers.py
Expand Up @@ -3,7 +3,7 @@
from mongomock import InvalidURI
import re
from six.moves.urllib_parse import unquote_plus
from six import iteritems, raise_from, string_types
from six import PY3, iteritems, raise_from, string_types
import time
import warnings

Expand Down Expand Up @@ -418,3 +418,11 @@ def mongodb_to_bool(value):
"""Converts any value to bool the way MongoDB does it"""

return value not in [False, None, 0]


def to_long(value):
"""Backwards compatible `long` function. It tries to convert input to a long integer."""
if PY3:
global long # pylint: disable=global-variable-undefined
long = int
return long(value)
67 changes: 62 additions & 5 deletions tests/test__collection_api.py
Expand Up @@ -4455,11 +4455,6 @@ def test__aggregate_not_implemented(self):
{'$project': {'a': {'$setIntersection': [[2], [1, 2, 3]]}}},
])

with self.assertRaises(NotImplementedError):
self.db.collection.aggregate([
{'$project': {'a': {'$toLong': '$scores'}}},
])

def test__aggregate_project_let(self):
self.db.collection.insert_one({'_id': 1, 'a': 5, 'b': 2, 'c': 3})
actual = self.db.collection.aggregate([{'$project': {
Expand Down Expand Up @@ -6100,6 +6095,68 @@ def test__aggregate_to_int(self):
}]
self.assertEqual(expect, list(actual))

@skipIf(not _HAVE_PYMONGO, 'pymongo not installed')
def test__aggregate_to_long(self):
collection = self.db.collection
collection.insert_one({
'boolean_true': True,
'boolean_false': False,
'integer': 100,
'double': 1.999,
'decimal': decimal128.Decimal128('5.5000')
})
actual = collection.aggregate(
[
{
'$addFields': {
'boolean_true': {'$toLong': '$boolean_true'},
'boolean_false': {'$toLong': '$boolean_false'},
'integer': {'$toLong': '$integer'},
'double': {'$toLong': '$double'},
'decimal': {'$toLong': '$decimal'},
'not_exist': {'$toLong': '$not_exist'},
}
},
{
'$project': {
'_id': 0
}
}
]
)
expect = [{
'boolean_true': 1,
'boolean_false': 0,
'integer': 100,
'double': 1,
'decimal': 5,
'not_exist': None,
}]
self.assertEqual(expect, list(actual))

@skipIf(_HAVE_PYMONGO, 'pymongo installed')
def test__aggregate_to_long_no_pymongo(self):
collection = self.db.collection
collection.drop()
collection.insert_one({
'double': 1.999,
})
with self.assertRaises(NotImplementedError):
list(collection.aggregate(
[
{
'$addFields': {
'double': {'$toLong': '$double'},
}
},
{
'$project': {
'_id': 0
}
}
]
))

@skipIf(not _HAVE_PYMONGO, 'pymongo not installed')
def test__aggregate_date_to_string(self):
collection = self.db.collection
Expand Down
28 changes: 28 additions & 0 deletions tests/test__mongomock.py
Expand Up @@ -3431,6 +3431,34 @@ def test_aggregate_to_int(self):
]
self.cmp.compare.aggregate(pipeline)

def test_aggregate_to_long(self):
self.cmp.do.drop()
self.cmp.do.insert_one({
'boolean_true': True,
'boolean_false': False,
'integer': 100,
'double': 1.999,
'decimal': decimal128.Decimal128('5.5000')
})
pipeline = [
{
'$addFields': {
'boolean_true': {'$toLong': '$boolean_true'},
'boolean_false': {'$toLong': '$boolean_false'},
'integer': {'$toLong': '$integer'},
'double': {'$toLong': '$double'},
'decimal': {'$toLong': '$decimal'},
'not_exist': {'$toLong': '$not_exist'},
}
},
{
'$project': {
'_id': 0
}
}
]
self.cmp.compare.aggregate(pipeline)

def test_aggregate_date_to_string(self):
self.cmp.do.drop()
self.cmp.do.insert_one({
Expand Down

0 comments on commit 26d82f9

Please sign in to comment.