You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@couchdb.apache.org by "Joan Touzet (JIRA)" <ji...@apache.org> on 2017/10/13 22:15:00 UTC
[jira] [Closed] (COUCHDB-3393) Function lookup() - script can ask
for documents!
[ https://issues.apache.org/jira/browse/COUCHDB-3393?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Joan Touzet closed COUCHDB-3393.
--------------------------------
Resolution: Won't Fix
> Function lookup() - script can ask for documents!
> -------------------------------------------------
>
> Key: COUCHDB-3393
> URL: https://issues.apache.org/jira/browse/COUCHDB-3393
> Project: CouchDB
> Issue Type: New Feature
> Components: Database Core
> Reporter: Ondřej Novák
>
> This is not just a feature request, this is proof of concept. Working with query servers, i wondered why the functions list() and show() cannot asks for additional documents. Sticking with one document (show) or one view(list) very limiting these rendering functions.
> CouchDB also doesn't support join operation with more then one reference hop and only with include_docs (which is not supported for reduce). Giving to the script some lookup function can break many these limitations (INCLUDING REDUCED RESULTS!)
> After some research, i found a way, how the query server can ask the the database for the documents. So I downloaded couchdb sources and tried to implement my idea. And it worked.
> From the javascript, there is a new function available for the Render section (show, list, update).
> {noformat}
> function lookup(docs[]) -> array
> {noformat}
> The function accepts list of doc-ids. It returns whole document for every doc-id in the array. Asking for non-existing documen, or deleted document causes that null is returned.
> How is this implemented?
> The query server can return a command instead of a regular response.
> {noformat}
> ["lookup", [docs]]
> {noformat}
> The couchdb performs lookup for the specified documents and returns them through the proc_prompt back to the query server. Then the couchdb repeats the waiting for the regular response. The above situation can repeat as many times as needed.
> The following log was created on my development version of couchdb. You can see "lookup" function in the action.
> {noformat}
> [debug] [<0.163.0>] OS Process #Port<0.3023> Input :: ["ddoc","_design/example",["shows","test"],[null,{..}]]
> [debug] [<0.163.0>] OS Process #Port<0.3023> Output :: ["lookup",["doc1","doc2","doc3"]]
> [debug] [<0.163.0>] OS Process #Port<0.3023> Input :: [true,[{"_id":"doc1","_rev":"1-7ef1a0ad6b5aae5c716ed7969c87fe97","payload":"aaa"},null,null]]
> [debug] [<0.163.0>] OS Process #Port<0.3023> Output :: ["resp",{"body":"[{\"_id\":\"doc1\",\"_rev\":\"1-7ef1a0ad6b5aae5c716ed7969c87fe97\",\"payload\":\"aaa\"},null,null]"}]
> {noformat}
> I hope this new feature can improve scripting a lot!
> I have implemented this feature in the branch 1.6.x, because i have this version in the production. You can find the patch below. It would be probably easy to port the patch to the master branch.
> I would appreciate if somebody look at this concept and perhaps improve my implementation and include it to the future version.
> Diff to 1.6.x
> {noformat}
> diff --git a/share/server/loop.js b/share/server/loop.js
> index 644d89b..886afb3 100644
> --- a/share/server/loop.js
> +++ b/share/server/loop.js
> @@ -28,6 +28,7 @@ function init_sandbox() {
> sandbox.send = Render.send;
> sandbox.getRow = Render.getRow;
> sandbox.isArray = isArray;
> + sandbox.lookup = Render.lookup;
> } catch (e) {
> //log(e.toSource());
> }
> diff --git a/share/server/render.js b/share/server/render.js
> index 49b0863..3384fd0 100644
> --- a/share/server/render.js
> +++ b/share/server/render.js
> @@ -195,6 +195,18 @@ var Render = (function() {
> return json[1];
> };
>
> +
> + function lookup(docs) {
> + respond(["lookup", docs]);
> + var json = JSON.parse(readline());
> + if (json[0] == true) {
> + return json[1];
> + } else {
> + return null;
> + }
> + };
> +
> +
>
> function maybeWrapResponse(resp) {
> var type = typeof resp;
> @@ -337,6 +349,7 @@ var Render = (function() {
> start : start,
> send : send,
> getRow : getRow,
> + lookup: lookup,
> show : function(fun, ddoc, args) {
> // var showFun = Couch.compileFunction(funSrc);
> runShow(fun, ddoc, args);
> diff --git a/src/couch_mrview/src/couch_mrview_show.erl b/src/couch_mrview/src/couch_mrview_show.erl
> index f8fa837..fe1eaca 100644
> --- a/src/couch_mrview/src/couch_mrview_show.erl
> +++ b/src/couch_mrview/src/couch_mrview_show.erl
> @@ -86,8 +86,7 @@ handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId) ->
> couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
> JsonReq = couch_httpd_external:json_req_obj(Req, Db, DocId),
> JsonDoc = couch_query_servers:json_doc(Doc),
> - [<<"resp">>, ExternalResp] =
> - couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName],
> + [<<"resp">>, ExternalResp] = couch_query_servers:ddoc_prompt_wlookup(Db,DDoc, [<<"shows">>, ShowName],
> [JsonDoc, JsonReq]),
> JsonResp = apply_etag(ExternalResp, CurrentEtag),
> couch_httpd_external:send_external_response(Req, JsonResp)
> @@ -132,7 +131,7 @@ send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) ->
> JsonReq = couch_httpd_external:json_req_obj(Req, Db, DocId),
> JsonDoc = couch_query_servers:json_doc(Doc),
> Cmd = [<<"updates">>, UpdateName],
> - UpdateResp = couch_query_servers:ddoc_prompt(DDoc, Cmd, [JsonDoc, JsonReq]),
> + UpdateResp = couch_query_servers:ddoc_prompt_wlookup(Db,DDoc, Cmd, [JsonDoc, JsonReq]),
> JsonResp = case UpdateResp of
> [<<"up">>, {NewJsonDoc}, {JsonResp0}] ->
> case couch_httpd:header_value(
> @@ -238,13 +237,13 @@ list_cb({row, Row}, #lacc{code=undefined} = Acc) ->
> list_cb({row, Row}, Acc) ->
> send_list_row(Row, Acc);
> list_cb(complete, Acc) ->
> - #lacc{qserver = {Proc, _}, resp = Resp0} = Acc,
> + #lacc{qserver = {Proc, _}, resp = Resp0, db = Db} = Acc,
> if Resp0 =:= nil ->
> {ok, #lacc{resp = Resp}} = start_list_resp({[]}, Acc);
> true ->
> Resp = Resp0
> end,
> - case couch_query_servers:proc_prompt(Proc, [<<"list_end">>]) of
> + case couch_query_servers:proc_prompt_wlookup(Db, Proc, [<<"list_end">>]) of
> [<<"end">>, Data, Headers] ->
> Acc2 = fixup_headers(Headers, Acc#lacc{resp=Resp}),
> #lacc{resp = Resp2} = send_non_empty_chunk(Acc2, Data);
> @@ -258,7 +257,7 @@ start_list_resp(Head, Acc) ->
> #lacc{db=Db, req=Req, qserver=QServer, lname=LName} = Acc,
> JsonReq = couch_httpd_external:json_req_obj(Req, Db),
>
> - [<<"start">>,Chunk,JsonResp] = couch_query_servers:ddoc_proc_prompt(QServer,
> + [<<"start">>,Chunk,JsonResp] = couch_query_servers:ddoc_proc_prompt_wlookup(Db, QServer,
> [<<"lists">>, LName], [Head, JsonReq]),
> Acc2 = send_non_empty_chunk(fixup_headers(JsonResp, Acc), Chunk),
> {ok, Acc2}.
> @@ -273,7 +272,7 @@ fixup_headers(Headers, #lacc{etag=ETag} = Acc) ->
> Headers3 = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
> Acc#lacc{code=Code, headers=Headers3}.
>
> -send_list_row(Row, #lacc{qserver = {Proc, _}, resp = Resp} = Acc) ->
> +send_list_row(Row, #lacc{qserver = {Proc, _}, resp = Resp, db=Db} = Acc) ->
> RowObj = case couch_util:get_value(id, Row) of
> undefined -> [];
> Id -> [{id, Id}]
> @@ -287,7 +286,7 @@ send_list_row(Row, #lacc{qserver = {Proc, _}, resp = Resp} = Acc) ->
> undefined -> [];
> Doc -> [{doc, Doc}]
> end,
> - try couch_query_servers:proc_prompt(Proc, [<<"list_row">>, {RowObj}]) of
> + try couch_query_servers:proc_prompt_wlookup(Db,Proc, [<<"list_row">>, {RowObj}]) of
> [<<"chunks">>, Chunk, Headers] ->
> Acc2 = send_non_empty_chunk(fixup_headers(Headers, Acc), Chunk),
> {ok, Acc2};
> diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl
> index 3b58cbe..959fd41 100644
> --- a/src/couchdb/couch_query_servers.erl
> +++ b/src/couchdb/couch_query_servers.erl
> @@ -22,6 +22,7 @@
> -export([filter_view/3]).
>
> -export([with_ddoc_proc/2, proc_prompt/2, ddoc_prompt/3, ddoc_proc_prompt/3, json_doc/1]).
> +-export([proc_prompt_wlookup/3, ddoc_prompt_wlookup/4, ddoc_proc_prompt_wlookup/4]).
>
> % For 210-os-proc-pool.t
> -export([get_os_process/1, ret_os_process/1]).
> @@ -237,6 +238,10 @@ json_doc(nil) -> null;
> json_doc(Doc) ->
> couch_doc:to_json_obj(Doc, [revs]).
>
> +json_doc(nil, Options) -> null;
> +json_doc(Doc, Options) ->
> + couch_doc:to_json_obj(Doc, Options).
> +
> filter_view(DDoc, VName, Docs) ->
> JsonDocs = [couch_doc:to_json_obj(Doc, [revs]) || Doc <- Docs],
> [true, Passes] = ddoc_prompt(DDoc, [<<"views">>, VName, <<"map">>], [JsonDocs]),
> @@ -257,6 +262,14 @@ filter_docs(Req, Db, DDoc, FName, Docs) ->
> ddoc_proc_prompt({Proc, DDocId}, FunPath, Args) ->
> proc_prompt(Proc, [<<"ddoc">>, DDocId, FunPath, Args]).
>
> +ddoc_proc_prompt_wlookup(Db, {Proc, DDocId}, FunPath, Args) ->
> + proc_prompt_wlookup(Db, Proc, [<<"ddoc">>, DDocId, FunPath, Args]).
> +
> +ddoc_prompt_wlookup(Db, DDoc, FunPath, Args) ->
> + with_ddoc_proc(DDoc, fun({Proc, DDocId}) ->
> + proc_prompt_wlookup(Db, Proc, [<<"ddoc">>, DDocId, FunPath, Args])
> + end).
> +
> ddoc_prompt(DDoc, FunPath, Args) ->
> with_ddoc_proc(DDoc, fun({Proc, DDocId}) ->
> proc_prompt(Proc, [<<"ddoc">>, DDocId, FunPath, Args])
> @@ -507,6 +520,25 @@ proc_with_ddoc(DDoc, DDocKey, LangProcs) ->
> {ok, SmartProc}
> end.
>
> +maybe_open_doc(Db, DocId) ->
> + case catch couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
> + #doc{} = Doc -> Doc;
> + {not_found, _} -> nil
> + end.
> +
> +
> +do_lookup(Db, DocList) ->
> + lists:map(fun(X) -> json_doc(maybe_open_doc(Db, X),[]) end, DocList ).
> +
> +proc_prompt_wlookup(Db, Proc, Args) ->
> + case proc_prompt(Proc,Args) of
> + [<<"lookup">>, DocList] ->
> + proc_prompt_wlookup(Db,Proc,[true, do_lookup(Db, DocList)]);
> + Other ->
> + Other
> + end.
> +
> +
> proc_prompt(Proc, Args) ->
> case proc_prompt_raw(Proc, Args) of
> {json, Json} ->
> {noformat}
--
This message was sent by Atlassian JIRA
(v6.4.14#64029)