This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch nouveau-6075-sort-by-relevance
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit bb44851b6da0d664568f151e4fad1a7fb58a723c
Author: Robert Newson <[email protected]>
AuthorDate: Tue Jul 28 13:30:18 2026 +0100

    Fix explicit sorting by relevance/score
    
    Also fixed some misleading docs (from the original import from previous 
search docs)
    
    Closes https://github.com/apache/couchdb/issues/6075
---
 .../apache/couchdb/nouveau/lucene/LuceneIndex.java |  6 +++-
 src/docs/src/api/ddoc/nouveau.rst                  |  2 +-
 test/elixir/test/config/nouveau.elixir             |  4 ++-
 test/elixir/test/nouveau_test.exs                  | 34 +++++++++++++++++++++-
 4 files changed, 42 insertions(+), 4 deletions(-)

diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
index 14cc672e9..1384e75e7 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
@@ -102,6 +102,7 @@ public class LuceneIndex extends Index {
     private static final Sort DEFAULT_SORT =
             new Sort(SortField.FIELD_SCORE, new SortField("_id", 
SortField.Type.STRING));
     private static final Pattern SORT_FIELD_RE = 
Pattern.compile("^([-+])?([\\.\\w]+)$");
+    private static final SortField FIELD_REVERSE_SCORE = new SortField(null, 
SortField.Type.SCORE, true);
 
     private final Analyzer analyzer;
     private final IndexWriter writer;
@@ -380,9 +381,12 @@ public class LuceneIndex extends Index {
     }
 
     private SortField convertSortField(final String sortString) {
-        if ("relevance".equals(sortString)) {
+        if ("-<score>".equals(sortString)) {
             return SortField.FIELD_SCORE;
         }
+        if ("<score>".equals(sortString)) {
+            return FIELD_REVERSE_SCORE;
+        }
         final Matcher m = SORT_FIELD_RE.matcher(sortString);
         if (!m.matches()) {
             throw new WebApplicationException(sortString + " is not a valid 
sort parameter", Status.BAD_REQUEST);
diff --git a/src/docs/src/api/ddoc/nouveau.rst 
b/src/docs/src/api/ddoc/nouveau.rst
index b6c262c6d..fa9935e97 100644
--- a/src/docs/src/api/ddoc/nouveau.rst
+++ b/src/docs/src/api/ddoc/nouveau.rst
@@ -65,7 +65,7 @@
         fieldname is the name of a string or double field. You can use a 
single string
         to sort by one field or an array of strings to sort by several fields 
in the
         same order as the array.
-        Some examples are ``"relevance"``, ``"bar"``, ``"-foo"`` and
+        Some examples are ``"-<score>"``, ``"bar"``, ``"-foo"`` and
         [``"-foo"``, ``"bar"``].
     :query number top_n: Limit the number of facets returned by group, 
defaulting to 10
         with a maximum of 1000.
diff --git a/test/elixir/test/config/nouveau.elixir 
b/test/elixir/test/config/nouveau.elixir
index 0066ce372..7fd6ff195 100644
--- a/test/elixir/test/config/nouveau.elixir
+++ b/test/elixir/test/config/nouveau.elixir
@@ -6,7 +6,9 @@
     "search returns all items for GET",
     "search returns all items for POST",
     "search returns all items (paginated)",
-    "search returns all matches for hello by relevance",
+    "search returns all matches for hello by relevance (implicit)",
+    "search returns all matches for hello by relevance (explicit)",
+    "search returns all matches for hello by reverse relevance (explicit)",
     "search for foo:bar",
     "search for numeric ranges with locales",
     "multiple values for stored field",
diff --git a/test/elixir/test/nouveau_test.exs 
b/test/elixir/test/nouveau_test.exs
index 352270c22..609877b7b 100644
--- a/test/elixir/test/nouveau_test.exs
+++ b/test/elixir/test/nouveau_test.exs
@@ -194,7 +194,7 @@ defmodule NouveauTest do
   end
 
   @tag :with_db
-  test "search returns all matches for hello by relevance", context do
+  test "search returns all matches for hello by relevance (implicit)", context 
do
     db_name = context[:db_name]
     create_search_docs(db_name)
     create_ddoc(db_name)
@@ -209,6 +209,38 @@ defmodule NouveauTest do
     assert Enum.at(Enum.at(orders, 0), 0)["value"] > Enum.at(Enum.at(orders, 
1), 0)["value"]
   end
 
+  @tag :with_db
+  test "search returns all matches for hello by relevance (explicit)", context 
do
+    db_name = context[:db_name]
+    create_search_docs(db_name)
+    create_ddoc(db_name)
+
+    url = "/#{db_name}/_design/foo/_nouveau/bar"
+    resp = Couch.get(url, query: %{q: "txt:hello", sort: "\"-<score>\"", 
include_docs: true})
+    assert_status_code(resp, 200)
+    ids = get_ids(resp)
+    orders = get_orders(resp)
+    # doc4 scores higher (more hello's)
+    assert ids == ["doc4", "doc3"]
+    assert Enum.at(Enum.at(orders, 0), 0)["value"] > Enum.at(Enum.at(orders, 
1), 0)["value"]
+  end
+
+  @tag :with_db
+  test "search returns all matches for hello by reverse relevance (explicit)", 
context do
+    db_name = context[:db_name]
+    create_search_docs(db_name)
+    create_ddoc(db_name)
+
+    url = "/#{db_name}/_design/foo/_nouveau/bar"
+    resp = Couch.get(url, query: %{q: "txt:hello", sort: "\"<score>\"", 
include_docs: true})
+    assert_status_code(resp, 200)
+    ids = get_ids(resp)
+    orders = get_orders(resp)
+    # doc4 scores lower (fewer hello's)
+    assert ids == ["doc3", "doc4"]
+    assert Enum.at(Enum.at(orders, 0), 0)["value"] < Enum.at(Enum.at(orders, 
1), 0)["value"]
+  end
+
   @tag :with_db
   test "search returns all items for POST", context do
     db_name = context[:db_name]

Reply via email to