You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ch...@apache.org on 2018/06/22 22:15:46 UTC

[couchdb] 17/31: Port reduce.js suite

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

chewbranca pushed a commit to branch elixir-suite
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 859533a9db9794a69feeb024609cefec865876d2
Author: Russell Branca <ch...@apache.org>
AuthorDate: Fri Dec 8 20:56:24 2017 +0000

    Port reduce.js suite
---
 elixir_suite/test/reduce_test.exs | 430 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 430 insertions(+)

diff --git a/elixir_suite/test/reduce_test.exs b/elixir_suite/test/reduce_test.exs
new file mode 100644
index 0000000..a01c997
--- /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