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

jan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 598d8b1a245d5647030691fe88e7038d124a9991
Author: James Coglan <[email protected]>
AuthorDate: Wed Oct 29 15:52:42 2025 +0000

    Complete MangoDatabase Elixir port
---
 test/elixir/test/mango/02_basic_find_test.exs |   2 +-
 test/elixir/test/mango/06_basic_text_test.exs |   4 +-
 test/elixir/test/mango/08_text_limit_test.exs |   2 +-
 test/elixir/test/support/mango_database.ex    | 157 ++++++++++++++++++++++----
 test/elixir/test/support/user_docs.ex         |   4 +-
 5 files changed, 145 insertions(+), 24 deletions(-)

diff --git a/test/elixir/test/mango/02_basic_find_test.exs 
b/test/elixir/test/mango/02_basic_find_test.exs
index e6ae1ad64..b9ea42da6 100644
--- a/test/elixir/test/mango/02_basic_find_test.exs
+++ b/test/elixir/test/mango/02_basic_find_test.exs
@@ -20,7 +20,7 @@ defmodule BasicFindTest do
   end
 
   test "simple find" do
-    docs = MangoDatabase.find(@db_name, %{"age" => %{"$lt" => 35}})
+    {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => %{"$lt" => 35}})
     user_ids = Enum.map(docs, fn doc -> doc["user_id"] end)
 
     assert user_ids == [9, 1, 7]
diff --git a/test/elixir/test/mango/06_basic_text_test.exs 
b/test/elixir/test/mango/06_basic_text_test.exs
index 679875bfa..310cd9cdb 100644
--- a/test/elixir/test/mango/06_basic_text_test.exs
+++ b/test/elixir/test/mango/06_basic_text_test.exs
@@ -21,12 +21,12 @@ defmodule ElemMatchTests do
 
   test "elem match non object" do
     q = %{"bestfriends" => %{"$elemMatch" => %{"$eq" => "Wolverine", "$eq" => 
"Cyclops"}}}
-    docs = MangoDatabase.find(@db_name, q)
+    {:ok, docs} = MangoDatabase.find(@db_name, q)
     assert length(docs) == 1
     assert Enum.at(docs, 0)["bestfriends"] == ["Wolverine", "Cyclops"]
 
     q = %{"results" => %{"$elemMatch" => %{"$gte" => 80, "$lt" => 85}}}
-    docs = MangoDatabase.find(@db_name, q)
+    {:ok, docs} = MangoDatabase.find(@db_name, q)
     assert length(docs) == 1
     assert Enum.at(docs, 0)["results"] == [82, 85, 88]
   end
diff --git a/test/elixir/test/mango/08_text_limit_test.exs 
b/test/elixir/test/mango/08_text_limit_test.exs
index e61f8085b..07c6b01f9 100644
--- a/test/elixir/test/mango/08_text_limit_test.exs
+++ b/test/elixir/test/mango/08_text_limit_test.exs
@@ -21,7 +21,7 @@ defmodule LimitTests do
 
   test "limit field" do
     q = %{"$or" => [%{"user_id" => %{"$lt" => 10}}, %{"filtered_array.[]" => 
1}]}
-    docs = MangoDatabase.find(@db_name, q, limit: 10)
+    {:ok, docs} = MangoDatabase.find(@db_name, q, limit: 10)
 
     assert length(docs) == 8
     Enum.each(docs, fn d -> assert d["user_id"] < 10 end)
diff --git a/test/elixir/test/support/mango_database.ex 
b/test/elixir/test/support/mango_database.ex
index fbbd23d1c..1b5b4ab63 100644
--- a/test/elixir/test/support/mango_database.ex
+++ b/test/elixir/test/support/mango_database.ex
@@ -11,6 +11,7 @@
 # the License.
 
 defmodule MangoDatabase do
+  @moduledoc false
   def has_text_service() do
     resp = Couch.get("/")
     "search" in resp.body["features"]
@@ -29,7 +30,7 @@ defmodule MangoDatabase do
     end
   end
 
-  defp create(db, opts) do
+  def create(db, opts \\ []) do
     partitioned = Keyword.get(opts, :partitioned, false)
     Couch.put("/#{db}?partitioned=#{partitioned}")
   end
@@ -38,42 +39,160 @@ defmodule MangoDatabase do
     Couch.delete("/#{db}")
   end
 
+  def save_doc(db, doc, opts \\ []) do
+    MangoDatabase.save_docs(db, [doc], opts)
+  end
+
+  def save_docs_with_conflicts(db, docs) do
+    body = %{"docs" => docs, "new_edits" => false}
+    Couch.post("/#{db}/_bulk_docs", body: body)
+  end
+
+  # If a certain keyword like sort or field is passed in the options,
+  # then it is added to the request body.
+  defp put_if_set(map, key, opts, opts_key) do
+    if Keyword.has_key?(opts, opts_key) do
+      Map.put(map, key, opts[opts_key])
+    else
+      map
+    end
+  end
+
   # TODO: make this use batches if necessary
