-module(couch_numidx). -behaviour(gen_server). % gen_server exports -export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). % API exports -export([get_docs/1, range_search/1]). -include("couch_db.hrl"). -define(FILENAME, "/tmp/couchdb_numidx.bin"). -record(ni_daemon, { seq = 0, ni_pos = nil, fd = nil }). get_docs(Db) -> gen_server:call(couch_numidx, {do_get_docs, Db}). range_search(Range) -> gen_server:call(couch_numidx, {do_range_search, Range}). open_file(Filename) -> case couch_file:open(Filename, [create, overwrite]) of {ok, Fd} -> {ok, Fd}; {error, Reason} -> io:format("ERROR (~s): Couldn't open file (~s) for tree storage~n", [Reason, Filename]), {error, Reason} end. start_link() -> ?LOG_DEBUG("Numidx daemon: starting link.", []), gen_server:start_link({local, couch_numidx}, couch_numidx, [], []). init([]) -> {ok, Fd} = open_file(?FILENAME), {ok, #ni_daemon{fd=Fd}}. terminate(_Reason, _Srv) -> ok. handle_call({do_get_docs, Db}, _From, State) -> {ok, _Cnt, StateNew} = couch_db:enum_docs_since(Db, State#ni_daemon.seq, fun(DocInfo, _, #ni_daemon{fd=Fd, ni_pos=Pos}=StateCur) -> {doc_info, DocId, DocSeq, _RevInfo} = DocInfo, ?LOG_DEBUG("doc-seq: ~p", [DocSeq]), {ok, Doc} = couch_db:open_doc(Db, DocInfo), {Body} = Doc#doc.body, case proplists:get_value(<<"numidx">>, Body) of undefined -> {ok, StateCur#ni_daemon{seq=DocSeq}}; Numidx -> {ok, NewPos} = numidx:add({Fd, Pos}, {DocId, Numidx}), {ok, StateCur#ni_daemon{seq=DocSeq, ni_pos=NewPos}} end end, State, []), {reply, ok, StateNew}; handle_call({do_range_search, Range}, _From, #ni_daemon{fd=Fd, ni_pos=Pos}=State) -> Result = numidx:range({Fd, Pos}, Range), {reply, Result, State}; handle_call(_Req, _From, State) -> {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Msg, Server) -> {noreply, Server}. code_change(_OldVsn, State, _Extra) -> {ok, State}.