Repository: conan-io/conan

Issue:
conan search - versions not ordered in some cases
To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.

```
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.6 LTS
Release:	16.04
Codename:	xenial
```
`Conan version 1.18.2`

It appears that when running a `conan search`, versions are mostly ordered until one of the fields gets into multiple digits.

```
PackageA/2.2.48-6+g56420c1@User/PR-149
PackageA/2.2.48-7+g2f4c6a4@User/PR-148
PackageA/2.2.48-9+g17d14dc@User/PR-80
PackageA/2.2.48-9+g421d64d@User/PR-80
PackageA/2.2.49-1+g0be7926@User/PR-153
PackageA/2.2.5+g25a37a8@User/conan_integration
PackageA/2.2.5+g25a37a8@User/master
PackageA/2.2.6+gdccf284@User/master
PackageA/2.2.7+gb63fd8c@User/master
PackageA/2.2.8+ga5959f2@User/master
```
Here you can see that the latest version is not at the bottom, it appears it is ordered by the first digit. `2.2.49` > `2.2.8`, but `2.2.8` appears later in the list.
If everything is single digit, it works fine.

Full example
```
PackageA/2.2.1-5+g2774c5c@User/conan_integration
PackageA/2.2.1-5+g2774c5c@User/conan_stable
PackageA/2.2.10+g69bf553@User/conan_integration
PackageA/2.2.10+g69bf553@User/conan_stable
PackageA/2.2.10+g69bf553@User/master
PackageA/2.2.11+g46eb7e0@User/conan_integration
PackageA/2.2.11+g46eb7e0@User/conan_stable
PackageA/2.2.11+g46eb7e0@User/master
PackageA/2.2.11-1+ga5d6290@User/stable
PackageA/2.2.11-10+ge5d08e5@User/stable
PackageA/2.2.11-11+g029ff2b@User/stable
PackageA/2.2.11-12+g695b7ea@User/stable
PackageA/2.2.11-2+gf382e9b@User/stable
PackageA/2.2.11-3+gcd516d2@User/stable
PackageA/2.2.11-4+gfe2f2d2@User/stable
PackageA/2.2.11-5+gd65b144@User/stable
PackageA/2.2.11-6+gaaea265@User/stable
PackageA/2.2.11-7+gacbf11b@User/stable
PackageA/2.2.11-8+g43737bb@User/stable
PackageA/2.2.11-9+g3120967@User/stable
PackageA/2.2.12+g0dbc89f@User/conan_integration
PackageA/2.2.12+g0dbc89f@User/conan_stable
PackageA/2.2.12+g0dbc89f@User/master
PackageA/2.2.13+g7b956cf@User/conan_integration
PackageA/2.2.13+g7b956cf@User/conan_stable
PackageA/2.2.13+g7b956cf@User/master
PackageA/2.2.14+g73ec065@User/master
PackageA/2.2.15+g566bd29@User/conan_integration
PackageA/2.2.15+g566bd29@User/conan_stable
PackageA/2.2.15+g566bd29@User/master
PackageA/2.2.16+g7ba3fc9@User/master
PackageA/2.2.17+g242fe53@User/conan_integration
PackageA/2.2.17+g242fe53@User/master
PackageA/2.2.18+g5d61e8c@User/conan_integration
PackageA/2.2.18+g5d61e8c@User/conan_stable
PackageA/2.2.18+g5d61e8c@User/master
PackageA/2.2.19+g6a69ba4@User/conan_integration
PackageA/2.2.19+g6a69ba4@User/conan_stable
PackageA/2.2.19+g6a69ba4@User/master
PackageA/2.2.2+gb531446@User/master
PackageA/2.2.2-105+gbfabeb6@User/stable
PackageA/2.2.20+gd0c762b@User/conan_integration
PackageA/2.2.20+gd0c762b@User/conan_stable
PackageA/2.2.20+gd0c762b@User/master
PackageA/2.2.21+ge8b23d2@User/master
PackageA/2.2.21-17+g8e1f729@User/stable
PackageA/2.2.22+gfbd9bab@User/conan_integration
PackageA/2.2.22+gfbd9bab@User/conan_stable
PackageA/2.2.22+gfbd9bab@User/master
PackageA/2.2.22-18+g9d6e238@User/stable
PackageA/2.2.22-19+g0bbbb7c@User/stable
PackageA/2.2.23+ge672183@User/conan_integration
PackageA/2.2.23+ge672183@User/conan_stable
PackageA/2.2.23+ge672183@User/master
PackageA/2.2.24+g9963777@User/conan_integration
PackageA/2.2.24+g9963777@User/conan_stable
PackageA/2.2.24+g9963777@User/master
PackageA/2.2.24+gbe5b554@User/master
PackageA/2.2.24-22+g8f6c15e@User/stable
PackageA/2.2.24-23+gd17bf65@User/stable
PackageA/2.2.24-24+gdf56507@User/stable
PackageA/2.2.24-25+g73fcc0e@User/stable
PackageA/2.2.24-26+ga3d966b@User/stable
PackageA/2.2.25+g3eab7c1@User/master
PackageA/2.2.26+g8bcadcc@User/conan_integration
PackageA/2.2.26+g8bcadcc@User/conan_stable
PackageA/2.2.26+g8bcadcc@User/master
PackageA/2.2.27+ga76b4ca@User/conan_integration
PackageA/2.2.27+ga76b4ca@User/conan_stable
PackageA/2.2.27+ga76b4ca@User/master
PackageA/2.2.28+g0683681@User/conan_integration
PackageA/2.2.28+g0683681@User/master
PackageA/2.2.28-26+g47d0630@User/stable
PackageA/2.2.28-27+g728ac93@User/stable
PackageA/2.2.28-29+g54bc980@User/stable
PackageA/2.2.28-30+g86bc40b@User/stable
PackageA/2.2.28-31+g7e34596@User/stable
PackageA/2.2.29+ga7e12b1@User/conan_integration
PackageA/2.2.29+ga7e12b1@User/master
PackageA/2.2.3+gf6b057c@User/master
PackageA/2.2.30+g405bcc1@User/conan_integration
PackageA/2.2.30+g405bcc1@User/conan_stable
PackageA/2.2.30+g405bcc1@User/master
PackageA/2.2.31+g0d92e76@User/conan_integration
PackageA/2.2.31+g0d92e76@User/conan_stable
PackageA/2.2.31+g0d92e76@User/master
PackageA/2.2.32+gbd4993e@User/conan_integration
PackageA/2.2.32+gbd4993e@User/conan_stable
PackageA/2.2.32+gbd4993e@User/master
PackageA/2.2.33+g4f7c87e@User/conan_integration
PackageA/2.2.33+g4f7c87e@User/conan_stable
PackageA/2.2.33+g4f7c87e@User/master
PackageA/2.2.34+gf5d327e@User/conan_integration
PackageA/2.2.34+gf5d327e@User/conan_stable
PackageA/2.2.34+gf5d327e@User/master
PackageA/2.2.35+g4399a1e@User/conan_integration
PackageA/2.2.35+g4399a1e@User/conan_stable
PackageA/2.2.35+g4399a1e@User/master
PackageA/2.2.36+g9e24a0f@User/conan_integration
PackageA/2.2.36+g9e24a0f@User/conan_stable
PackageA/2.2.36+g9e24a0f@User/master
PackageA/2.2.37+gc2b3494@User/conan_integration
PackageA/2.2.37+gc2b3494@User/conan_stable
PackageA/2.2.37+gc2b3494@User/master
PackageA/2.2.38+g46f7ba6@User/master
PackageA/2.2.38-32+g8bbfa8c@User/stable
PackageA/2.2.38-33+gc8dcd2f@User/stable
PackageA/2.2.38-34+g31e5311@User/stable
PackageA/2.2.38-35+g52c95b9@User/stable
PackageA/2.2.38-36+g4f92042@User/stable
PackageA/2.2.38-37+g725ed82@User/stable
PackageA/2.2.38-38+g1e2770f@User/stable
PackageA/2.2.38-39+g6ba6dd5@User/stable
PackageA/2.2.38-40+g078ac63@User/stable
PackageA/2.2.39+g4f64294@User/conan_integration
PackageA/2.2.39+g4f64294@User/conan_stable
PackageA/2.2.39+g4f64294@User/master
PackageA/2.2.4+g1fe4bc6@User/master
PackageA/2.2.40+gecded4b@User/conan_integration
PackageA/2.2.40+gecded4b@User/conan_stable
PackageA/2.2.40+gecded4b@User/master
PackageA/2.2.41+g9264e1f@User/conan_integration
PackageA/2.2.41+g9264e1f@User/master
PackageA/2.2.42+gef92ba3@User/conan_integration
PackageA/2.2.42+gef92ba3@User/conan_stable
PackageA/2.2.42+gef92ba3@User/master
PackageA/2.2.43+gc5e3804@User/conan_integration
PackageA/2.2.43+gc5e3804@User/conan_stable
PackageA/2.2.43+gc5e3804@User/master
PackageA/2.2.44+g6071422@User/conan_integration
PackageA/2.2.44+g6071422@User/conan_stable
PackageA/2.2.44+g6071422@User/master
PackageA/2.2.45+g26ef833@User/master
PackageA/2.2.46+ge04c760@User/master
PackageA/2.2.47+g8694ab0@User/conan_integration
PackageA/2.2.47+g8694ab0@User/conan_stable
PackageA/2.2.47+g8694ab0@User/master
PackageA/2.2.48+g315af12@User/conan_integration
PackageA/2.2.48+g315af12@User/conan_stable
PackageA/2.2.48+g315af12@User/master
PackageA/2.2.48-1+gd6d4bd4@User/stable
PackageA/2.2.48-2+g1d59ed5@User/PR-149
PackageA/2.2.48-2+g9164a28@User/stable
PackageA/2.2.48-3+g8ec3d18@User/stable
PackageA/2.2.48-3+g9470ded@User/PR-150
PackageA/2.2.48-4+g49fac00@User/PR-152
PackageA/2.2.48-4+gf415bda@User/stable
PackageA/2.2.48-4+gf777315@User/PR-151
PackageA/2.2.48-5+g034d84a@User/stable
PackageA/2.2.48-5+g1995894@User/PR-150
PackageA/2.2.48-5+g4afc117@User/PR-152
PackageA/2.2.48-6+g56420c1@User/PR-149
PackageA/2.2.48-7+g2f4c6a4@User/PR-148
PackageA/2.2.48-9+g17d14dc@User/PR-80
PackageA/2.2.48-9+g421d64d@User/PR-80
PackageA/2.2.49-1+g0be7926@User/PR-153
PackageA/2.2.5+g25a37a8@User/conan_integration
PackageA/2.2.5+g25a37a8@User/master
PackageA/2.2.6+gdccf284@User/master
PackageA/2.2.7+gb63fd8c@User/master
PackageA/2.2.8+ga5959f2@User/master
PackageA/2.2.9+gbb19f00@User/master
``` 



