Repository: conan-io/conan

Issue:
conan remove --query removes binaries without exact match
```python
    def remove_query_test(self):
        test_server = TestServer(users={"lasote": "password"})  # exported users and passwords
        servers = {"default": test_server}
        client = TestClient(servers=servers, users={"default": [("lasote", "password")]})
        conanfile = """from conans import ConanFile
class Test(ConanFile):
    settings = "os"
    """
        client.save({"conanfile.py": conanfile})
        client.run("create . Test/0.1@lasote/testing -s os=Windows")
        client.run("create . Test/0.1@lasote/testing -s os=Linux")
        client.save({"conanfile.py": conanfile.replace("settings", "pass #")})
        client.run("create . Test2/0.1@lasote/testing")
        client.run("upload * --all --confirm")
        client.run("remove Test/0.1@lasote/testing -q=os=Windows -f -r=default")
        client.run("search Test/0.1@lasote/testing -r=default")
        self.assertNotIn("os: Windows", client.out)
        self.assertIn("os: Linux", client.out)
        # THIS REMOVAL IS BUGGY
        client.run("remove Test2/0.1@lasote/testing -q=os=Windows -f -r=default")
        client.run("search Test2/0.1@lasote/testing -r=default")
        self.assertIn("Package_ID: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.out)
```




Discussion:

Resolving patch:
diff --git a/conans/client/remote_manager.py b/conans/client/remote_manager.py
--- a/conans/client/remote_manager.py
+++ b/conans/client/remote_manager.py
@@ -23,6 +23,7 @@
                                 log_package_download)
 from conans.client.source import merge_directories
 from conans.util.env_reader import get_env
+from conans.search.search import filter_packages
 
 
 class RemoteManager(object):
@@ -254,7 +255,9 @@ def search_recipes(self, remote, pattern=None, ignorecase=True):
         return self._call_remote(remote, "search", pattern, ignorecase)
 
     def search_packages(self, remote, reference, query):
-        return self._call_remote(remote, "search_packages", reference, query)
+        packages = self._call_remote(remote, "search_packages", reference, query)
+        packages = filter_packages(query, packages)
+        return packages
 
     def remove(self, conan_ref, remote):
         """
diff --git a/conans/search/search.py b/conans/search/search.py
--- a/conans/search/search.py
+++ b/conans/search/search.py
@@ -61,7 +61,7 @@ def evaluate(prop_name, prop_value, conan_vars_info):
     """
 
     def compatible_prop(setting_value, prop_value):
-        return setting_value is None or prop_value == setting_value
+        return (prop_value == setting_value) or (prop_value == "None" and setting_value is None)
 
     info_settings = conan_vars_info.get("settings", [])
     info_options = conan_vars_info.get("options", [])


Test patch:
diff --git a/conans/test/command/remove_test.py b/conans/test/command/remove_test.py
--- a/conans/test/command/remove_test.py
+++ b/conans/test/command/remove_test.py
@@ -15,6 +15,35 @@
 
 
 class RemoveOutdatedTest(unittest.TestCase):
