You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2008/08/16 23:01:22 UTC
svn commit: r686560 - in /incubator/couchdb/branches/runtimeconfig: ./
CHANGES Makefile.am README configure.ac share/www/script/couch_tests.js
src/couchdb/couch_btree.erl src/couchdb/couch_db.hrl
src/couchdb/couch_view.erl
Author: jan
Date: Sat Aug 16 14:01:18 2008
New Revision: 686560
URL: http://svn.apache.org/viewvc?rev=686560&view=rev
Log:
Merged revisions 685963,685968,685975,686007,686011,686014,686383-686385 via svnmerge from
https://svn.apache.org/repos/asf/incubator/couchdb/trunk
Modified:
incubator/couchdb/branches/runtimeconfig/ (props changed)
incubator/couchdb/branches/runtimeconfig/CHANGES
incubator/couchdb/branches/runtimeconfig/Makefile.am
incubator/couchdb/branches/runtimeconfig/README
incubator/couchdb/branches/runtimeconfig/configure.ac
incubator/couchdb/branches/runtimeconfig/share/www/script/couch_tests.js
incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_btree.erl
incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_db.hrl
incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_view.erl
Propchange: incubator/couchdb/branches/runtimeconfig/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Sat Aug 16 14:01:18 2008
@@ -1 +1 @@
-/incubator/couchdb/trunk:1-685172
+/incubator/couchdb/trunk:1-686559
Modified: incubator/couchdb/branches/runtimeconfig/CHANGES
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/CHANGES?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/CHANGES (original)
+++ incubator/couchdb/branches/runtimeconfig/CHANGES Sat Aug 16 14:01:18 2008
@@ -1,6 +1,14 @@
Apache CouchDB CHANGES
======================
+Version 0.9.0-incubating
+------------------------
+
+Packaging and System Integration:
+
+ * Manually linked `libm` for portability with FreeBSD 7.0.
+ * Extended default library paths in `configure.ac` file.
+
Version 0.8.1-incubating
------------------------
@@ -31,13 +39,12 @@
SpiderMonkey 1.9.
* Improve error handling for undefined values emitted by map functions.
(COUCHDB-83)
-
Packaging and System Integration:
* The `couchdb` script no longer uses `awk` for configuration checks as this
was causing portability problems.
- * Updated `sudo` example in the README to use the `-i` option, this fixes
+ * Updated `sudo` example in the `README` to use the `-i` option, this fixes
problems when invoking from a directory the `couchdb` user cannot access.
Futon Utility Client:
@@ -57,53 +64,53 @@
and old document revisions.
* Support for incremental map/reduce views has been added.
* To support map/reduce, the structure of design documents has changed. View
- values are now JSON objects containing at least a "map" member, and
- optionally a "reduce" member.
- * View servers are now identified by name (for example "javascript") instead of
- by MIME type.
+ values are now JSON objects containing at least a `map` member, and
+ optionally a `reduce` member.
+ * View servers are now identified by name (for example `javascript`) instead of
+ by media type.
* Automatically generated document IDs are now based on proper UUID generation
using the crypto module.
- * The field "content-type" in the JSON representation of attachments has been
- renamed to "content_type" (underscore).
+ * The field `content-type` in the JSON representation of attachments has been
+ renamed to `content_type` (underscore).
HTTP Interface:
* CouchDB now uses MochiWeb instead of inets for the HTTP server
implementation. Among other things, this means that the extra configuration
- files needed for inets (such as couch_httpd.conf) are no longer used.
- * The HTTP interface now completely supports the HEAD method. (COUCHDB-3)
- * Improved compliance of Etag handling with the HTTP specification.
+ files needed for inets (such as `couch_httpd.conf`) are no longer used.
+ * The HTTP interface now completely supports the `HEAD` method. (COUCHDB-3)
+ * Improved compliance of `Etag` handling with the HTTP specification.
(COUCHDB-13)
- * Etags are no longer included in responses to document GET requests that
+ * Etags are no longer included in responses to document `GET` requests that
include query string parameters causing the JSON response to change without
the revision or the URI having changed.
* The bulk document update API has changed slightly on both the request and the
response side. In addition, bulk updates are now atomic.
- * CouchDB now uses TCP_NODELAY to fix performance problems with persistent
+ * CouchDB now uses `TCP_NODELAY` to fix performance problems with persistent
connections on some platforms due to nagling.
* Including a `?descending=false` query string parameter in requests to views
no longer raises an error.
* Requests to unknown top-level reserved URLs (anything with a leading
- underscore) now return a "unknown_private_path" error instead of the
- confusing "illegal_database_name".
+ underscore) now return a `unknown_private_path` error instead of the
+ confusing `illegal_database_name`.
* The Temporary view handling now expects a JSON request body, where the JSON
- is an object with at least a "map" member, and optional "reduce" and
- "language" members.
+ is an object with at least a `map` member, and optional `reduce` and
+ `language` members.
* Temporary views no longer determine the view server based on the Content-Type
- header of the POST request, but rather by looking for a "language" member in
- the JSON body of the request.
- * The status code of responses to DELETE requests is now 200 to reflect that
+ header of the `POST` request, but rather by looking for a `language` member
+ in the JSON body of the request.
+ * The status code of responses to `DELETE` requests is now 200 to reflect that
that the deletion is performed synchronously.
Javascript View Server:
* SpiderMonkey is no longer included with CouchDB, but rather treated as a
- normal external dependency. A simple C program (_couchjs) is provided that
+ normal external dependency. A simple C program (`_couchjs`) is provided that
links against an existing SpiderMonkey installation and uses the interpreter
embedding API.
* View functions using the default Javascript view server can now do logging
using the global `log(message)` function. Log messages are directed into the
- CouchDB log at INFO level. (COUCHDB-59)
+ CouchDB log at `INFO` level. (COUCHDB-59)
* The global `map(key, value)` function made available to view code has been
renamed to `emit(key, value)`.
* Fixed handling of exceptions raised by view functions.
@@ -130,10 +137,10 @@
* Futon now uses the XMLHTTPRequest API asynchronously to communicate with the
CouchDB HTTP server, so that most operations no longer block the browser.
* View results sorting can now be switched between ascending and descending by
- clicking on the "Key" column header.
- * Fixed a bug where documents that contained a "@" character could not be
+ clicking on the `Key` column header.
+ * Fixed a bug where documents that contained a `@` character could not be
viewed. (COUCHDB-12)
- * The database page now provides a "Compact" button to trigger database
+ * The database page now provides a `Compact` button to trigger database
compaction. (COUCHDB-38)
* Fixed portential double encoding of document IDs and other URI segments in
many instances. (COUCHDB-39)
Modified: incubator/couchdb/branches/runtimeconfig/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/Makefile.am?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/Makefile.am (original)
+++ incubator/couchdb/branches/runtimeconfig/Makefile.am Sat Aug 16 14:01:18 2008
@@ -36,18 +36,28 @@
THANKS.gz: $(top_srcdir)/THANKS
-gzip -9 < $< > $@
+<<<<<<< .working
bootstrap-clean: maintainer-clean
- @echo "This command is intended for maintainers to use;"
- @echo "it deletes files that may need special tools to rebuild."
- rm -f INSTALL
- rm -f acinclude.m4
- rm -f aclocal.m4
- rm -fr build-aux
- rm -f config.h.in*
- rm -f configure
- rm -f configure~
- rm -f *.tar.gz
- rm -fr m4
- find . -name Makefile.in | xargs rm
-
+=======
.PHONY: local-clean
+local-clean: maintainer-clean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may need special tools to rebuild."
+ -rm -f INSTALL
+ -rm -f acinclude.m4
+ -rm -f aclocal.m4
+ -rm -f config.h.in
+ -rm -f configure
+ -rm -f *.tar.gz
+ -rm -f *.tar.gz.asc
+ -rm -f *.tar.gz.md5
+ -rm -f *.tar.gz.sha
+ -rm -fr build-aux
+ -rm -fr m4
+ -find $(top_srcdir) -name Makefile.in | xargs rm
+
+.PHONY: distsign
+distsign: distcheck
+ gpg --armor --detach-sig < $(distdir).tar.gz > $(distdir).tar.gz.asc
+ md5sum $(distdir).tar.gz > $(distdir).tar.gz.md5
+ sha1sum $(distdir).tar.gz > $(distdir).tar.gz.sha
Modified: incubator/couchdb/branches/runtimeconfig/README
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/README?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/README (original)
+++ incubator/couchdb/branches/runtimeconfig/README Sat Aug 16 14:01:18 2008
@@ -124,6 +124,9 @@
system owned directory. You do not need to do this if you are installing
elsewhere, such as your home directory.
+If you are having problems running `make` you may want to try running `gmake` if
+it is available on your system.
+
More options can be found by reading the `INSTALL` file.
#### Security Considerations ####
Modified: incubator/couchdb/branches/runtimeconfig/configure.ac
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/configure.ac?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/configure.ac (original)
+++ incubator/couchdb/branches/runtimeconfig/configure.ac Sat Aug 16 14:01:18 2008
@@ -59,24 +59,28 @@
AC_ARG_WITH([js-include], [AC_HELP_STRING([--with-js-include=PATH],
[set PATH to the SpiderMonkey include directory])], [
- JS_INCLUDE_FLAGS="-I$withval"
+ JS_FLAGS="-I$withval"
], [
- JS_INCLUDE_FLAGS="-I/usr/include/js"
- JS_INCLUDE_FLAGS="$JS_INCLUDE_FLAGS -I/usr/include/mozjs"
- JS_INCLUDE_FLAGS="$JS_INCLUDE_FLAGS -I/usr/local/include/js"
- JS_INCLUDE_FLAGS="$JS_INCLUDE_FLAGS -I/opt/local/include/js"
+ JS_FLAGS="-I/usr/include"
+ JS_FLAGS="$JS_FLAGS -I/usr/include/js"
+ JS_FLAGS="$JS_FLAGS -I/usr/include/mozjs"
+ JS_FLAGS="$JS_FLAGS -I/usr/local/include"
+ JS_FLAGS="$JS_FLAGS -I/opt/local/include"
+ JS_FLAGS="$JS_FLAGS -I/usr/local/include/js"
+ JS_FLAGS="$JS_FLAGS -I/opt/local/include/js"
])
AC_ARG_WITH([js-lib], [AC_HELP_STRING([--with-js-lib=PATH],
[set PATH to the SpiderMonkey library directory])],
[JS_LIB_FLAGS="-L$withval"], [])
-# XP_UNIX required for jsapi.h and has been tested to work on Linux and Darwin.
LIB_FLAGS="-L/usr/local/lib -L/opt/local/lib $JS_LIB_FLAGS"
LIBS="$LIB_FLAGS $LIBS"
-FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_INCLUDE_FLAGS -DXP_UNIX $FLAGS"
+# XP_UNIX required for jsapi.h and has been tested to work on Linux and Darwin.
+FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_FLAGS -DXP_UNIX $FLAGS"
CPPFLAGS="$FLAGS $CPPFLAGS"
-LDFLAGS="$FLAGS $LDFLAGS"
+# manually linking libm is requred for FreeBSD 7.0
+LDFLAGS="$FLAGS -lm $LDFLAGS"
AC_CHECK_LIB([js], [JS_NewContext], [], [
AC_CHECK_LIB([mozjs], [JS_NewContext], [], [
@@ -87,7 +91,7 @@
AC_CHECK_HEADER([jsapi.h], [], [
AC_MSG_ERROR([Could not find the jsapi header.
-Are Mozilla SpiderMonkey headers installed?])])
+Are the Mozilla SpiderMonkey headers installed?])])
AC_CHECK_ICU([3])
Modified: incubator/couchdb/branches/runtimeconfig/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/share/www/script/couch_tests.js?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/share/www/script/couch_tests.js [utf-8] (original)
+++ incubator/couchdb/branches/runtimeconfig/share/www/script/couch_tests.js [utf-8] Sat Aug 16 14:01:18 2008
@@ -363,29 +363,30 @@
T(db.bulkSave(docs).ok);
var summate = function(N) {return (N+1)*N/2;};
- var map = function (doc) {emit(doc.integer, doc.integer)};
+ var map = function (doc) {
+ emit(doc.integer, doc.integer);
+ emit(doc.integer, doc.integer)};
var reduce = function (keys, values) { return sum(values); };
var result = db.query(map, reduce);
- T(result.rows[0].value == summate(numDocs));
+ T(result.rows[0].value == 2*summate(numDocs));
result = db.query(map, reduce, {startkey: 4, endkey: 4});
- T(result.rows[0].value == 4);
+ T(result.rows[0].value == 8);
result = db.query(map, reduce, {startkey: 4, endkey: 5});
- T(result.rows[0].value == 9);
+ T(result.rows[0].value == 18);
result = db.query(map, reduce, {startkey: 4, endkey: 6});
- T(result.rows[0].value == 15);
+ T(result.rows[0].value == 30);
result = db.query(map, reduce, {group:true, count:3});
- T(result.rows.length == 3);
- T(result.rows[0].value == 1);
- T(result.rows[1].value == 2);
- T(result.rows[2].value == 3);
+ T(result.rows[0].value == 2);
+ T(result.rows[1].value == 4);
+ T(result.rows[2].value == 6);
for(var i=1; i<numDocs/2; i+=30) {
result = db.query(map, reduce, {startkey: i, endkey: numDocs - i});
- T(result.rows[0].value == summate(numDocs-i) - summate(i-1));
+ T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1)));
}
db.deleteDb();
@@ -799,7 +800,7 @@
_id:"_design/test",
language: "javascript",
views: {
- all_docs: {map: "function(doc) { emit(doc.integer, null) }"},
+ all_docs: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
no_docs: {map: "function(doc) {}"},
single_doc: {map: "function(doc) { if (doc._id == \"1\") { emit(1, null) }}"},
summate: {map:"function (doc) {emit(doc.integer, doc.integer)};",
@@ -815,9 +816,11 @@
T(db.bulkSave(makeDocs(1, numDocs + 1)).ok);
for (var loop = 0; loop < 2; loop++) {
+ if (db.view("test/all_docs") == null) throw "fuck";
var rows = db.view("test/all_docs").rows;
- for (var i = 1; i <= numDocs; i++) {
- T(rows[i-1].key == i);
+ for (var i = 0; i < numDocs; i++) {
+ T(rows[2*i].key == i+1);
+ T(rows[(2*i)+1].key == i+1);
}
T(db.view("test/no_docs").total_rows == 0)
T(db.view("test/single_doc").total_rows == 1)
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_btree.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_btree.erl?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_btree.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_btree.erl Sat Aug 16 14:01:18 2008
@@ -14,7 +14,8 @@
-export([open/2, open/3, query_modify/4, add/2, add_remove/3, foldl/3, foldl/4]).
-export([foldr/3, foldr/4, fold/4, fold/5, full_reduce/1, final_reduce/2]).
--export([fold_reduce/7, lookup/2, get_state/1, set_options/2, test/1, test/0]).
+-export([fold_reduce/7, lookup/2, get_state/1, set_options/2]).
+-export([test/1, test/0, test_remove/2, test_add/2]).
-define(CHUNK_THRESHOLD, 16#4ff).
@@ -180,49 +181,52 @@
{NodeType, NodeList} = get_node(Bt, Pointer),
case NodeType of
kp_node ->
- lookup_kpnode(Bt, NodeList, Keys, []);
+ lookup_kpnode(Bt, list_to_tuple(NodeList), 1, Keys, []);
kv_node ->
- lookup_kvnode(Bt, NodeList, Keys, [])
+ lookup_kvnode(Bt, list_to_tuple(NodeList), 1, Keys, [])
end.
-lookup_kpnode(_Bt, [], Keys, Output) ->
- {ok, lists:reverse(Output, [{Key, not_found} || Key <- Keys])};
-lookup_kpnode(_Bt, _KPs, [], Output) ->
+lookup_kpnode(_Bt, _NodeTuple, _LowerBound, [], Output) ->
{ok, lists:reverse(Output)};
+
+lookup_kpnode(_Bt, NodeTuple, LowerBound, Keys, Output) when size(NodeTuple) < LowerBound ->
+ {ok, lists:reverse(Output, [{Key, not_found} || Key <- Keys])};
-lookup_kpnode(Bt, [{Key, PointerInfo} | RestKPs], LookupKeys, Output) ->
- % Split the Keys into two lists, queries of values less
- % than equals, and greater than the current key
+lookup_kpnode(Bt, NodeTuple, LowerBound, [FirstLookupKey | _] = LookupKeys, Output) ->
+ N = find_first_gteq(Bt, NodeTuple, LowerBound, size(NodeTuple), FirstLookupKey),
+ {Key, PointerInfo} = element(N, NodeTuple),
SplitFun = fun(LookupKey) -> not less(Bt, Key, LookupKey) end,
case lists:splitwith(SplitFun, LookupKeys) of
{[], GreaterQueries} ->
- lookup_kpnode(Bt, RestKPs, GreaterQueries, Output);
+ lookup_kpnode(Bt, NodeTuple, N + 1, GreaterQueries, Output);
{LessEqQueries, GreaterQueries} ->
{ok, Results} = lookup(Bt, PointerInfo, LessEqQueries),
- lookup_kpnode(Bt, RestKPs, GreaterQueries, lists:reverse(Results, Output))
+ lookup_kpnode(Bt, NodeTuple, N + 1, GreaterQueries, lists:reverse(Results, Output))
end.
-
-lookup_kvnode(_Bt, _KVs, [], Output) ->
+lookup_kvnode(_Bt, _NodeTuple, _LowerBound, [], Output) ->
{ok, lists:reverse(Output)};
-lookup_kvnode(_Bt, [], Keys, Output) ->
+lookup_kvnode(_Bt, NodeTuple, LowerBound, Keys, Output) when size(NodeTuple) < LowerBound ->
% keys not found
{ok, lists:reverse(Output, [{Key, not_found} || Key <- Keys])};
-lookup_kvnode(Bt, [{Key, Value} | RestKVs], [LookupKey | RestLookupKeys], Output) ->
+lookup_kvnode(Bt, NodeTuple, LowerBound, [LookupKey | RestLookupKeys], Output) ->
+ N = find_first_gteq(Bt, NodeTuple, LowerBound, size(NodeTuple), LookupKey),
+ {Key, Value} = element(N, NodeTuple),
case less(Bt, LookupKey, Key) of
true ->
- lookup_kvnode(Bt, [{Key, Value} | RestKVs], RestLookupKeys, [{LookupKey, not_found} | Output]);
+ % LookupKey is less than Key
+ lookup_kvnode(Bt, NodeTuple, N, RestLookupKeys, [{LookupKey, not_found} | Output]);
false ->
case less(Bt, Key, LookupKey) of
true ->
% LookupKey is greater than Key
- lookup_kvnode(Bt, RestKVs, [LookupKey | RestLookupKeys], Output);
+ lookup_kvnode(Bt, NodeTuple, N+1, RestLookupKeys, [{LookupKey, not_found} | Output]);
false ->
% LookupKey is equal to Key
- lookup_kvnode(Bt, RestKVs, RestLookupKeys, [{LookupKey, {ok, assemble(Bt, LookupKey, Value)}} | Output])
+ lookup_kvnode(Bt, NodeTuple, N, RestLookupKeys, [{LookupKey, {ok, assemble(Bt, LookupKey, Value)}} | Output])
end
end.
@@ -272,17 +276,18 @@
{Pointer, _Reds} ->
{NodeType, NodeList} = get_node(Bt, Pointer)
end,
+ NodeTuple = list_to_tuple(NodeList),
case NodeType of
kp_node ->
- {ok, NewNodeList, QueryOutput2, Bt2} = modify_kpnode(Bt, NodeList, Actions, [], QueryOutput);
+ {ok, NewNodeList, QueryOutput2, Bt2} = modify_kpnode(Bt, NodeTuple, 1, Actions, [], QueryOutput);
kv_node ->
- {ok, NewNodeList, QueryOutput2, Bt2} = modify_kvnode(Bt, NodeList, Actions, [], QueryOutput)
+ {ok, NewNodeList, QueryOutput2, Bt2} = modify_kvnode(Bt, NodeTuple, 1, Actions, [], QueryOutput)
end,
case NewNodeList of
[] -> % no nodes remain
{ok, [], QueryOutput2, Bt2};
NodeList -> % nothing changed
- {LastKey, _LastValue} = lists:last(NodeList),
+ {LastKey, _LastValue} = element(size(NodeTuple), NodeTuple),
{ok, [{LastKey, RootPointerInfo}], QueryOutput2, Bt2};
_Else2 ->
{ok, ResultList, Bt3} = write_node(Bt2, NodeType, NewNodeList),
@@ -299,17 +304,6 @@
get_node(#btree{fd = Fd}, NodePos) ->
{ok, {NodeType, NodeList}} = couch_file:pread_term(Fd, NodePos),
- case NodeType of
- kp_node ->
- % Node pointers always point backward on disk.
- % Validating this prevents infinite loops should
- % a disk corruption occur.
- [throw({error, disk_corruption})
- || {_Key, {SubNodePos, _Reds}}
- <- NodeList, SubNodePos >= NodePos];
- kv_node ->
- ok
- end,
{NodeType, NodeList}.
write_node(Bt, NodeType, NodeList) ->
@@ -326,77 +320,108 @@
ANodeList <- NodeListList
],
{ok, ResultList, Bt}.
-
-
-modify_kpnode(Bt, KPs, [], ResultNode, QueryOutput) ->
- % processed all queries for the current tree
- {ok, lists:reverse(ResultNode, KPs), QueryOutput, Bt};
-
-modify_kpnode(Bt, [], Actions, [], QueryOutput) ->
+
+modify_kpnode(Bt, {}, _LowerBound, Actions, [], QueryOutput) ->
modify_node(Bt, nil, Actions, QueryOutput);
-
-modify_kpnode(Bt, [], Actions, [{_Key, PointerInfo} | ResultNode], QueryOutput) ->
- {ok, ChildKPs, QueryOutput2, Bt2} = modify_node(Bt, PointerInfo, Actions, QueryOutput),
- {ok, lists:reverse(ResultNode, ChildKPs), QueryOutput2, Bt2};
-
-modify_kpnode(Bt, [{Key,PointerInfo} | RestKPs], Actions, ResultNode, QueryOutput) ->
- % Split the actions into two lists, queries of values <= and > than the current key
- SplitFun = fun({_ActionType, ActionKey, _ActionValue}) ->
- not less(Bt, Key, ActionKey)
- end,
- case lists:splitwith(SplitFun, Actions) of
- {[], GreaterQueries} ->
- modify_kpnode(Bt, RestKPs, GreaterQueries, [{Key, PointerInfo} | ResultNode], QueryOutput);
- {LessEqQueries, GreaterQueries} ->
- {ok, ChildKPs, QueryOutput2, Bt2} = modify_node(Bt, PointerInfo, LessEqQueries, QueryOutput),
- modify_kpnode(Bt2, RestKPs, GreaterQueries, lists:reverse(ChildKPs, ResultNode), QueryOutput2)
+modify_kpnode(Bt, NodeTuple, LowerBound, [], ResultNode, QueryOutput) ->
+ {ok, lists:reverse(ResultNode, bounded_tuple_to_list(NodeTuple, LowerBound,
+ size(NodeTuple), [])), QueryOutput, Bt};
+modify_kpnode(Bt, NodeTuple, LowerBound,
+ [{_, FirstActionKey, _}|_]=Actions, ResultNode, QueryOutput) ->
+ N = find_first_gteq(Bt, NodeTuple, LowerBound, size(NodeTuple), FirstActionKey),
+ case N == size(NodeTuple) of
+ true ->
+ % perform remaining actions on last node
+ {_, PointerInfo} = element(size(NodeTuple), NodeTuple),
+ {ok, ChildKPs, QueryOutput2, Bt2} =
+ modify_node(Bt, PointerInfo, Actions, QueryOutput),
+ NodeList = lists:reverse(ResultNode, bounded_tuple_to_list(NodeTuple, LowerBound,
+ size(NodeTuple) - 1, ChildKPs)),
+ {ok, NodeList, QueryOutput2, Bt2};
+ false ->
+ {NodeKey, PointerInfo} = element(N, NodeTuple),
+ SplitFun = fun({_ActionType, ActionKey, _ActionValue}) ->
+ not less(Bt, NodeKey, ActionKey)
+ end,
+ {LessEqQueries, GreaterQueries} = lists:splitwith(SplitFun, Actions),
+ {ok, ChildKPs, QueryOutput2, Bt2} =
+ modify_node(Bt, PointerInfo, LessEqQueries, QueryOutput),
+ ResultNode2 = lists:reverse(ChildKPs, bounded_tuple_to_revlist(NodeTuple,
+ LowerBound, N - 1, ResultNode)),
+ modify_kpnode(Bt2, NodeTuple, N+1, GreaterQueries, ResultNode2, QueryOutput2)
+ end.
+
+bounded_tuple_to_revlist(_Tuple, Start, End, Tail) when Start > End ->
+ Tail;
+bounded_tuple_to_revlist(Tuple, Start, End, Tail) ->
+ bounded_tuple_to_revlist(Tuple, Start+1, End, [element(Start, Tuple)|Tail]).
+
+bounded_tuple_to_list(Tuple, Start, End, Tail) ->
+ bounded_tuple_to_list2(Tuple, Start, End, [], Tail).
+
+bounded_tuple_to_list2(_Tuple, Start, End, Acc, Tail) when Start > End ->
+ lists:reverse(Acc, Tail);
+bounded_tuple_to_list2(Tuple, Start, End, Acc, Tail) ->
+ bounded_tuple_to_list2(Tuple, Start + 1, End, [element(Start, Tuple) | Acc], Tail).
+
+find_first_gteq(_Bt, _Tuple, Start, End, _Key) when Start == End ->
+ End;
+find_first_gteq(Bt, Tuple, Start, End, Key) ->
+ Mid = Start + ((End - Start) div 2),
+ {TupleKey, _} = element(Mid, Tuple),
+ case less(Bt, TupleKey, Key) of
+ true ->
+ find_first_gteq(Bt, Tuple, Mid+1, End, Key);
+ false ->
+ find_first_gteq(Bt, Tuple, Start, Mid, Key)
end.
-modify_kvnode(Bt, KVs, [], ResultNode, QueryOutput) ->
- {ok, lists:reverse(ResultNode, KVs), QueryOutput, Bt};
-modify_kvnode(Bt, [], [{ActionType, ActionKey, ActionValue} | RestActions], ResultNode, QueryOutput) ->
+modify_kvnode(Bt, NodeTuple, LowerBound, [], ResultNode, QueryOutput) ->
+ {ok, lists:reverse(ResultNode, bounded_tuple_to_list(NodeTuple, LowerBound, size(NodeTuple), [])), QueryOutput, Bt};
+modify_kvnode(Bt, NodeTuple, LowerBound, [{ActionType, ActionKey, ActionValue} | RestActions], ResultNode, QueryOutput) when LowerBound > size(NodeTuple) ->
case ActionType of
insert ->
- modify_kvnode(Bt, [], RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
+ modify_kvnode(Bt, NodeTuple, LowerBound, RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
remove ->
% just drop the action
- modify_kvnode(Bt, [], RestActions, ResultNode, QueryOutput);
+ modify_kvnode(Bt, NodeTuple, LowerBound, RestActions, ResultNode, QueryOutput);
fetch ->
% the key/value must not exist in the tree
- modify_kvnode(Bt, [], RestActions, ResultNode, [{not_found, {ActionKey, nil}} | QueryOutput])
+ modify_kvnode(Bt, NodeTuple, LowerBound, RestActions, ResultNode, [{not_found, {ActionKey, nil}} | QueryOutput])
end;
-modify_kvnode(Bt, [{Key, Value} | RestKVs], [{ActionType, ActionKey, ActionValue} | RestActions], ResultNode, QueryOutput) ->
+modify_kvnode(Bt, NodeTuple, LowerBound, [{ActionType, ActionKey, ActionValue} | RestActions], AccNode, QueryOutput) ->
+ N = find_first_gteq(Bt, NodeTuple, LowerBound, size(NodeTuple), ActionKey),
+ {Key, Value} = element(N, NodeTuple),
+ ResultNode = bounded_tuple_to_revlist(NodeTuple, LowerBound, N - 1, AccNode),
case less(Bt, ActionKey, Key) of
true ->
case ActionType of
insert ->
% ActionKey is less than the Key, so insert
- modify_kvnode(Bt, [{Key, Value} | RestKVs], RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
+ modify_kvnode(Bt, NodeTuple, N, RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
remove ->
% ActionKey is less than the Key, just drop the action
- modify_kvnode(Bt, [{Key, Value} | RestKVs], RestActions, ResultNode, QueryOutput);
+ modify_kvnode(Bt, NodeTuple, N, RestActions, ResultNode, QueryOutput);
fetch ->
% ActionKey is less than the Key, the key/value must not exist in the tree
- modify_kvnode(Bt, [{Key, Value} | RestKVs], RestActions, ResultNode, [{not_found, {ActionKey, nil}} | QueryOutput])
+ modify_kvnode(Bt, NodeTuple, N, RestActions, ResultNode, [{not_found, {ActionKey, nil}} | QueryOutput])
end;
false ->
+ % ActionKey and Key are maybe equal.
case less(Bt, Key, ActionKey) of
- true ->
- % ActionKey is greater than Key
- modify_kvnode(Bt, RestKVs, [{ActionType, ActionKey, ActionValue} | RestActions], [{Key, Value} | ResultNode], QueryOutput);
false ->
- % InsertKey is equal to Key
case ActionType of
insert ->
- % ActionKey is less than the Key, so insert
- modify_kvnode(Bt, RestKVs, RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
+ modify_kvnode(Bt, NodeTuple, N+1, RestActions, [{ActionKey, ActionValue} | ResultNode], QueryOutput);
remove ->
- modify_kvnode(Bt, RestKVs, RestActions, ResultNode, QueryOutput);
+ modify_kvnode(Bt, NodeTuple, N+1, RestActions, ResultNode, QueryOutput);
fetch ->
% ActionKey is equal to the Key, insert into the QueryOuput, but re-process the node
% since an identical action key can follow it.
- modify_kvnode(Bt, [{Key, Value} | RestKVs], RestActions, ResultNode, [{ok, assemble(Bt, Key, Value)} | QueryOutput])
- end
+ modify_kvnode(Bt, NodeTuple, N, RestActions, ResultNode, [{ok, assemble(Bt, Key, Value)} | QueryOutput])
+ end;
+ true ->
+ modify_kvnode(Bt, NodeTuple, N + 1, [{ActionType, ActionKey, ActionValue} | RestActions], [{Key, Value} | ResultNode], QueryOutput)
end
end.
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_db.hrl?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_db.hrl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_db.hrl Sat Aug 16 14:01:18 2008
@@ -52,7 +52,7 @@
-record(doc,
{
id = "",
- revs = [], % in the form [{RevId, IsAvailable}, ...]
+ revs = [],
% the json body object.
body = {obj, []},
Modified: incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_view.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_view.erl?rev=686560&r1=686559&r2=686560&view=diff
==============================================================================
--- incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_view.erl (original)
+++ incubator/couchdb/branches/runtimeconfig/src/couchdb/couch_view.erl Sat Aug 16 14:01:18 2008
@@ -94,6 +94,14 @@
N -> {ok, {reduce, N, Lang, View}}
end.
+expand_dups([], Acc) ->
+ lists:reverse(Acc);
+expand_dups([{Key, {dups, Vals}} | Rest], Acc) ->
+ Expanded = [{Key, Val} || Val <- Vals],
+ expand_dups(Rest, Expanded ++ Acc);
+expand_dups([KV | Rest], Acc) ->
+ expand_dups(Rest, [KV | Acc]).
+
fold_reduce({temp_reduce, #view{btree=Bt}}, Dir, StartKey, EndKey, GroupFun, Fun, Acc) ->
WrapperFun = fun({GroupedKey, _}, PartialReds, Acc0) ->
@@ -109,7 +117,7 @@
{_Name, FunSrc} = lists:nth(NthRed,RedFuns),
ReduceFun =
fun(reduce, KVs) ->
- {ok, Reduced} = couch_query_servers:reduce(Lang, [FunSrc], KVs),
+ {ok, Reduced} = couch_query_servers:reduce(Lang, [FunSrc], expand_dups(KVs, [])),
{0, PreResultPadding ++ Reduced ++ PostResultPadding};
(rereduce, Reds) ->
UserReds = [[lists:nth(NthRed, UserRedsList)] || {_, UserRedsList} <- Reds],
@@ -149,7 +157,10 @@
{Count, _} =
couch_btree:final_reduce(
fun(reduce, KVs) ->
- {length(KVs), []};
+ Count = lists:sum(
+ [case V of {dups, Vals} -> length(Vals); _ -> 1 end
+ || {_,V} <- KVs]),
+ {Count, []};
(rereduce, Reds) ->
{lists:sum([Count0 || {Count0, _} <- Reds]), []}
end, Reductions),
@@ -188,13 +199,25 @@
Group = #group{name=Id, views=Views, def_lang=Language},
Group#group{sig=erlang:md5(term_to_binary(Group))}.
-
+fold_fun(_Fun, [], _, Acc) ->
+ {ok, Acc};
+fold_fun(Fun, [KV|Rest], {KVReds, Reds}, Acc) ->
+ case Fun(KV, {KVReds, Reds}, Acc) of
+ {ok, Acc2} ->
+ fold_fun(Fun, Rest, {[KV|KVReds], Reds}, Acc2);
+ {stop, Acc2} ->
+ {stop, Acc2}
+ end.
fold(#view{btree=Btree}, Dir, Fun, Acc) ->
- {ok, _AccResult} = couch_btree:fold(Btree, Dir, Fun, Acc).
+ fold(Btree, nil, Dir, Fun, Acc).
fold(#view{btree=Btree}, StartKey, Dir, Fun, Acc) ->
- {ok, _AccResult} = couch_btree:fold(Btree, StartKey, Dir, Fun, Acc).
+ WrapperFun =
+ fun(KV, Reds, Acc2) ->
+ fold_fun(Fun, expand_dups([KV],[]), Reds, Acc2)
+ end,
+ {ok, _AccResult} = couch_btree:fold(Btree, StartKey, Dir, WrapperFun, Acc).
init([]) ->
@@ -502,8 +525,9 @@
FunSrcs = [FunSrc || {_Name, FunSrc} <- RedFuns],
ReduceFun =
fun(reduce, KVs) ->
- {ok, Reduced} = couch_query_servers:reduce(Lang, FunSrcs, KVs),
- {length(KVs), Reduced};
+ KVs2 = expand_dups(KVs,[]),
+ {ok, Reduced} = couch_query_servers:reduce(Lang, FunSrcs, KVs2),
+ {length(KVs2), Reduced};
(rereduce, Reds) ->
Count = lists:sum([Count0 || {Count0, _} <- Reds]),
UserReds = [UserRedsList || {_, UserRedsList} <- Reds],
@@ -649,8 +673,25 @@
view_insert_doc_query_results(_Doc, [], [], ViewKVsAcc, ViewIdKeysAcc) ->
{lists:reverse(ViewKVsAcc), lists:reverse(ViewIdKeysAcc)};
view_insert_doc_query_results(#doc{id=DocId}=Doc, [ResultKVs|RestResults], [{View, KVs}|RestViewKVs], ViewKVsAcc, ViewIdKeysAcc) ->
- NewKVs = [{{Key, DocId}, Value} || {Key, Value} <- ResultKVs],
- NewViewIdKeys = [{View#view.id_num, Key} || {Key, _Value} <- ResultKVs],
+ % Take any identical keys and combine the values
+ ResultKVs2 = lists:foldl(
+ fun({Key,Value}, [{PrevKey,PrevVal}|AccRest]) ->
+ case Key == PrevKey of
+ true ->
+ case PrevVal of
+ {dups, Dups} ->
+ [{PrevKey, {dups, [Value|Dups]}} | AccRest];
+ _ ->
+ [{PrevKey, {dups, [Value,PrevVal]}} | AccRest]
+ end;
+ false ->
+ [{Key,Value},{PrevKey,PrevVal}|AccRest]
+ end;
+ (KV, []) ->
+ [KV]
+ end, [], lists:sort(ResultKVs)),
+ NewKVs = [{{Key, DocId}, Value} || {Key, Value} <- ResultKVs2],
+ NewViewIdKeys = [{View#view.id_num, Key} || {Key, _Value} <- ResultKVs2],
NewViewKVsAcc = [{View, NewKVs ++ KVs} | ViewKVsAcc],
NewViewIdKeysAcc = NewViewIdKeys ++ ViewIdKeysAcc,
view_insert_doc_query_results(Doc, RestResults, RestViewKVs, NewViewKVsAcc, NewViewIdKeysAcc).
@@ -667,6 +708,7 @@
{ok, QueryServerIn}
end,
{ok, Results} = couch_query_servers:map_docs(QueryServer, Docs),
+
{Group#group{query_server=QueryServer}, Results}.