Repository: altair-viz/altair

Issue:
SelectionNot is not appropriately serialized
See https://github.com/altair-viz/altair/issues/695#issuecomment-411506890 and https://github.com/altair-viz/altair/issues/695#issuecomment-411536841

Discussion:

Patch:
diff --git a/CHANGES.md b/CHANGES.md
index dc755b43..8f2a9392 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -18,6 +18,11 @@
 
 - ``alt.SortField`` renamed to ``alt.EncodingSortField`` and ``alt.WindowSortField`` renamed to ``alt.SortField`` (https://github.com/vega/vega-lite/pull/3741)
 
+### Bug Fixes
+
+- Fixed serialization of logical operands on selections within
+  ``transform_filter()``: (#1075)
+
 ## Version 2.1.0 (Released June 6, 2018):
 
 ### Enhancements
diff --git a/altair/vegalite/v2/api.py b/altair/vegalite/v2/api.py
index 2d46d218..ac413390 100644
--- a/altair/vegalite/v2/api.py
+++ b/altair/vegalite/v2/api.py
@@ -830,8 +830,12 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
         alt.FilterTransform : underlying transform object
 
         """
+        selection_predicates = (core.SelectionNot, core.SelectionOr,
+                                core.SelectionAnd, core.SelectionOperand)
         if isinstance(filter, NamedSelection):
-            filter = filter.ref()
+            filter = {'selection': filter._get_name()}
+        elif isinstance(filter, selection_predicates):
+            filter = {'selection': filter}
         return self._add_transform(core.FilterTransform(filter=filter, **kwargs))
 
     def transform_lookup(self, as_=Undefined, from_=Undefined, lookup=Undefined, default=Undefined, **kwargs):


Test patch:
diff --git a/altair/vegalite/v2/tests/test_api.py b/altair/vegalite/v2/tests/test_api.py
index b59e1062..aeba0701 100644
--- a/altair/vegalite/v2/tests/test_api.py
+++ b/altair/vegalite/v2/tests/test_api.py
@@ -336,6 +336,24 @@ def test_transforms():
                                                        window=window[::-1])])
 
 
+def test_filter_transform_selection_predicates():
+    selector1 = alt.selection_interval(name='s1')
+    selector2 = alt.selection_interval(name='s2')
+    base = alt.Chart('data.txt').mark_point()
+
+    chart = base.transform_filter(selector1)
+    assert chart.to_dict()['transform'] == [{'filter': {'selection': 's1'}}]
+
+    chart = base.transform_filter(~selector1)
+    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'not': 's1'}}}]
+
+    chart = base.transform_filter(selector1 & selector2)
+    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'and': ['s1', 's2']}}}]
+
+    chart = base.transform_filter(selector1 | selector2)
+    assert chart.to_dict()['transform'] == [{'filter': {'selection': {'or': ['s1', 's2']}}}]
+
+
 
 def test_resolve_methods():
     chart = alt.LayerChart().resolve_axis(x='shared', y='independent')