+
+    def remove_query_test(self):
+        test_server = TestServer(users={"lasote": "password"})  # exported users and passwords
+        servers = {"default": test_server}
+        client = TestClient(servers=servers, users={"default": [("lasote", "password")]})
+        conanfile = """from conans import ConanFile
+class Test(ConanFile):
+    settings = "os"
+    """
+        client.save({"conanfile.py": conanfile})
+        client.run("create . Test/0.1@lasote/testing -s os=Windows")
+        client.run("create . Test/0.1@lasote/testing -s os=Linux")
+        client.save({"conanfile.py": conanfile.replace("settings", "pass #")})
+        client.run("create . Test2/0.1@lasote/testing")
+        client.run("upload * --all --confirm")
+        for remote in ("", "-r=default"):
+            client.run("remove Test/0.1@lasote/testing -q=os=Windows -f %s" % remote)
+            client.run("search Test/0.1@lasote/testing %s" % remote)
+            self.assertNotIn("os: Windows", client.out)
+            self.assertIn("os: Linux", client.out)
+
+            client.run("remove Test2/0.1@lasote/testing -q=os=Windows -f %s" % remote)
+            client.run("search Test2/0.1@lasote/testing %s" % remote)
+            self.assertIn("Package_ID: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.out)
+            client.run("remove Test2/0.1@lasote/testing -q=os=None -f %s" % remote)
+            client.run("search Test2/0.1@lasote/testing %s" % remote)
+            self.assertNotIn("Package_ID: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9", client.out)
+            self.assertIn("There are no packages", client.out)
+
     def remove_outdated_test(self):
         test_server = TestServer(users={"lasote": "password"})  # exported users and passwords
         servers = {"default": test_server}
diff --git a/conans/test/command/search_test.py b/conans/test/command/search_test.py
--- a/conans/test/command/search_test.py
+++ b/conans/test/command/search_test.py
@@ -387,8 +387,7 @@ def test_cases(remote=None):
 
             q = 'compiler="gcc" OR compiler.libcxx=libstdc++11'
             # Should find Visual because of the OR, visual doesn't care about libcxx
-            self._assert_pkg_q(q, ["LinuxPackageSHA", "PlatformIndependantSHA",
-                                   "WindowsPackageSHA"], remote)
+            self._assert_pkg_q(q, ["LinuxPackageSHA", "PlatformIndependantSHA"], remote)
 
             q = '(compiler="gcc" AND compiler.libcxx=libstdc++11) OR compiler.version=4.5'
             self._assert_pkg_q(q, ["LinuxPackageSHA"], remote)
@@ -407,18 +406,25 @@ def test_cases(remote=None):
             self._assert_pkg_q(q, ["PlatformIndependantSHA", "WindowsPackageSHA"], remote)
 
             q = '(os="Linux" OR os=Windows)'
-            self._assert_pkg_q(q, ["PlatformIndependantSHA", "LinuxPackageSHA",
-                                   "WindowsPackageSHA"], remote)
+            self._assert_pkg_q(q, ["LinuxPackageSHA", "WindowsPackageSHA"], remote)
+
+            q = '(os="Linux" OR os=None)'
+            self._assert_pkg_q(q, ["LinuxPackageSHA", "PlatformIndependantSHA"], remote)
+
+            q = '(os=None)'
+            self._assert_pkg_q(q, ["PlatformIndependantSHA"], remote)
 
             q = '(os="Linux" OR os=Windows) AND use_Qt=True'
+            self._assert_pkg_q(q, ["WindowsPackageSHA"], remote)
+
+            q = '(os=None OR os=Windows) AND use_Qt=True'
             self._assert_pkg_q(q, ["PlatformIndependantSHA", "WindowsPackageSHA"], remote)
 
             q = '(os="Linux" OR os=Windows) AND use_Qt=True AND nonexistant_option=3'
-            self._assert_pkg_q(q, ["PlatformIndependantSHA", "WindowsPackageSHA"], remote)
+            self._assert_pkg_q(q, [], remote)
 
             q = '(os="Linux" OR os=Windows) AND use_Qt=True OR nonexistant_option=3'
-            self._assert_pkg_q(q, ["PlatformIndependantSHA",
-                                   "WindowsPackageSHA", "LinuxPackageSHA"], remote)
+            self._assert_pkg_q(q, ["WindowsPackageSHA", "LinuxPackageSHA"], remote)
 
         # test in local
         test_cases()
@@ -474,9 +480,19 @@ def package_search_properties_filter_test(self):
 
         self.client.run('search Hello/1.4.10/fenix/testing -q os=Windows')
         self.assertIn("WindowsPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertNotIn("LinuxPackageSHA", self.client.out)
+
+        self.client.run('search Hello/1.4.10/fenix/testing -q "os=Windows or os=None"')
+        self.assertIn("WindowsPackageSHA", self.client.out)
         self.assertIn("PlatformIndependantSHA", self.client.out)
         self.assertNotIn("LinuxPackageSHA", self.client.out)
 
+        self.client.run('search Hello/1.4.10/fenix/testing -q "os=Windows or os=Linux"')
+        self.assertIn("WindowsPackageSHA", self.client.out)
+        self.assertNotIn("PlatformIndependantSHA", self.client.out)
+        self.assertIn("LinuxPackageSHA", self.client.out)
+
         self.client.run('search Hello/1.4.10/fenix/testing -q "os=Windows AND compiler.version=4.5"')
         self.assertIn("There are no packages for reference 'Hello/1.4.10@fenix/testing' "
                       "matching the query 'os=Windows AND compiler.version=4.5'", self.client.out)