summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/test/all_docs.js7
-rw-r--r--share/www/script/test/view_collation_raw.js9
-rw-r--r--src/couchdb/couch_btree.erl1
-rw-r--r--src/couchdb/couch_httpd_db.erl4
-rw-r--r--src/couchdb/couch_httpd_view.erl54
5 files changed, 56 insertions, 19 deletions
diff --git a/share/www/script/test/all_docs.js b/share/www/script/test/all_docs.js
index 1d83aa95..1afe701d 100644
--- a/share/www/script/test/all_docs.js
+++ b/share/www/script/test/all_docs.js
@@ -41,6 +41,13 @@ couchTests.all_docs = function(debug) {
var all = db.allDocs({startkey:"2"});
T(all.offset == 2);
+ // Confirm that queries may assume raw collation.
+ var raw = db.allDocs({
+ startkey: "org.couchdb.user:",
+ endkey: "org.couchdb.user;"
+ });
+ TEquals(0, raw.rows.length);
+
// check that the docs show up in the seq view in the order they were created
var changes = db.changes();
var ids = ["0","3","1","2"];
diff --git a/share/www/script/test/view_collation_raw.js b/share/www/script/test/view_collation_raw.js
index 31624cdb..779f7eb8 100644
--- a/share/www/script/test/view_collation_raw.js
+++ b/share/www/script/test/view_collation_raw.js
@@ -76,12 +76,19 @@ couchTests.view_collation_raw = function(debug) {
}
}
T(db.save(designDoc).ok);
+
+ // Confirm that everything collates correctly.
var rows = db.view("test/test").rows;
for (i=0; i<values.length; i++) {
T(equals(rows[i].key, values[i]));
}
- // everything has collated correctly. Now to check the descending output
+ // Confirm that couch allows raw semantics in key ranges.
+ rows = db.view("test/test", {startkey:"Z", endkey:"a"}).rows;
+ TEquals(1, rows.length);
+ TEquals("a", rows[0].key);
+
+ // Check the descending output.
rows = db.view("test/test", {descending: true}).rows;
for (i=0; i<values.length; i++) {
T(equals(rows[i].key, values[values.length - 1 -i]));
diff --git a/src/couchdb/couch_btree.erl b/src/couchdb/couch_btree.erl
index 91bc8f1b..0ddf7a48 100644
--- a/src/couchdb/couch_btree.erl
+++ b/src/couchdb/couch_btree.erl
@@ -15,6 +15,7 @@
-export([open/2, open/3, query_modify/4, add/2, add_remove/3]).
-export([fold/4, full_reduce/1, final_reduce/2, foldl/3, foldl/4]).
-export([fold_reduce/4, lookup/2, get_state/1, set_options/2]).
+-export([less/3]).
-define(CHUNK_THRESHOLD, 16#4ff).
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 71204598..db430bbd 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -477,6 +477,7 @@ db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) ->
db_attachment_req(Req, Db, DocId, FileNameParts).
all_docs_view(Req, Db, Keys) ->
+ RawCollator = fun(A, B) -> A < B end,
#view_query_args{
start_key = StartKey,
start_docid = StartDocId,
@@ -486,7 +487,8 @@ all_docs_view(Req, Db, Keys) ->
skip = SkipCount,
direction = Dir,
inclusive_end = Inclusive
- } = QueryArgs = couch_httpd_view:parse_view_params(Req, Keys, map),
+ } = QueryArgs
+ = couch_httpd_view:parse_view_params(Req, Keys, map, RawCollator),
{ok, Info} = couch_db:get_db_info(Db),
CurrentEtag = couch_httpd:make_etag(Info),
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 1f279417..082a5039 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -15,7 +15,7 @@
-export([handle_view_req/3,handle_temp_view_req/2]).
--export([parse_view_params/3]).
+-export([parse_view_params/4]).
-export([make_view_fold_fun/7, finish_view_fold/4, finish_view_fold/5, view_row_obj/4]).
-export([view_etag/5, make_reduce_fold_funs/6]).
-export([design_doc_view/5, parse_bool_param/1, doc_member/3]).
@@ -34,18 +34,19 @@ design_doc_view(Req, Db, DName, ViewName, Keys) ->
Reduce = get_reduce_type(Req),
Result = case couch_view:get_map_view(Db, DesignId, ViewName, Stale) of
{ok, View, Group} ->
- QueryArgs = parse_view_params(Req, Keys, map),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
{not_found, Reason} ->
case couch_view:get_reduce_view(Db, DesignId, ViewName, Stale) of
{ok, ReduceView, Group} ->
+ Collator = view_collator(ReduceView),
case Reduce of
false ->
- QueryArgs = parse_view_params(Req, Keys, red_map),
+ QueryArgs = parse_view_params(Req, Keys, red_map, Collator),
MapView = couch_view:extract_map_view(ReduceView),
output_map_view(Req, MapView, Group, Db, QueryArgs, Keys);
_ ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
+ QueryArgs = parse_view_params(Req, Keys, reduce, Collator),
output_reduce_view(Req, Db, ReduceView, Group, QueryArgs, Keys)
end;
_ ->
@@ -90,19 +91,19 @@ handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
Reduce = get_reduce_type(Req),
case couch_util:get_value(<<"reduce">>, Props, null) of
null ->
- QueryArgs = parse_view_params(Req, Keys, map),
{ok, View, Group} = couch_view:get_temp_map_view(Db, Language,
DesignOptions, MapSrc),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
_ when Reduce =:= false ->
- QueryArgs = parse_view_params(Req, Keys, red_map),
{ok, View, Group} = couch_view:get_temp_map_view(Db, Language,
DesignOptions, MapSrc),
+ QueryArgs = parse_view_params(Req, Keys, red_map, view_collator(View)),
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
RedSrc ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
{ok, View, Group} = couch_view:get_temp_reduce_view(Db, Language,
DesignOptions, MapSrc, RedSrc),
+ QueryArgs = parse_view_params(Req, Keys, reduce, view_collator(View)),
output_reduce_view(Req, Db, View, Group, QueryArgs, Keys)
end;
@@ -209,18 +210,19 @@ load_view(Req, Db, {ViewDesignId, ViewName}, Keys) ->
Reduce = get_reduce_type(Req),
case couch_view:get_map_view(Db, ViewDesignId, ViewName, Stale) of
{ok, View, Group} ->
- QueryArgs = parse_view_params(Req, Keys, map),
+ QueryArgs = parse_view_params(Req, Keys, map, view_collator(View)),
{map, View, Group, QueryArgs};
{not_found, _Reason} ->
case couch_view:get_reduce_view(Db, ViewDesignId, ViewName, Stale) of
{ok, ReduceView, Group} ->
+ Collator = view_collator(ReduceView),
case Reduce of
false ->
- QueryArgs = parse_view_params(Req, Keys, map_red),
+ QueryArgs = parse_view_params(Req, Keys, map_red, Collator),
MapView = couch_view:extract_map_view(ReduceView),
{map, MapView, Group, QueryArgs};
_ ->
- QueryArgs = parse_view_params(Req, Keys, reduce),
+ QueryArgs = parse_view_params(Req, Keys, reduce, Collator),
{reduce, ReduceView, Group, QueryArgs}
end;
{not_found, Reason} ->
@@ -228,12 +230,30 @@ load_view(Req, Db, {ViewDesignId, ViewName}, Keys) ->
end
end.
+view_collator({reduce, _N, _Lang, View}) ->
+ view_collator(View);
+
+view_collator({temp_reduce, View}) ->
+ view_collator(View);
+
+view_collator(#view{btree=Btree}) ->
+ % Return an "is-less-than" predicate by calling into the btree's
+ % collator. For raw collation, couch_btree compares arbitrary
+ % Erlang terms, but for normal (ICU) collation, it expects
+ % {Json, Id} tuples.
+ fun
+ ({_JsonA, _IdA}=A, {_JsonB, _IdB}=B) ->
+ couch_btree:less(Btree, A, B);
+ (JsonA, JsonB) ->
+ couch_btree:less(Btree, {JsonA, null}, {JsonB, null})
+ end.
+
% query_parse_error could be removed
% we wouldn't need to pass the view type, it'd just parse params.
% I'm not sure what to do about the error handling, but
% it might simplify things to have a parse_view_params function
% that doesn't throw().
-parse_view_params(Req, Keys, ViewType) ->
+parse_view_params(Req, Keys, ViewType, LessThan) ->
QueryList = couch_httpd:qs(Req),
QueryParams =
lists:foldl(fun({K, V}, Acc) ->
@@ -247,7 +267,7 @@ parse_view_params(Req, Keys, ViewType) ->
QueryArgs = lists:foldl(fun({K, V}, Args2) ->
validate_view_query(K, V, Args2)
end, Args, lists:reverse(QueryParams)), % Reverse to match QS order.
- warn_on_empty_key_range(QueryArgs),
+ warn_on_empty_key_range(QueryArgs, LessThan),
GroupLevel = QueryArgs#view_query_args.group_level,
case {ViewType, GroupLevel, IsMultiGet} of
{reduce, exact, true} ->
@@ -328,15 +348,15 @@ parse_view_param("callback", _) ->
parse_view_param(Key, Value) ->
[{extra, {Key, Value}}].
-warn_on_empty_key_range(#view_query_args{start_key=undefined}) ->
+warn_on_empty_key_range(#view_query_args{start_key=undefined}, _Lt) ->
ok;
-warn_on_empty_key_range(#view_query_args{end_key=undefined}) ->
+warn_on_empty_key_range(#view_query_args{end_key=undefined}, _Lt) ->
ok;
-warn_on_empty_key_range(#view_query_args{start_key=A, end_key=A}) ->
+warn_on_empty_key_range(#view_query_args{start_key=A, end_key=A}, _Lt) ->
ok;
warn_on_empty_key_range(#view_query_args{
- start_key=StartKey, end_key=EndKey, direction=Dir}) ->
- case {Dir, couch_view:less_json(StartKey, EndKey)} of
+ start_key=StartKey, end_key=EndKey, direction=Dir}, LessThan) ->
+ case {Dir, LessThan(StartKey, EndKey)} of
{fwd, false} ->
throw({query_parse_error,
<<"No rows can match your key range, reverse your ",