summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Branca <chewbranca@apache.org>2017-12-08 20:56:24 +0000
committerRussell Branca <chewbranca@apache.org>2017-12-08 20:56:24 +0000
commit6895fdc8b6a4147fa1a91119e701789657955376 (patch)
tree8c0ed1c03e346e74097b8f5cc8290d947a204d96
parent7aba50bdaee685ff7bc8cd0dc6271eed96aa2f9f (diff)
downloadcouchdb-6895fdc8b6a4147fa1a91119e701789657955376.tar.gz
Port reduce.js suite
-rw-r--r--elixir_suite/test/reduce_test.exs430
1 files changed, 430 insertions, 0 deletions
diff --git a/elixir_suite/test/reduce_test.exs b/elixir_suite/test/reduce_test.exs
new file mode 100644
index 000000000..a01c99764
--- /dev/null
+++ b/elixir_suite/test/reduce_test.exs
@@ -0,0 +1,430 @@
+defmodule ReduceTest do
+ use CouchTestCase
+
+ @moduletag :views
+
+ @moduledoc """
+ Test CouchDB view reduces
+ This is a port of the reduce.js suite
+ """
+
+ def summate(n) do
+ (n + 1) * n / 2
+ end
+
+ def make_docs(id, count) do
+ for i <- id..count do
+ %{
+ :_id => Integer.to_string(i),
+ :integer => i,
+ :string => Integer.to_string(i)
+ }
+ end
+ end
+
+ @tag :with_db
+ test "Basic reduce functions", context do
+ db_name = context[:db_name]
+ view_url = "/#{db_name}/_design/foo/_view/bar"
+ num_docs = 500
+ map = ~s"""
+function (doc) {
+ emit(doc.integer, doc.integer);
+ emit(doc.integer, doc.integer);
+};
+ """
+ reduce = "function (keys, values) { return sum(values); };"
+ red_doc = %{:views => %{:bar => %{:map => map, :reduce => reduce}}}
+
+ assert Couch.put("/#{db_name}/_design/foo", [body: red_doc]).body["ok"]
+ docs = make_docs(1, num_docs)
+ assert Couch.post("/#{db_name}/_bulk_docs", [body: %{:docs => docs}]).status_code == 201
+ :timer.sleep(200) # *sigh*
+
+ rows = Couch.get(view_url).body["rows"]
+ assert hd(rows)["value"] == 2 * summate(num_docs)
+
+ query = %{:startkey => 4, :endkey => 4}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 8
+
+ query = %{:startkey => 4, :endkey => 5}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 18
+
+ query = %{:startkey => 4, :endkey => 6}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 30
+
+ query = %{:group => true, :limit => 3}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0)["value"] == 2
+ assert Enum.at(rows, 1)["value"] == 4
+ assert Enum.at(rows, 2)["value"] == 6
+
+ half_num_docs = Integer.floor_div(num_docs, 2)
+ max = Integer.floor_div(num_docs, 30) + 1
+ for i <- 1..max, i * 30 + 1 < half_num_docs do
+ i = i * 30 + 1
+ query = %{:startkey => i, :endkey => num_docs - i}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 2 * (summate(num_docs - i) - summate(i - 1))
+ end
+ end
+
+ @tag :with_db
+ test "More complex array key view row testing", context do
+ db_name = context[:db_name]
+ view_url = "/#{db_name}/_design/foo/_view/bar"
+ map = "function (doc) { emit(doc.keys, 1); };"
+ reduce = "function (keys, values) { return sum(values); };"
+ red_doc = %{:views => %{bar: %{map: map, reduce: reduce}}}
+
+ assert Couch.put("/#{db_name}/_design/foo", [body: red_doc]).body["ok"]
+ for i <- 1..5 do
+ for j <- 0..9 do
+ docs = [
+ %{keys: ["a"]},
+ %{keys: ["a"]},
+ %{keys: ["a", "b"]},
+ %{keys: ["a", "b"]},
+ %{keys: ["a", "b", "c"]},
+ %{keys: ["a", "b", "d"]},
+ %{keys: ["a", "c", "d"]},
+ %{keys: ["d"]},
+ %{keys: ["d", "a"]},
+ %{keys: ["d", "b"]},
+ %{keys: ["d", "c"]}
+ ]
+ assert Couch.post("/#{db_name}/_bulk_docs", [body: %{docs: docs}]).status_code == 201
+ :timer.sleep(20) # *sigh*
+ total_docs = 1 + ((i - 1) * 10 * 11) + ((j + 1) * 11);
+ assert Couch.get("/#{db_name}").body["doc_count"] == total_docs
+ end
+
+ # test group by exact key match
+ query = %{group: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20 * i}
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20 * i}
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20 * i}
+ assert Enum.at(rows, 1) == %{"key" => ["a", "b"], "value" => 20 * i}
+ assert Enum.at(rows, 2) == %{"key" => ["a", "b", "c"], "value" => 10 * i}
+ assert Enum.at(rows, 3) == %{"key" => ["a", "b", "d"], "value" => 10 * i}
+
+ # test group reduce and limit params provide valid json
+ query = %{group: true, limit: 2}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20 * i}
+ assert length(rows) == 2
+
+ # test group by the first element in the key array
+ query = %{group_level: 2}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20*i}
+ assert Enum.at(rows, 1) == %{"key" => ["a","b"], "value" => 40*i}
+ assert Enum.at(rows, 2) == %{"key" => ["a","c"], "value" => 10*i}
+ assert Enum.at(rows, 3) == %{"key" => ["d"], "value" => 10*i}
+ assert Enum.at(rows, 4) == %{"key" => ["d","a"], "value" => 10*i}
+ assert Enum.at(rows, 5) == %{"key" => ["d","b"], "value" => 10*i}
+ assert Enum.at(rows, 6) == %{"key" => ["d","c"], "value" => 10*i}
+
+ # test endkey with inclusive_end=true
+ query = %{group_level: 2, endkey: ~s(["d"]), inclusive_end: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20*i}
+ assert Enum.at(rows, 1) == %{"key" => ["a","b"], "value" => 40*i}
+ assert Enum.at(rows, 2) == %{"key" => ["a","c"], "value" => 10*i}
+ assert Enum.at(rows, 3) == %{"key" => ["d"], "value" => 10*i}
+ assert length(rows) == 4
+
+ # test endkey with inclusive_end=false
+ query = %{group_level: 2, endkey: ~s(["d"]), inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert Enum.at(rows, 0) == %{"key" => ["a"], "value" => 20*i}
+ assert Enum.at(rows, 1) == %{"key" => ["a","b"], "value" => 40*i}
+ assert Enum.at(rows, 2) == %{"key" => ["a","c"], "value" => 10*i}
+ assert length(rows) == 3
+ end
+ end
+
+ @tag :with_db
+ test "More complex reductions that need to use the combine option", context do
+ db_name = context[:db_name]
+ view_url = "/#{db_name}/_design/foo/_view/bar"
+ map = "function (doc) { emit(doc.val, doc.val); };"
+ reduce = ~s"""
+function (keys, values, rereduce) {
+ // This computes the standard deviation of the mapped results
+ var stdDeviation=0.0;
+ var count=0;
+ var total=0.0;
+ var sqrTotal=0.0;
+
+ if (!rereduce) {
+ // This is the reduce phase, we are reducing over emitted values from
+ // the map functions.
+ for(var i in values) {
+ total = total + values[i];
+ sqrTotal = sqrTotal + (values[i] * values[i]);
+ }
+ count = values.length;
+ }
+ else {
+ // This is the rereduce phase, we are re-reducing previosuly
+ // reduced values.
+ for(var i in values) {
+ count = count + values[i].count;
+ total = total + values[i].total;
+ sqrTotal = sqrTotal + values[i].sqrTotal;
+ }
+ }
+
+ var variance = (sqrTotal - ((total * total)/count)) / count;
+ stdDeviation = Math.sqrt(variance);
+
+ // the reduce result. It contains enough information to be rereduced
+ // with other reduce results.
+ return {"stdDeviation":stdDeviation,"count":count,
+ "total":total,"sqrTotal":sqrTotal};
+}
+ """
+
+ red_doc = %{:views => %{:bar => %{:map => map, :reduce => reduce}}}
+ assert Couch.put("/#{db_name}/_design/foo", [body: red_doc]).body["ok"]
+
+ Enum.each(1..10, fn _ ->
+ docs = for i <- 1..10, do: %{val: i * 10}
+ assert Couch.post("/#{db_name}/_bulk_docs", [body: %{:docs => docs}]).status_code == 201
+ end)
+ :timer.sleep(200) # *sigh*
+
+ rows = Couch.get(view_url).body["rows"]
+ assert_in_delta hd(rows)["value"]["stdDeviation"], 28.722813232690143, 0.0000000001
+ end
+
+ @tag :with_db
+ test "Reduce pagination", context do
+ db_name = context[:db_name]
+ view_url = "/#{db_name}/_design/foo/_view/bar"
+ ddoc = %{
+ _id: "_design/foo",
+ language: "javascript",
+ views: %{
+ bar: %{
+ reduce: "_count",
+ map: ~s"""
+ function(doc) {
+ emit(doc.int, doc._id);
+ emit(doc.int + 1, doc._id);
+ emit(doc.int + 2, doc._id);
+ }
+ """
+ }
+ }
+ }
+
+ assert Couch.put("/#{db_name}/_design/foo", [body: ddoc]).body["ok"]
+ docs = for i <- 0..1122, do: %{_id: Integer.to_string(i), int: i}
+ assert Couch.post("/#{db_name}/_bulk_docs", [body: %{:docs => docs}]).status_code == 201
+ :timer.sleep(200) # *sigh*
+
+ rand_val = fn -> :rand.uniform(100000000) end
+
+ # ?group=false tests
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.()}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 9
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 9
+
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 6
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), inclusive_end: false, descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 6
+
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "400"}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 7
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "400", inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 6
+
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "401"}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 8
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "401", inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 7
+
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "402"}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 9
+ query = %{startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "402", inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 8
+
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "398", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 9
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "398", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 8
+
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "399", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 8
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "399", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 7
+
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "400", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 7
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "400", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 6
+
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "400", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 7
+
+ query = %{startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "401", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert hd(rows)["value"] == 5
+
+ # ?group=true tests
+ query = %{:group => true, startkey: 400, endkey: 402, foobar: rand_val.()}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 400
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 402
+ assert Enum.at(rows, 2)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 3
+
+ query = %{:group => true, startkey: 400, endkey: 402, foobar: rand_val.(), inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 2
+ assert Enum.at(rows, 0)["key"] == 400
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), inclusive_end: false, descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 2
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+
+ query = %{:group => true, startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "401"}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 400
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 402
+ assert Enum.at(rows, 2)["value"] == 2
+
+ query = %{:group => true, startkey: 400, endkey: 402, foobar: rand_val.(), endkey_docid: "400"}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 400
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 402
+ assert Enum.at(rows, 2)["value"] == 1
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "401", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 2
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "400", descending: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 1
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "401", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 2
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 2
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), startkey_docid: "400", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 2
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 1
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "398", descending: true, inclusive_end: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 3
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "399", descending: true, inclusive_end: true}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 2
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "399", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 3
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ assert Enum.at(rows, 2)["key"] == 400
+ assert Enum.at(rows, 2)["value"] == 1
+
+ query = %{:group => true, startkey: 402, endkey: 400, foobar: rand_val.(), endkey_docid: "400", descending: true, inclusive_end: false}
+ rows = Couch.get(view_url, query: query).body["rows"]
+ assert length(rows) == 2
+ assert Enum.at(rows, 0)["key"] == 402
+ assert Enum.at(rows, 0)["value"] == 3
+ assert Enum.at(rows, 1)["key"] == 401
+ assert Enum.at(rows, 1)["value"] == 3
+ end
+end