Discussion:
I got around this by piping the conan search through `sort -V`
`conan search blah | sort -V`, but it would be nice if it was already sorted, including the json ouput.
Yeah, it sorted alphabetically. 🙂 

Resolving patch:
diff --git a/conans/client/cmd/search.py b/conans/client/cmd/search.py
--- a/conans/client/cmd/search.py
+++ b/conans/client/cmd/search.py
@@ -26,12 +26,12 @@ def search_recipes(self, pattern, remote_name=None, case_sensitive=False):
                 for remote in self._remotes.values():
                     refs = self._remote_manager.search_recipes(remote, pattern, ignorecase)
                     if refs:
-                        references[remote.name] = refs
+                        references[remote.name] = sorted(refs)
                 return references
         # single remote
         remote = self._remotes[remote_name]
         refs = self._remote_manager.search_recipes(remote, pattern, ignorecase)
-        references[remote.name] = refs
+        references[remote.name] = sorted(refs)
         return references
 
     remote_ref = namedtuple('remote_ref', 'ordered_packages recipe_hash')
diff --git a/conans/search/search.py b/conans/search/search.py
--- a/conans/search/search.py
+++ b/conans/search/search.py
@@ -49,14 +49,14 @@ def filter_packages(query, package_infos):
         postfix = infix_to_postfix(query) if query else []
         result = OrderedDict()
         for package_id, info in package_infos.items():
