summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Christopher Anderson <jchris@apache.org>2009-03-19 11:20:36 +0000
committerJohn Christopher Anderson <jchris@apache.org>2009-03-19 11:20:36 +0000
commit292091ece9a97c51f92e2dda547e732848db1515 (patch)
treed1716717332ffe828eb037389eb04cc9a2d55df4
parent25a26fc5ad1c759dbc2392b6eec5ccc20952f12f (diff)
inclusive_end query option for views, defaults to true (does not change current behavior). inclusive_end=false treats endkey as an open interval.
this is progress on COUCHDB-194. I think it is enough for 0.9, we can discuss switching the default behavior, and adding a similar option for startkey. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@755926 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--share/www/script/test/view_collation.js24
-rw-r--r--src/couchdb/couch_db.hrl1
-rw-r--r--src/couchdb/couch_httpd_view.erl67
3 files changed, 70 insertions, 22 deletions
diff --git a/share/www/script/test/view_collation.js b/share/www/script/test/view_collation.js
index bcb63404..f59204c7 100644
--- a/share/www/script/test/view_collation.js
+++ b/share/www/script/test/view_collation.js
@@ -85,4 +85,28 @@ couchTests.view_collation = function(debug) {
rows = db.query(queryFun, null, queryOptions).rows;
T(rows.length == 1 && equals(rows[0].key, values[i]));
}
+
+ // test inclusive_end=true (the default)
+ // the inclusive_end=true functionality is limited to endkey currently
+ // if you need inclusive_start=false for startkey, please do implement. ;)
+ var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:true}).rows;
+ T(rows[rows.length-1].key == "b")
+ // descending=true
+ var rows = db.query(queryFun, null, {endkey : "b",
+ descending:true, inclusive_end:true}).rows;
+ T(rows[rows.length-1].key == "b")
+
+ // test inclusive_end=false
+ var rows = db.query(queryFun, null, {endkey : "b", inclusive_end:false}).rows;
+ T(rows[rows.length-1].key == "aa")
+ // descending=true
+ var rows = db.query(queryFun, null, {endkey : "b",
+ descending:true, inclusive_end:false}).rows;
+ T(rows[rows.length-1].key == "B")
+
+ // inclusive_end=false overrides endkey_docid
+ var rows = db.query(queryFun, null, {
+ endkey : "b", endkey_docid: "b",
+ inclusive_end:false}).rows;
+ T(rows[rows.length-1].key == "aa")
};
diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index f460f450..6d9081a8 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -155,6 +155,7 @@
skip = 0,
group_level = 0,
reduce = true,
+ inclusive_end=true, % aka a closed-interval
include_docs = false
}).
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 7f338655..4e922d50 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -259,7 +259,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{start_key=JsonKey,end_key=JsonKey};
_ ->
Msg = io_lib:format("Query parameter \"~s\" not compatible with multi key mode.", [Key]),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"startkey_docid", DocId} ->
Args#view_query_args{start_docid=list_to_binary(DocId)};
@@ -271,7 +271,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{start_key=?JSON_DECODE(Value)};
_ ->
Msg = io_lib:format("Query parameter \"~s\" not compatible with multi key mode.", [Key]),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"endkey", Value} ->
case Keys of
@@ -279,27 +279,27 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{end_key=?JSON_DECODE(Value)};
_ ->
Msg = io_lib:format("Query parameter \"~s\" not compatible with multi key mode.", [Key]),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"limit", Value} ->
case (catch list_to_integer(Value)) of
Limit when is_integer(Limit) ->
if Limit < 0 ->
Msg = io_lib:format("Limit must be a positive integer: limit=~s", [Value]),
- throw({query_parse_error, Msg});
+ throw({query_parse_error, ?l2b(Msg)});
true ->
Args#view_query_args{limit=Limit}
end;
_Error ->
Msg = io_lib:format("Bad URL query value, number expected: limit=~s", [Value]),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"count", Value} ->
- throw({query_parse_error, "URL query parameter 'count' has been changed to 'limit'."});
+ throw({query_parse_error, <<"URL query parameter 'count' has been changed to 'limit'.">>});
{"stale", "ok"} ->
Args#view_query_args{stale=ok};
{"update", "false"} ->
- throw({query_parse_error, "URL query parameter 'update=false' has been changed to 'stale=ok'."});
+ throw({query_parse_error, <<"URL query parameter 'update=false' has been changed to 'stale=ok'.">>});
{"descending", "true"} ->
case Args#view_query_args.direction of
fwd ->
@@ -329,7 +329,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
_Error ->
Msg = lists:flatten(io_lib:format(
"Bad URL query value, number expected: skip=~s", [Value])),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"group", Value} ->
case Value of
@@ -339,7 +339,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{group_level=0};
_ ->
Msg = "Bad URL query value for 'group' expected \"true\" or \"false\".",
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
{"group_level", LevelStr} ->
case Keys of
@@ -347,8 +347,12 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{group_level=list_to_integer(LevelStr)};
_ ->
Msg = lists:flatten(io_lib:format("Multi-key fetches for a reduce view must include group=true", [])),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end;
+ {"inclusive_end", "true"} ->
+ Args#view_query_args{inclusive_end=true};
+ {"inclusive_end", "false"} ->
+ Args#view_query_args{inclusive_end=false};
{"reduce", "true"} ->
Args#view_query_args{reduce=true};
{"reduce", "false"} ->
@@ -361,7 +365,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
Args#view_query_args{include_docs=false};
_ ->
Msg1 = "Bad URL query value for 'include_docs' expected \"true\" or \"false\".",
- throw({query_parse_error, Msg1})
+ throw({query_parse_error, ?l2b(Msg1)})
end;
{"format", _} ->
% we just ignore format, so that JS can have it
@@ -373,7 +377,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
false ->
Msg = lists:flatten(io_lib:format(
"Bad URL query key:~s", [Key])),
- throw({query_parse_error, Msg})
+ throw({query_parse_error, ?l2b(Msg)})
end
end
end, #view_query_args{}, QueryList),
@@ -381,7 +385,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
true ->
case QueryArgs#view_query_args.include_docs and QueryArgs#view_query_args.reduce of
true ->
- ErrMsg = "Bad URL query key for reduce operation: include_docs",
+ ErrMsg = <<"Bad URL query key for reduce operation: include_docs">>,
throw({query_parse_error, ErrMsg});
_ ->
ok
@@ -404,8 +408,7 @@ parse_view_query(Req, Keys, IsReduce, IgnoreExtra) ->
#view_query_args{reduce=OptReduce} = QueryArgs,
case OptReduce of
true ->
- Msg = lists:flatten(io_lib:format(
- "Multi-key fetches for a reduce view must include group=true", [])),
+ Msg = <<"Multi-key fetches for a reduce view must include group=true">>,
throw({query_parse_error, Msg});
_ ->
QueryArgs
@@ -419,6 +422,7 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db,
#view_query_args{
end_key = EndKey,
end_docid = EndDocId,
+ inclusive_end = InclusiveEnd,
direction = Dir
} = QueryArgs,
@@ -427,7 +431,8 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db,
start_response = StartRespFun,
send_row = SendRowFun,
reduce_count = ReduceCountFun
- } = apply_default_helper_funs(HelperFuns, {Dir, EndKey, EndDocId}),
+ } = apply_default_helper_funs(HelperFuns,
+ {Dir, EndKey, EndDocId, InclusiveEnd}),
#view_query_args{
include_docs = IncludeDocs
@@ -467,9 +472,9 @@ apply_default_helper_funs(#view_fold_helper_funs{
passed_end = PassedEnd,
start_response = StartResp,
send_row = SendRow
-}=Helpers, {Dir, EndKey, EndDocId}) ->
+}=Helpers, {Dir, EndKey, EndDocId, InclusiveEnd}) ->
PassedEnd2 = case PassedEnd of
- undefined -> make_passed_end_fun(Dir, EndKey, EndDocId);
+ undefined -> make_passed_end_fun(Dir, EndKey, EndDocId, InclusiveEnd);
_ -> PassedEnd
end,
@@ -508,15 +513,33 @@ apply_default_helper_funs(#reduce_fold_helper_funs{
send_row = SendRow2
}.
-make_passed_end_fun(Dir, EndKey, EndDocId) ->
- case Dir of
- fwd ->
+make_passed_end_fun(fwd, EndKey, EndDocId, InclusiveEnd) ->
+ case InclusiveEnd of
+ true ->
fun(ViewKey, ViewId) ->
couch_view:less_json([EndKey, EndDocId], [ViewKey, ViewId])
end;
- rev->
+ false ->
+ fun
+ (ViewKey, _ViewId) when ViewKey == EndKey ->
+ true;
+ (ViewKey, ViewId) ->
+ couch_view:less_json([EndKey, EndDocId], [ViewKey, ViewId])
+ end
+ end;
+
+make_passed_end_fun(rev, EndKey, EndDocId, InclusiveEnd) ->
+ case InclusiveEnd of
+ true ->
fun(ViewKey, ViewId) ->
couch_view:less_json([ViewKey, ViewId], [EndKey, EndDocId])
+ end;
+ false->
+ fun
+ (ViewKey, _ViewId) when ViewKey == EndKey ->
+ true;
+ (ViewKey, ViewId) ->
+ couch_view:less_json([ViewKey, ViewId], [EndKey, EndDocId])
end
end.