You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2025/12/02 12:27:26 UTC
(couchdb) 05/08: Complete MangoDatabase Elixir port
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 <ja...@neighbourhood.ie>
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