-            if evaluate_postfix_with_info(postfix, info):
+            if _evaluate_postfix_with_info(postfix, info):
                 result[package_id] = info
         return result
     except Exception as exc:
         raise ConanException("Invalid package query: %s. %s" % (query, exc))
 
 
-def evaluate_postfix_with_info(postfix, conan_vars_info):
+def _evaluate_postfix_with_info(postfix, conan_vars_info):
 
     # Evaluate conaninfo with the expression
 
@@ -65,12 +65,12 @@ def evaluate_info(expression):
         Uses conan_vars_info in the closure to evaluate it"""
         name, value = expression.split("=", 1)
         value = value.replace("\"", "")
-        return evaluate(name, value, conan_vars_info)
+        return _evaluate(name, value, conan_vars_info)
 
     return evaluate_postfix(postfix, evaluate_info)
 
 
-def evaluate(prop_name, prop_value, conan_vars_info):
+def _evaluate(prop_name, prop_value, conan_vars_info):
     """
     Evaluates a single prop_name, prop_value like "os", "Windows" against
     conan_vars_info.serialize_min()
diff --git a/conans/server/service/common/search.py b/conans/server/service/common/search.py
--- a/conans/server/service/common/search.py
+++ b/conans/server/service/common/search.py
@@ -100,7 +100,8 @@ def get_ref(_pattern):
 
         subdirs = list_folder_subdirs(basedir=self._server_store.store, level=5)
         if not pattern:
