Commits

Eric Larson committed cebe19d

Added support for a conversion function to get the proper type when constructing a query

Comments (0)

Files changed (2)

 
 
 class Expr(object):
-    def __init__(self, op, k, v):
+    def __init__(self, op, k, v, conversion=None):
         self.op = op
         self.k = k
         self.v = v
+        self.conversion = conversion
 
     def as_dict(self):
+        value = self.v
+        if self.conversion:
+            value = self.conversion(self.k, self.v)
+
         if self.op == '$eq':
-            return {self.k: self.v}
-        return {self.k: {self.op: self.v}}
+            return {self.k: value}
+        return {self.k: {self.op: value}}
 
 
 class AndOr(object):
 
 class Parser(object):
 
-    def __init__(self):
+    def __init__(self, conversion=None):
         self._parser = self.parser()
         self._query = {}
+        self.conversion = conversion
 
     def parser(self):
         """
             '>': '$gte', '<': '$lte', ':': '$eq'
         }
         k, op, v = toks
-        return [Expr(ops[op], k, v)]
+        return [Expr(ops[op], k, v, self.conversion)]
 
     def handle_and_or(self, s, loc, toks):
         """

tests/test_parser.py

 converting types. That is the responsibility of the caller to do so or
 extend the Query class to do it.
 """
+import pytest
 
 from mgoquery import Parser, Query
 
                                                      {'x': {'$lte': '5'}}]},
                                            {'$or': [{'y': {'$gte': '10'}},
                                                     {'y': 'None'}]}]}
+
+
+class TestParseAndValidate(object):
+    """
+    The parser can accept a validator callable that can be used to
+    convert any values to the proper type.
+
+    Here is an example using a set list of converter functions ::
+
+      def format_values(k, v):
+          conversion_map = {
+              'start': parse_datetime,
+              'end': parse_datetime,
+              'size': int,
+              'amount': float,
+          }
+          return conversion_map.get(k, lambda x: x)
+
+    As MongoDB doesn't enforce any sort of type constraint on a
+    document, we depend on the key in order to supply the correct
+    conversion method.
+    """
+
+    def to_int(k, v):
+        return int(v)
+
+    p = Parser(conversion=to_int)
+    query_tests = [
+        ('x:1', {'x': 1}),
+        ('x>1', {'x': {'$gte': 1}}),
+        ('x:1|a:2', {'$or': [{'x': 1}, {'a': 2}]}),
+        ('"x:1,y:2"|"foo:5,bar:6"', {'$or': [{'$and': [{'x': 1},
+                                                       {'y': 2}]},
+                                             {'$and': [{'foo': 5},
+                                                       {'bar': 6}]}]})
+    ]
+
+    def get_query(self, query):
+        return Query(self.p.parse(query))
+
+    @pytest.mark.parametrize(('query', 'expected'), query_tests)
+    def test_map(self, query, expected):
+        q = self.get_query(query)
+        assert q.as_dict() == expected
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.