| defmodule ViewMultiKeyDesignTest do |
| use CouchTestCase |
| |
| @keys [10, 15, 30, 37, 50] |
| |
| @ddoc %{ |
| _id: "_design/test", |
| language: "javascript", |
| views: %{ |
| all_docs: %{ |
| map: "function(doc) { emit(doc.integer, doc.string) }" |
| }, |
| multi_emit: %{ |
| map: "function(doc) {for(var i = 0 ; i < 3 ; i++) { emit(i, doc.integer) ; } }" |
| }, |
| summate: %{ |
| map: "function (doc) {emit(doc.integer, doc.integer)};", |
| reduce: "function (keys, values) { return sum(values); };" |
| } |
| } |
| } |
| |
| setup_all do |
| db_name = random_db_name() |
| {:ok, _} = create_db(db_name) |
| on_exit(fn -> delete_db(db_name) end) |
| |
| bulk_save(db_name, make_docs(0..99)) |
| {:ok, _} = create_doc(db_name, @ddoc) |
| |
| {:ok, [db_name: db_name]} |
| end |
| |
| test "that missing keys work too", context do |
| db_name = context[:db_name] |
| keys = [101, 30, 15, 37, 50] |
| resp = view(db_name, "test/summate", [group: true], keys) |
| rows = resp.body["rows"] |
| assert length(rows) == length(keys) - 1 |
| |
| assert Enum.all?(rows, &Enum.member?(keys, &1["key"])) |
| assert Enum.all?(rows, &(&1["key"] == &1["value"])) |
| end |
| |
| test "keys in POST body", context do |
| db_name = context[:db_name] |
| resp = view(db_name, "test/all_docs", nil, @keys) |
| rows = resp.body["rows"] |
| assert length(rows) == length(@keys) |
| assert Enum.all?(rows, &Enum.member?(@keys, &1["key"])) |
| assert Enum.all?(rows, &(&1["key"] == String.to_integer(&1["value"]))) |
| end |
| |
| test "keys in GET parameters", context do |
| db_name = context[:db_name] |
| resp = view(db_name, "test/all_docs", keys: :jiffy.encode(@keys)) |
| rows = resp.body["rows"] |
| assert length(rows) == length(@keys) |
| assert Enum.all?(rows, &Enum.member?(@keys, &1["key"])) |
| assert Enum.all?(rows, &(&1["key"] == String.to_integer(&1["value"]))) |
| end |
| |
| test "empty keys", context do |
| db_name = context[:db_name] |
| |
| resp = view(db_name, "test/all_docs", keys: :jiffy.encode([])) |
| assert resp.status_code == 200 |
| rows = resp.body["rows"] |
| assert Enum.empty?(rows) |
| end |
| |
| test "keys in POST body (group)", context do |
| db_name = context[:db_name] |
| resp = view(db_name, "test/summate", [group: true], @keys) |
| rows = resp.body["rows"] |
| assert length(rows) == length(@keys) |
| assert Enum.all?(rows, &Enum.member?(@keys, &1["key"])) |
| assert Enum.all?(rows, &(&1["key"] == &1["value"])) |
| end |
| |
| test "keys in GET body (group)", context do |
| db_name = context[:db_name] |
| resp = view(db_name, "test/summate", group: true, keys: :jiffy.encode(@keys)) |
| rows = resp.body["rows"] |
| assert length(rows) == length(@keys) |
| assert Enum.all?(rows, &Enum.member?(@keys, &1["key"])) |
| assert Enum.all?(rows, &(&1["key"] == &1["value"])) |
| end |
| |
| test "POST - invalid parameter combinations get rejected ", context do |
| db_name = context[:db_name] |
| |
| badargs = [[startkey: 0], [endkey: 0], [key: 0], [group_level: 2]] |
| |
| Enum.each(badargs, fn args -> |
| resp = |
| Couch.post("/#{db_name}/_design/test/_view/all_docs", |
| query: args, |
| body: %{"keys" => @keys} |
| ) |
| |
| assert resp.status_code == 400 |
| assert resp.body["error"] == "query_parse_error" |
| end) |
| |
| resp = |
| Couch.post("/#{db_name}/_design/test/_view/summate", |
| query: nil, |
| body: %{"keys" => @keys} |
| ) |
| |
| assert resp.status_code == 400 |
| assert resp.body["error"] == "query_parse_error" |
| end |
| |
| test "GET - invalid parameter combinations get rejected ", context do |
| db_name = context[:db_name] |
| |
| badargs = [ |
| [startkey: 0, keys: :jiffy.encode(@keys)], |
| [endkey: 0, keys: :jiffy.encode(@keys)], |
| [key: 0, keys: :jiffy.encode(@keys)], |
| [group_level: 2, keys: :jiffy.encode(@keys)] |
| ] |
| |
| Enum.each(badargs, fn args -> |
| resp = |
| Couch.get("/#{db_name}/_design/test/_view/all_docs", |
| query: args |
| ) |
| |
| assert resp.status_code == 400 |
| assert resp.body["error"] == "query_parse_error" |
| end) |
| |
| resp = |
| Couch.get("/#{db_name}/_design/test/_view/summate", |
| query: [keys: :jiffy.encode(@keys)], |
| body: %{"keys" => @keys} |
| ) |
| |
| assert resp.status_code == 400 |
| assert resp.body["error"] == "query_parse_error" |
| end |
| |
| test "that a map & reduce containing func support keys when reduce=false", context do |
| db_name = context[:db_name] |
| resp = view(db_name, "test/summate", [reduce: false], @keys) |
| assert length(resp.body["rows"]) == 5 |
| |
| resp = view(db_name, "test/summate", reduce: false, keys: :jiffy.encode(@keys)) |
| assert length(resp.body["rows"]) == 5 |
| end |
| |
| test "that limiting by startkey_docid and endkey_docid get applied", context do |
| db_name = context[:db_name] |
| |
| exp_key = [0, 0, 0, 2, 2, 2] |
| exp_val = [21, 22, 23, 21, 22, 23] |
| |
| resp = |
| view(db_name, "test/multi_emit", [startkey_docid: 21, endkey_docid: 23], [0, 2]) |
| |
| rows = resp.body["rows"] |
| rows_key = Enum.map(rows, & &1["key"]) |
| assert rows_key == exp_key |
| |
| rows_value = Enum.map(rows, & &1["value"]) |
| assert rows_value == exp_val |
| |
| resp = |
| view(db_name, "test/multi_emit", |
| startkey_docid: 21, |
| endkey_docid: 23, |
| keys: :jiffy.encode([0, 2]) |
| ) |
| |
| rows = resp.body["rows"] |
| rows_key = Enum.map(rows, & &1["key"]) |
| assert rows_key == exp_key |
| |
| rows_value = Enum.map(rows, & &1["value"]) |
| assert rows_value == exp_val |
| end |
| |
| test "limit works", context do |
| db_name = context[:db_name] |
| |
| resp = view(db_name, "test/all_docs", [limit: 1], @keys) |
| rows = resp.body["rows"] |
| assert length(rows) == 1 |
| assert Enum.at(rows, 0)["key"] == 10 |
| |
| resp = view(db_name, "test/all_docs", limit: 1, keys: :jiffy.encode(@keys)) |
| rows = resp.body["rows"] |
| assert length(rows) == 1 |
| assert Enum.at(rows, 0)["key"] == 10 |
| end |
| |
| test "offset works", context do |
| db_name = context[:db_name] |
| |
| resp = view(db_name, "test/multi_emit", [skip: 1], [0]) |
| rows = resp.body["rows"] |
| assert length(rows) == 99 |
| |
| resp = view(db_name, "test/multi_emit", skip: 1, keys: :jiffy.encode([0])) |
| rows = resp.body["rows"] |
| assert length(rows) == 99 |
| end |
| |
| test "dir ascending works", context do |
| db_name = context[:db_name] |
| |
| expect_rows = mk_rows(0..99, 1, &</2) ++ mk_rows(0..99, 2, &</2) |
| |
| resp = view(db_name, "test/multi_emit", [descending: false], [1, 2]) |
| rows = resp.body["rows"] |
| assert length(rows) == 200 |
| assert expect_rows == rows |
| |
| keys = :jiffy.encode([1, 2]) |
| resp = view(db_name, "test/multi_emit", descending: false, keys: keys) |
| rows = resp.body["rows"] |
| assert length(rows) == 200 |
| assert expect_rows == rows |
| end |
| |
| test "dir descending works", context do |
| db_name = context[:db_name] |
| |
| expect_rows = mk_rows(0..99, 2, &>/2) ++ mk_rows(0..99, 1, &>/2) |
| |
| resp = view(db_name, "test/multi_emit", [descending: true], [1, 2]) |
| rows = resp.body["rows"] |
| assert length(rows) == 200 |
| assert expect_rows == rows |
| |
| keys = :jiffy.encode([1, 2]) |
| resp = view(db_name, "test/multi_emit", descending: true, keys: keys) |
| rows = resp.body["rows"] |
| assert length(rows) == 200 |
| assert expect_rows == rows |
| end |
| |
| test "argument combinations", context do |
| db_name = context[:db_name] |
| |
| resp = view(db_name, "test/multi_emit", [descending: true, skip: 3, limit: 2], [2]) |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| |
| resp = |
| view(db_name, "test/multi_emit", |
| descending: true, |
| skip: 3, |
| limit: 2, |
| keys: :jiffy.encode([2]) |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| |
| resp = |
| view(db_name, "test/multi_emit", [skip: 0, limit: 1, startkey_docid: "13"], [0]) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 1 |
| assert Enum.at(rows, 0)["value"] == 13 |
| |
| resp = |
| view(db_name, "test/multi_emit", [skip: 2, limit: 3, startkey_docid: "13"], [0]) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 3 |
| |
| resp = |
| view(db_name, "test/multi_emit", |
| skip: 2, |
| limit: 3, |
| startkey_docid: "13", |
| keys: :jiffy.encode([0]) |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 3 |
| |
| resp = |
| view( |
| db_name, |
| "test/multi_emit", |
| [skip: 1, limit: 5, startkey_docid: "25", endkey_docid: "27"], |
| [1] |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| assert Enum.at(rows, 0)["value"] == 26 or assert(Enum.at(rows, 0)["value"] == 27) |
| |
| resp = |
| view(db_name, "test/multi_emit", |
| skip: 1, |
| limit: 5, |
| startkey_docid: "25", |
| endkey_docid: "27", |
| keys: :jiffy.encode([1]) |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| assert Enum.at(rows, 0)["value"] == 26 or assert(Enum.at(rows, 0)["value"] == 27) |
| |
| resp = |
| view( |
| db_name, |
| "test/multi_emit", |
| [skip: 1, limit: 5, startkey_docid: "28", endkey_docid: "26", descending: true], |
| [1] |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| assert Enum.at(rows, 0)["value"] == 26 or assert(Enum.at(rows, 0)["value"] == 27) |
| |
| resp = |
| view(db_name, "test/multi_emit", |
| skip: 1, |
| limit: 5, |
| startkey_docid: "28", |
| endkey_docid: "26", |
| descending: true, |
| keys: :jiffy.encode([1]) |
| ) |
| |
| rows = resp.body["rows"] |
| assert length(rows) == 2 |
| end |
| |
| defp mk_rows(range, key, sort_fun) do |
| row_fun = fn(i) -> %{"id" => "#{i}", "key" => key, "value" => i} end |
| sort_mapper = fn(row) -> {row["key"], row["id"]} end |
| range |
| |> Enum.map(row_fun) |
| |> Enum.sort_by(sort_mapper, sort_fun) |
| end |
| end |