You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
5.6 KiB
Lua
257 lines
5.6 KiB
Lua
local cbson = require("cbson")
|
|
local bit = require("bit")
|
|
|
|
|
|
local function check_bit(num, bitnum)
|
|
return bit.band(num,math.pow(2,bitnum)) ~= 0 -- and true or false
|
|
end
|
|
|
|
local _M = {}
|
|
|
|
local mt = { __index = _M }
|
|
|
|
function _M.new(collection, query, fields, explain, id)
|
|
return setmetatable(
|
|
{
|
|
_collection = collection,
|
|
_query = query,
|
|
_fields = fields,
|
|
_id = id or cbson.uint(0),
|
|
_skip = 0,
|
|
_limit = 0,
|
|
_docs = {},
|
|
_started = false,
|
|
_cnt = 0,
|
|
_comment = nil,
|
|
_hint = nil,
|
|
_max_scan = nil ,
|
|
_max_time_ms = nil,
|
|
_read_preference = nil,
|
|
_snapshot = nil,
|
|
_sort = nil,
|
|
_await = false,
|
|
_tailable = false,
|
|
_explain = explain or false
|
|
},
|
|
mt)
|
|
end
|
|
|
|
function _M.tailable(self, tailable)
|
|
self._tailable = tailable
|
|
return self
|
|
end
|
|
|
|
function _M.await(self, await)
|
|
self._await = await
|
|
return self
|
|
end
|
|
|
|
|
|
function _M.comment(self, comment)
|
|
self._comment = comment
|
|
return self
|
|
end
|
|
|
|
function _M.hint(self, hint)
|
|
self._hint = hint
|
|
return self
|
|
end
|
|
|
|
function _M.max_scan(self, max_scan)
|
|
self._max_scan = max_scan
|
|
return self
|
|
end
|
|
|
|
function _M.max_time_ms(self, max_time_ms)
|
|
self._max_time_ms = max_time_ms
|
|
return self
|
|
end
|
|
|
|
function _M.read_preference(self, read_preference)
|
|
self._read_preference = read_preference
|
|
return self
|
|
end
|
|
|
|
function _M.snapshot(self, snapshot)
|
|
self._snapshot = snapshot
|
|
return self
|
|
end
|
|
|
|
function _M.sort(self, sort)
|
|
self._sort = sort
|
|
return self
|
|
end
|
|
|
|
|
|
function _M.clone(self, explain)
|
|
local clone = self.new(self._collection, self._query, self._fields, explain)
|
|
clone:limit(self._limit)
|
|
clone:skip(self._skip)
|
|
|
|
clone:comment(self._comment)
|
|
clone:hint(self._hint)
|
|
clone:max_scan(self._max_scan)
|
|
clone:max_time_ms(self._max_time_ms)
|
|
clone:read_preference(self._read_preference)
|
|
clone:snapshot(self._snapshot)
|
|
clone:sort(self._sort)
|
|
|
|
return clone
|
|
end
|
|
|
|
function _M.skip(self, skip)
|
|
if self._started then
|
|
print("Can's set skip after starting cursor")
|
|
else
|
|
self._skip = skip
|
|
end
|
|
return self
|
|
end
|
|
|
|
function _M.limit(self, limit)
|
|
if self._started then
|
|
print("Can's set limit after starting cursor")
|
|
else
|
|
self._limit = limit
|
|
end
|
|
return self
|
|
end
|
|
|
|
function _M._build_query(self)
|
|
local ext = {}
|
|
if self._comment then ext['$comment'] = self._comment end
|
|
if self._explain then ext['$explain'] = true end
|
|
|
|
|
|
if self._hint then ext['$hint'] = self._hint end
|
|
if self._max_scan then ext['$maxScan'] = self._max_scan end
|
|
if self._max_time_ms then ext['$maxTimeMS'] = self._max_time_ms end
|
|
if self._read_preference then ext['$readPreference'] = self._read_preference end
|
|
if self._snapshot then ext['$snapshot'] = true end
|
|
if self._sort then ext['$orderby'] = self._sort end
|
|
|
|
ext['$query'] = self._query
|
|
|
|
return cbson.encode(ext)
|
|
end
|
|
|
|
function _M.next(self)
|
|
local moongoo, err = self._collection._db._moongoo:connect()
|
|
if not moongoo then
|
|
return nil, err
|
|
end
|
|
|
|
if self:_finished() then
|
|
if self._id ~= cbson.uint(0) then
|
|
self._collection._db._moongoo.connection:_kill_cursors(self._id)
|
|
self._id = cbson.uint(0)
|
|
end
|
|
return nil, "no more data"
|
|
end
|
|
|
|
if (not self._started) and (self._id == cbson.uint(0)) then
|
|
|
|
-- query and add id and batch
|
|
local flags, id, from, number, docs = self._collection._db._moongoo.connection:_query(self._collection:full_name(), self:_build_query(), self._skip, self._limit, self._fields, {tailable = self._tailable, await = self._await})
|
|
|
|
flags = tonumber(tostring(flags)) -- bitop can't work with cbson.int, so...
|
|
|
|
if check_bit(flags, 1) then -- QueryFailure
|
|
return nil, docs[1]['$err'] -- why is this $err and not errmsg, like others??
|
|
end
|
|
self._id = id
|
|
self:add_batch(docs)
|
|
elseif #self._docs == 0 and self._id ~= cbson.uint(0) then
|
|
-- we have something to fetch - get_more and add_batch
|
|
local flags, id, from, number, docs = self._collection._db._moongoo.connection:_get_more(self._collection:full_name(), self._limit, self._id)
|
|
|
|
flags = tonumber(tostring(flags)) -- bitop can't work with cbson.int, so...
|
|
|
|
if check_bit(flags, 0) then -- QueryFailure
|
|
return nil, "wrong cursor id"
|
|
end
|
|
self:add_batch(docs)
|
|
self._id = id
|
|
|
|
elseif #self._docs == 0 then--or self._id == cbson.uint(0) then
|
|
return nil, "no more data"
|
|
end
|
|
self._cnt = self._cnt+1
|
|
return table.remove(self._docs, 1) or nil, 'No more data'
|
|
end
|
|
|
|
function _M.all(self)
|
|
local docs = {}
|
|
while true do
|
|
local doc = self:next()
|
|
if doc == nil then break end
|
|
table.insert(docs, doc)
|
|
end
|
|
return docs
|
|
end
|
|
|
|
function _M.rewind(self)
|
|
self._started = false
|
|
self._docs = {}
|
|
self._collection._db._moongoo.connection:_kill_cursors(self._id)
|
|
self._id = cbson.uint(0)
|
|
return self
|
|
end
|
|
|
|
function _M.count(self)
|
|
local doc, err = self._collection._db:cmd(
|
|
{ count = self._collection.name },
|
|
{
|
|
query = self._query,
|
|
skip = self._skip,
|
|
limit = self._limit
|
|
}
|
|
)
|
|
if not doc then
|
|
return nil, err
|
|
end
|
|
|
|
return doc and doc.n or 0
|
|
end
|
|
|
|
function _M.distinct(self, key)
|
|
local doc, err = self._collection._db:cmd(
|
|
{ distinct = self._collection.name },
|
|
{
|
|
query = self._query,
|
|
key = key
|
|
}
|
|
)
|
|
if not doc then
|
|
return nil, err
|
|
end
|
|
|
|
return doc and doc.values or {}
|
|
end
|
|
|
|
function _M.explain(self)
|
|
return self:clone(true):sort(nil):next()
|
|
end
|
|
|
|
function _M.add_batch(self, docs)
|
|
self._started = true
|
|
for k,v in ipairs(docs) do
|
|
table.insert(self._docs, v)
|
|
end
|
|
return self
|
|
end
|
|
|
|
function _M._finished(self)
|
|
if self._limit == 0 then
|
|
return false
|
|
else
|
|
if self._cnt >= math.abs(self._limit) then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
return _M
|