-  def save_docs(db, docs) do
-    resp = Couch.post("/#{db}/_bulk_docs", body: %{"docs" => docs})
+  def save_docs(db, docs, opts \\ []) do
+    query = %{}
+    |> put_if_set("w", opts, :w)
+
+    result = Couch.post("/#{db}/_bulk_docs", body: %{"docs" => docs}, query: 
query)
+    zipped_docs = Enum.zip(docs, result.body)
+
+    # This returns the doc list including _id and _rev values
+    resp = Enum.map(zipped_docs, fn {doc, result} ->
+      doc
+      |> Map.put("_id", result["id"])
+      |> Map.put("_rev", result["rev"])
+    end)
+
+    # _bulk_docs sometimes returns errors in the body and this is captured here
+    errors = Enum.filter(result.body, fn r ->
+      Map.has_key?(r, "error")
+    end)
+    if errors == [] do
+      resp
+    else
+      {:error, errors}
+    end
   end
 
-  def create_index(db, fields, name) do
-    Couch.post("/#{db}/_index", body: %{
-      "index" => %{"fields" => fields},
-      "name" => name,
-      "ddoc" => name,
+  def create_index(db, fields, options \\ []) do
+    index = %{
+      "fields" => fields,
+    }
+    |> put_if_set("selector", options, :selector)
+    |> put_if_set("partial_filter_selector", options, :partial_filter_selector)
+
+    body = %{
+      "index" => index,
       "type" => "json",
       "w" => 3
-    })
+    }
+    |> put_if_set("type", options, :idx_type)
+    |> put_if_set("name", options, :name)
+    |> put_if_set("ddoc", options, :ddoc)
+
+    resp = Couch.post("/#{db}/_index", body: body)
+    if resp.status_code == 200 do
+      {:ok, resp.body["result"] == "created"}
+    else
+      {:error, resp}
+    end
   end
 
-  def create_text_index(db) do
-    Couch.post("/#{db}/_index", body: %{
-      "index" => %{},
-      "type" => "text",
+  def create_text_index(db, options \\ []) do
+    index = %{}
+    |> put_if_set("default_analyzer", options, :analyzer)
+    |> put_if_set("default_field", options, :default_field)
+    |> put_if_set("index_array_lengths", options, :index_array_lengths)
+    |> put_if_set("selector", options, :selector)
+    |> put_if_set("partial_filter_selector", options, :partial_filter_selector)
+    |> put_if_set("fields", options, :fields)
+
+    body = %{
+      "index" => index,
+      "type" => Keyword.get(options, :idx_type, "text"),
       "w" => 3
-    })
+    }
+    |> put_if_set("name", options, :name)
+    |> put_if_set("ddoc", options, :ddoc)
+
+    resp = Couch.post("/#{db}/_index", body: body)
+
+    if resp.status_code == 200 do
+      {:ok, resp.body["result"] == "created"}
+    else
+      {:error, resp}
+    end
+  end
+
+  def list_indexes(db, opts \\ []) do
+    limit = Keyword.get(opts, :limit)
+    skip  = Keyword.get(opts, :skip)
+    query =
+      [limit: limit, skip: skip]
+      |> Enum.filter(fn {_k, v} -> not is_nil(v) end)
+      |> Enum.map_join("&", fn {k, v} -> "#{k}=#{v}" end)
+
+    path =
+      if query == "" do
+        "/#{db}/_index"
+      else
+        "/#{db}/_index?#{query}"
+      end
+    resp = Couch.get(path)
+
+    if resp.status_code == 200 do
+      {:ok, resp.body["indexes"]}
+    else
+      {:error, resp}
+    end
   end
 
-  # TODO: port more options from src/mango/test/mango.py `def find(...)`
   def find(db, selector, opts \\ []) do
-    defaults = [use_index: nil, skip: 0, limit: 25, r: 1, conflicts: false]
+    defaults = [
+      use_index: nil,
+      skip: 0,
+      limit: 25,
+      r: 1,
+      conflicts: false,
+      explain: false,
+      return_raw: false
+    ]
     options = Keyword.merge(defaults, opts)
 
-    resp = Couch.post("/#{db}/_find", body: %{
+    path =
+      case options[:explain] do
+        true -> "/#{db}/_explain"
+        _ -> "/#{db}/_find"
+      end
+
+    resp = Couch.post(path, body: %{
       "selector" => selector,
       "use_index" => options[:use_index],
       "skip" => options[:skip],
       "limit" => options[:limit],
       "r" => options[:r],
       "conflicts" => options[:conflicts]
-    })
-    resp.body["docs"]
+    }
+    |> put_if_set("sort", options, :sort)
+    |> put_if_set("fields", options, :fields)
+    |> put_if_set("execution_stats", options, :executionStats)
+    |> put_if_set("allow_fallback", options, :allow_fallback)
+    )
+
+    case {(options[:explain] or options[:return_raw]), resp.status_code} do
+      {false, 200} -> {:ok, resp.body["docs"]}
+      {true, 200} -> {:ok, resp.body}
+      _ -> {:error, resp}
+    end
   end
 end
diff --git a/test/elixir/test/support/user_docs.ex 
b/test/elixir/test/support/user_docs.ex
index 456fd0b81..eb015cc64 100644
--- a/test/elixir/test/support/user_docs.ex
+++ b/test/elixir/test/support/user_docs.ex
@@ -360,6 +360,8 @@ defmodule UserDocs do
       "text" -> add_text_indexes(db)
       "special" -> :ok
     end
+
+    :ok
   end
 
   def len() do
@@ -389,7 +391,7 @@ defmodule UserDocs do
     ]
 
     Enum.each(indexes, fn {idx, name} ->
-      MangoDatabase.create_index(db, idx, name)
+      MangoDatabase.create_index(db, idx, name: name, ddoc: name)
     end)
   end
 

Reply via email to