1kt = __kyototycoon__
2db = kt.db
3
4-- prepare secondary indices
5idxdbs = {}
6for dbname, dbobj in pairs(kt.dbs) do
7   if kt.strbwm(dbname, ".kct") then
8      local prefix = kt.regex(dbname, ".*/", "")
9      prefix = kt.regex(dbname, ".kct$", "")
10      if #prefix > 0 then
11         idxdbs[prefix] = dbobj
12      end
13   end
14end
15
16-- set a record
17function set(inmap, outmap)
18   local id = tostring(inmap.id)
19   if not id then
20      return kt.RVEINVALID
21   end
22   local err = false
23   inmap.id = nil
24   local serial = kt.mapdump(inmap)
25   -- visitor function
26   local function visit(key, value, xt)
27      -- clean up indices
28      if value then
29         local obj = kt.mapload(value)
30         for rkey, rvalue in pairs(obj) do
31            local idxdb = idxdbs[rkey]
32            if idxdb then
33               local idxkey = rvalue .. " " .. id
34               if not idxdb:remove(idxkey) then
35                  kt.log("error", "removing an index entry failed")
36                  err = true
37               end
38            end
39         end
40      end
41      -- insert into indices
42      for rkey, rvalue in pairs(inmap) do
43         local idxdb = idxdbs[rkey]
44         if idxdb then
45            local idxkey = rvalue .. " " .. id
46            if not idxdb:set(idxkey, "") then
47               kt.log("error", "setting an index entry failed")
48               err = true
49            end
50         end
51      end
52      -- insert the serialized data into the main database
53      return serial
54   end
55   -- perform the visitor atomically
56   if not db:accept(id, visit) then
57      kt.log("error", "inserting a record failed")
58      err = true
59   end
60   if err then
61      return kt.EVEINTERNAL
62   end
63   return kt.RVSUCCESS
64end
65
66-- get a record
67function get(inmap, outmap)
68   local id = tostring(inmap.id)
69   if not id then
70      return kt.RVEINVALID
71   end
72   local serial = db:get(id)
73   if not serial then
74      return kt.RVELOGIC
75   end
76   local rec = kt.mapload(serial)
77   for rkey, rvalue in pairs(rec) do
78      outmap[rkey] = rvalue
79   end
80   return kt.RVSUCCESS
81end
82
83-- get heading records
84function head(inmap, outmap)
85   local name = tostring(inmap.name)
86   if not name then
87      return kt.RVEINVALID
88   end
89   local max = tonumber(inmap.max)
90   if not max then
91      max = 10
92   end
93   local idxdb = idxdbs[name]
94   if not idxdb then
95      return kt.RVELOGIC
96   end
97   local cur = idxdb:cursor()
98   cur:jump()
99   local rec
100   while max > 0 do
101      local key = cur:get_key(true)
102      if not key then
103         break
104      end
105      local rkey = kt.regex(key, "[^ ]+ ", "")
106      local rvalue = kt.regex(key, " .*", "")
107      outmap[rkey] = rvalue
108      max = max - 1
109   end
110   cur:disable()
111   return kt.RVSUCCESS
112end
113
114-- get tailing records
115function tail(inmap, outmap)
116   local name = tostring(inmap.name)
117   if not name then
118      return kt.RVEINVALID
119   end
120   local max = tonumber(inmap.max)
121   if not max then
122      max = 10
123   end
124   local idxdb = idxdbs[name]
125   if not idxdb then
126      return kt.RVELOGIC
127   end
128   local cur = idxdb:cursor()
129   cur:jump_back()
130   local rec
131   while max > 0 do
132      local key = cur:get_key()
133      if not key then
134         break
135      end
136      local rkey = kt.regex(key, "[^ ]+ ", "")
137      local rvalue = kt.regex(key, " .*", "")
138      outmap[rkey] = rvalue
139      cur:step_back()
140      max = max - 1
141   end
142   cur:disable()
143   return kt.RVSUCCESS
144end
145
146-- reindex an index
147function reindex(inmap, outmap)
148   local name = tostring(inmap.name)
149   if not name then
150      return kt.RVEINVALID
151   end
152   local idxdb = idxdbs[name]
153   if not idxdb then
154      return kt.RVELOGIC
155   end
156   local err = false
157   -- map function: invert the record data
158   local function map(key, value, emit)
159      local obj = kt.mapload(value)
160      for rkey, rvalue in pairs(obj) do
161         local idxdb = idxdbs[rkey]
162         if idxdb then
163            emit(rvalue, key)
164         end
165      end
166      return true
167   end
168   -- reduce function: insert into the index
169   local function reduce(key, iter)
170      local value
171      while true do
172         value = iter()
173         if not value then
174            break
175         end
176         local idxkey = key .. " " .. value
177         if not idxdb:set(idxkey, "") then
178            kt.log("error", "setting an index entry failed")
179            err = true
180         end
181      end
182      if err then
183         return false
184      end
185      return true
186   end
187   -- clear the index
188   if not idxdb:clear() then
189      kt.log("error", "clearing an index failed")
190      err = true
191   end
192   -- update the index
193   if not db:mapreduce(map, reduce) then
194      kt.log("error", "mapreduce failed")
195      err = true
196   end
197   if err then
198      return kt.EVEINTERNAL
199   end
200   return kt.RVSUCCESS
201end
202