Repository: blaze/blaze

Issue:
Mongo selections on the same field overlap poorly 
Mongo aggregation pipeline spells selection queries with a `$match` expression that takes a dict of field: condition pairs.  If we have two conditions on the same field then one overwites the other in the dict.
## Failing Example

``` python
In [1]: from blaze import *
Couldn't import dot_parser, loading of dot files will not be possible.

In [2]: odo('/home/mrocklin/data/iris.csv', 'mongodb://localhost/db::iris')
Out[2]: Collection(Database(MongoClient('localhost', 27017), u'db'), u'iris')

In [3]: d = Data(_)

In [4]: d
Out[4]: 
    petal_length  petal_width  sepal_length  sepal_width      species
0            1.4          0.2           5.1          3.5  Iris-setosa
1            1.4          0.2           4.9          3.0  Iris-setosa
2            1.3          0.2           4.7          3.2  Iris-setosa
3            1.5          0.2           4.6          3.1  Iris-setosa
4            1.4          0.2           5.0          3.6  Iris-setosa
5            1.7          0.4           5.4          3.9  Iris-setosa
6            1.4          0.3           4.6          3.4  Iris-setosa
7            1.5          0.2           5.0          3.4  Iris-setosa
8            1.4          0.2           4.4          2.9  Iris-setosa
9            1.5          0.1           4.9          3.1  Iris-setosa
...

In [5]: compute(d[(d.petal_length > 1) & (d.petal_length < 2)], post_compute=False).query
Out[5]: ({'$match': {u'petal_length': {'$lt': 2}}},)
```

This could be resolved by stacking multiple match expressions when we encounter an overlap

`````` python
({'$match': {u'petal_length': {'$lt': 2}}},
 ('$match': (u'petal_length': {'$gt': 1}}
```)


cc @PeterDSteinberg 
``````



Discussion:

Resolving patch:
diff --git a/blaze/compute/mongo.py b/blaze/compute/mongo.py
--- a/blaze/compute/mongo.py
+++ b/blaze/compute/mongo.py
@@ -191,10 +191,7 @@ def compute_sub(t):
                           x]}
     elif isinstance(t, ceil):
         x = compute_sub(t._child)
-        return {'$add': [x,
-                         {'$subtract': [1,
-                                        {'$mod': [x, 1]}]}
-                          ]}
+        return {'$add': [x, {'$subtract': [1, {'$mod': [x, 1]}]}]}
     elif isinstance(t, tuple(datetime_terms)):
         op = datetime_terms[type(t)]
         return {'$%s' % op: compute_sub(t._child)}
@@ -465,10 +462,10 @@ def match(expr):
     if isinstance(expr, Ge):
         return {name(expr.lhs): {'$gte': expr.rhs}}
     if isinstance(expr, And):
-        return toolz.merge(match(expr.lhs), match(expr.rhs))
+        return {'$and': [match(expr.lhs), match(expr.rhs)]}
     if isinstance(expr, Or):
         return {'$or': [match(expr.lhs), match(expr.rhs)]}
     if isinstance(expr, Ne):
         return {name(expr.lhs): {'$ne': expr.rhs}}
-    raise NotImplementedError("Matching not supported on expressions of type %s"
-            % type(expr).__name__)
+    raise NotImplementedError("Matching not supported on expressions of type "
+                              "%r" % type(expr).__name__)


Test patch:
diff --git a/blaze/compute/tests/test_mongo_compute.py b/blaze/compute/tests/test_mongo_compute.py
--- a/blaze/compute/tests/test_mongo_compute.py
+++ b/blaze/compute/tests/test_mongo_compute.py
@@ -361,3 +361,10 @@ def test_Data_construct_with_table(bank):
     d = Data('mongodb://localhost/test_db::bank')
     assert set(d.fields) == set(('name', 'amount'))
     assert int(d.count()) == 5
+
+
+def test_and_same_key(bank):
+    expr = t[(t.amount > 100) & (t.amount < 300)]
+    result = compute(expr, bank)
+    expected = [('Alice', 200), ('Bob', 200)]
+    assert result == expected