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