-            return sorted([ConanFileReference(*folder.split("/")) for folder in subdirs])
+            return sorted([ConanFileReference(*folder.split("/")).copy_clear_rev()
+                           for folder in subdirs])
         else:
             ret = set()
             for subdir in subdirs:


Test patch:
diff --git a/conans/test/functional/command/download_test.py b/conans/test/functional/command/download_test.py
--- a/conans/test/functional/command/download_test.py
+++ b/conans/test/functional/command/download_test.py
@@ -10,10 +10,7 @@
 class DownloadTest(unittest.TestCase):
 
     def download_recipe_test(self):
-        server = TestServer()
-        servers = {"default": server}
-        client = TurboTestClient(servers=servers, users={"default": [("lasote", "mypass")]})
-
+        client = TurboTestClient(default_server_user={"lasote": "pass"})
         # Test download of the recipe only
         conanfile = """from conans import ConanFile
 class Pkg(ConanFile):
diff --git a/conans/test/functional/command/search_test.py b/conans/test/functional/command/search_test.py
--- a/conans/test/functional/command/search_test.py
+++ b/conans/test/functional/command/search_test.py
@@ -14,7 +14,8 @@
 from conans.model.ref import ConanFileReference, PackageReference
 from conans.paths import CONANINFO, EXPORT_FOLDER, PACKAGES_FOLDER
 from conans.server.revision_list import RevisionList
-from conans.test.utils.tools import TestClient, TestServer, NO_SETTINGS_PACKAGE_ID, TurboTestClient
+from conans.test.utils.tools import TestClient, TestServer, NO_SETTINGS_PACKAGE_ID, TurboTestClient, \
+    GenConanfile
 from conans.util.dates import iso8601_to_str, from_timestamp_to_iso8601
 from conans.util.env_reader import get_env
 from conans.util.files import list_folder_subdirs, load
@@ -1137,8 +1138,6 @@ def search_json_test(self):
         }
         self.assertEqual(expected_output, output)
 
-
-
     def search_packages_with_reference_not_exported_test(self):
         self.client.run("search my_pkg/1.0@conan/stable", assert_error=True)
         self.assertIn("ERROR: Recipe not found: 'my_pkg/1.0@conan/stable'", self.client.out)
@@ -1192,6 +1191,34 @@ def search_remote_test(self):
         self.assertIn("Package_ID: {}".format(NO_SETTINGS_PACKAGE_ID), client.out)
 
 
+class SearchOrder(unittest.TestCase):
+    def search_test(self):
+        client = TestClient(default_server_user=True)
+        client.save({"conanfile.py": GenConanfile()})
+        client.run("create . pkg/1.1@user/testing")
+        client.run("create . pkg/1.2@user/testing")
+        client.run("create . pkg/1.11@user/testing")
+        client.run("create . pkg/1.21@user/testing")
+        client.run("create . pkg/1.21.3@user/testing")
+        client.run("create . pkg/1.21.34@user/testing")
+        client.run("create . pkg/1.21.4@user/testing")
+        client.run("search")
+        output = textwrap.dedent("""
+            pkg/1.1@user/testing
+            pkg/1.2@user/testing
+            pkg/1.11@user/testing
+            pkg/1.21@user/testing
+            pkg/1.21.3@user/testing
+            pkg/1.21.4@user/testing
+            pkg/1.21.34@user/testing""")
+        self.assertIn(output, client.out)
+
+        client.run("upload * -r=default -c --all")
+        # This is by pattern
+        client.run("search -r=default")
+        self.assertIn(output, client.out)
+
+
 @unittest.skipIf(get_env("TESTING_REVISIONS_ENABLED", False), "No sense with revs")
 class SearchOutdatedTest(unittest.TestCase):
     def search_outdated_test(self):
diff --git a/conans/test/unittests/server/service/service_test.py b/conans/test/unittests/server/service/service_test.py
--- a/conans/test/unittests/server/service/service_test.py
+++ b/conans/test/unittests/server/service/service_test.py
@@ -218,7 +218,7 @@ def test_search(self):
         save_files(self.server_store.export(ref4), {"dummy.txt": "//"})
 
         info = self.search_service.search()
-        expected = [ref3, ref4, self.ref, ref2]
+        expected = [r.copy_clear_rev() for r in [ref3, ref4, self.ref, ref2]]
         self.assertEqual(expected, info)
 
         info = self.search_service.search(pattern="Assimp*", ignorecase=False)
diff --git a/conans/test/utils/tools.py b/conans/test/utils/tools.py
--- a/conans/test/utils/tools.py
+++ b/conans/test/utils/tools.py
@@ -642,13 +642,26 @@ class TestClient(object):
 
     def __init__(self, cache_folder=None, current_folder=None, servers=None, users=None,
                  requester_class=None, runner=None, path_with_spaces=True,
-                 revisions_enabled=None, cpu_count=1):
+                 revisions_enabled=None, cpu_count=1, default_server_user=None):
         """
         current_folder: Current execution folder
         servers: dict of {remote_name: TestServer}
         logins is a list of (user, password) for auto input in order
         if required==> [("lasote", "mypass"), ("other", "otherpass")]
         """
+        if default_server_user is not None:
+            if servers is not None:
+                raise Exception("Cannot define both 'servers' and 'default_server_user'")
+            if users is not None:
+                raise Exception("Cannot define both 'users' and 'default_server_user'")
+            if default_server_user is True:
+                server_users = {"user": "password"}
+                users = {"default": [("user", "password")]}
+            else:
+                server_users = default_server_user
+                users = {"default": list(default_server_user.items())}
+            server = TestServer(users=server_users)
+            servers = {"default": server}
 
         self.users = users
         if self.users is None:
@@ -865,7 +878,7 @@ class TurboTestClient(TestClient):
     tmp_json_name = ".tmp_json"
 
     def __init__(self, *args, **kwargs):
-        if "users" not in kwargs:
+        if "users" not in kwargs and "default_server_user" not in kwargs:
             from collections import defaultdict
             kwargs["users"] = defaultdict(lambda: [("conan", "password")])