diff options
author | Russell Branca <chewbranca@apache.org> | 2017-12-08 20:56:24 +0000 |
---|---|---|
committer | Russell Branca <chewbranca@apache.org> | 2017-12-08 20:56:24 +0000 |
commit | 6895fdc8b6a4147fa1a91119e701789657955376 (patch) | |
tree | 8c0ed1c03e346e74097b8f5cc8290d947a204d96 | |
parent | 7aba50bdaee685ff7bc8cd0dc6271eed96aa2f9f (diff) | |
download | couchdb-6895fdc8b6a4147fa1a91119e701789657955376.tar.gz |
Port reduce.js suite
-rw-r--r-- | elixir_suite/test/reduce_test.exs | 430 |
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 |