Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ming/mim.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,12 @@ def match(self, key, op, value):
if value.lower() in self[field].lower():
return True
return False
if op == '$type':
wanted = value if isinstance(value, list) else [value]
codes = {_BSON_TYPE_ALIASES.get(t, t) for t in wanted}
if 'number' in wanted:
codes |= {1, 16, 18, 19}
return _bson_type_code(val) in codes
raise NotImplementedError(op)

def _match_regex(self, regex, val):
Expand Down Expand Up @@ -1332,6 +1338,21 @@ def setdefault(self, key, default):
self._orig[key] = default


# mongo server's $type aliases; pymongo only has the numeric codes
_BSON_TYPE_ALIASES = {
'double': 1, 'string': 2, 'object': 3, 'array': 4, 'binData': 5, 'objectId': 7, 'bool': 8, 'date': 9,
'null': 10, 'regex': 11, 'javascript': 13, 'int': 16, 'timestamp': 17, 'long': 18, 'decimal': 19,
'minKey': -1, 'maxKey': 127,
}


def _bson_type_code(val) -> int | None:
if val == ():
return None
code = bson_safe({'': val._orig if isinstance(val, Match) else val})[4] # type byte of the first element
return -1 if code == 0xFF else code # minKey


def _parse_query(v):
if isinstance(v, dict) and all(k.startswith('$') for k in v.keys()):
return v.items()
Expand Down
34 changes: 34 additions & 0 deletions ming/tests/test_mim.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,40 @@ def test_exists(self):
self.assertIsNone(mim.match({'e': { '$exists': 1 } }, doc))
self.assertIsNotNone(mim.match({'e': { '$exists': 0 } }, doc))

def test_type(self):
doc = {'s': 'x', 'i': 1, 'l': 2**40, 'f': 1.5, 'b': True, 'n': None, 'o': {'x': 1}, 'a': ['x', 1],
'id': bson.ObjectId(), 'dt': datetime(2020, 1, 1), 'bin': bson.Binary(b'x'),
'u': uuid.uuid4(), 'js': bson.Code('x'), 'mk': bson.MinKey()}
self.assertIsNotNone(mim.match({'s': {'$type': 'string'}}, doc))
self.assertIsNotNone(mim.match({'s': {'$type': 2}}, doc))
self.assertIsNone(mim.match({'s': {'$type': 'int'}}, doc))
self.assertIsNotNone(mim.match({'i': {'$type': 'int'}}, doc))
self.assertIsNotNone(mim.match({'l': {'$type': 'long'}}, doc))
self.assertIsNotNone(mim.match({'f': {'$type': 'double'}}, doc))
self.assertIsNotNone(mim.match({'b': {'$type': 'bool'}}, doc))
self.assertIsNone(mim.match({'b': {'$type': 'int'}}, doc))
self.assertIsNotNone(mim.match({'n': {'$type': 'null'}}, doc))
self.assertIsNotNone(mim.match({'o': {'$type': 'object'}}, doc))
self.assertIsNotNone(mim.match({'id': {'$type': 'objectId'}}, doc))
self.assertIsNotNone(mim.match({'dt': {'$type': 'date'}}, doc))
self.assertIsNotNone(mim.match({'bin': {'$type': 'binData'}}, doc))
self.assertIsNotNone(mim.match({'u': {'$type': 'binData'}}, doc))
self.assertIsNotNone(mim.match({'js': {'$type': 'javascript'}}, doc))
self.assertIsNone(mim.match({'js': {'$type': 'string'}}, doc))
self.assertIsNotNone(mim.match({'mk': {'$type': 'minKey'}}, doc))
self.assertIsNotNone(mim.match({'mk': {'$type': -1}}, doc))
self.assertIsNone(mim.match({'missing': {'$type': 'null'}}, doc))
for f in ('i', 'l', 'f'):
self.assertIsNotNone(mim.match({f: {'$type': 'number'}}, doc))
self.assertIsNone(mim.match({'b': {'$type': 'number'}}, doc))
self.assertIsNotNone(mim.match({'n': {'$type': ['string', 'null']}}, doc))
self.assertIsNone(mim.match({'i': {'$type': ['string', 'null']}}, doc))
# arrays match on the array itself or any element
self.assertIsNotNone(mim.match({'a': {'$type': 'array'}}, doc))
self.assertIsNotNone(mim.match({'a': {'$type': 'string'}}, doc))
self.assertIsNotNone(mim.match({'a': {'$type': 'int'}}, doc))
self.assertIsNone(mim.match({'a': {'$type': 'double'}}, doc))

def test_all(self):
doc = { 'c': [ 1, 2 ] }
self.assertIsNotNone(mim.match({'c': {'$all': [] } }, doc))
Expand Down
Loading