基于cfadmin框架实现的MongoDB Driver.
最新版的协议(
OP_MSG), 交互更加高效;封装出了统一的
CURD、聚合、统计等接口方法;对
GridFS的上传、查询、删除等操作, 并且支持分片传输;拥有社区最完善的
BSON解析器, 使用更加简单、解析更加高效;更简洁的语法降低学习成本, 无需复杂的调用即可完成功能;
完善的使用示例, 可以自行测试实际使用情况;
字符串(
String)二进制类型(
Binary/MD5/uuid)正则表达式(
Regex)表(
table/array)空(
null)未定义(
undefined)时间(
datetime/timestamp)对象ID(
objectid)整型(
int32/int64)浮点型(
Double)布尔(
true/false)大小值(
MaxKey/MinKey)代码(
java script code)
本库使用了纯Lua实现了bson的序列化与反序列化, 但是经过一段时间的使用与测试发现体验并不乐观; 因为特性原因必须增加复杂的反序列化流程;
而我们主要在与mongodb服务器进行交互的时候回才会使用bson, 并且交互请求的编码不会有太多内容所以主要性能问题定位在bson的反序列化上.
在经过详细的测试后发现纯Lua实现的BSON反序列化性能十分糟糕,所以最后经过使用使用C语言重写的反序列化方法类解决这方面带来的一些负面影响.
值得一提的是C语言版的实现效率是Lua的100倍, 所以不用再担心性能问题了; 并且内部能自动检查用户是否有编译出C版本的bson实现, 用户有需要编译即可.
clone项目到3rd目录下, 这样就完成了基础安装;(可选) 如果您安装有
GCC或者clang编译器; 那么可以进入mongo的目录运行make build编译lbson.so;
local mongo = require "mongo"
local bson = require "mongo.bson"
function mongo:new(opt) return mongo end
opt.host-string类型, 服务器域名(默认是:"localhost");opt.port-integer类型, 服务器端口(默认是:27017);opt.SSL-boolean类型, 是否需要使用SSL协议握手;opt.auth_mode-string类型, 授权验证模式(仅支持:SCRAM-SHA-1);opt.db-string类型, 授权数据库名称(默认是:"admin");opt.username-string类型, 授权用户账号;opt.password-string类型, 授权用户密码;
调用此构造方法将会创建MongoDB对象.
function mongo:connect() return true | nil, string end
开发者在创建MongoDB对象的时候如果填写了username与password, 调用此方法的时候会自动完成授权认证.
但可能存在授权操作不完善(例如不可读、写)的情况, 这就可能出现鉴权成功但后续执行CRUD时提示鉴权的可能;
如遇到以上问题, 请开发者自行使用相关管理工具解决.
成功返回true, 失败返回false与失败信息string,
function mongo:close() return nil end
此方法无返回值.
function mongo:find(database, collect, filter, option) return info, id | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;filter-table类型, 一个符合语法规范的查询条件;option-table类型, 可选参数(option.sort/option.limit/option.skip/option.cursor/option.size/option.project);
filter可以用作查询的过滤条件, 例如: { nickname = "李小龙" }或一个空表; (但是不能为空数组);
option参数的组合作用为游标分页与跳跃分页:
* 跳跃分页(`limit`与`skip`): 操作方式类似结构化数据库`MySQL`、`Oracle`等的`LIMIT`与`OFFSET`;
* 游标分页(`cursor`与`size`):每次迭代(包括第一次)返回`size`条数据与下次迭代的游标`ID`(游标`ID`是一次性的);
sort参数指定了排序的方式, 表达式为: {sort = {age = 1}} 或者 {sort = {age = -1}}, (1)升序、(-1)降序;
project参数指定了查询时可以筛选出需要返回的字段, 这在需要过滤集合字段的情况下使用非常有效;
成功返回table类型的info与integer类型的cursor id, 失败返回false与失败信息string.
function mongo:insert(database, collect, documents, option) return info | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;documents-table数组类型, 包含(至少)有一个或者多个(可选)文档的数组;option-table类型, 可选参数(option.ordered);
documents为文档数组, 即使只插入一条数据也应该使用这样的数组表达式: { {nickname = "名称", age = 18 }};
option.ordered默认为false(如果插入多条的时候出错, 则忽略并继续处理后续数据), 设置为true则会不再处理后续数据;
成功返回table类型的info, 失败返回false与失败信息string.
function mongo:update(database, collect, filter, set, option) return info | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;filter-table类型, 查询过滤的条件;set-table类型, 查询修改的内容;option-table类型, 可选参数(option.upsert/option.multi);
filter可以用作查询的过滤条件, 例如: { nickname = "李小龙" }或一个空表; (但是不能为空数组);
set参数为一个文档或者文档更新语法, 具体使用方式类似mongo shell语法:
* `mongo:update('db', 'table', { name = "123" }, { ['$set'] = { name = "234" } } )`
* `mongo:update('db', 'table', { name = "123" }, { ['$inc'] = { age = 1 } } )`
* `mongo:update('db', 'table', { name = "123" }, { ['$unset'] = { age = 1} } )`
option.upsert默认为false; 如果设置为true, 则表示不存在set指定的记录则插入;
option.multi默认为false(只更新找到的第一条记录), 如果设置为true, 则表示更新所有记录;
成功返回table类型的info, 失败返回false与失败信息string.
function mongo:delete(database, collect, option) return info | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;filter-table类型, 查询过滤的条件;option-table类型, 可选参数(option.one);
filter可以用作查询的过滤条件, 例如: { nickname = "李小龙" }或一个空表; (但是不能为空数组);
option.one属性指定为1表示只删除1条数据, 其它值与默认情况下都表示删除所有匹配项;
成功返回table类型的info, 失败返回false与失败信息string.
function mongo:count(database, collect, filter) return info | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;filter-table类型, 查询过滤的条件;
根据filter参数的过滤条件(可以为空), 统计集合内符合过滤条件的数量;
成功返回table类型的info, 失败返回false与失败信息string.
function mongo:aggregate(database, collect, filters, option) return info | nil, string end
database-string类型,MongoDB的数据库名称;collect-string类型,MongoDB的集合名称;filters-table类型, 查询过滤的条件的数组;
此方法的返回值内容会根据实际filter内的提供的参数表达式返回不同的内容;
成功返回table类型的info, 失败返回false与失败信息string.
以下示例展示了基础API的使用方法.
1. CRUD操作
require"utils"localmongo=require"mongo"localbson=require"mongo.bson"localm=mongo:new {
db="mydb", -- 授权DB名称username="admin", -- 授权用户名称password="admin", -- 授权用户密码
}
require"logging":DEBUG("开始")
localok, err=m:connect()
ifnotokthenreturnprint(false, err)
endlocaldatabase, collect="mydb", "table"localtabtab, err=m:insert(database, collect, {
{ nickname="車先生", age=30, ts=bson.timestamp(), nullptr=bson.null(), regex=bson.regex("/先生/i"), uuid=bson.uuid() },
{ nickname="車太太", age=26, ts=bson.timestamp(), nullptr=bson.null(), regex=bson.regex("/太太/i"), guid=bson.guid() },
})
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
tab, err=m:find(database, collect)
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
tab, err=m:update(database, collect, { nickname="車太太" }, { ["$set"] = { nickname="車先生" }})
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
tab, err=m:find(database, collect)
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
tab, err=m:delete(database, collect, { nickname="車先生" } )
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
m:close()
require"logging":DEBUG("结束")输出如下:
[candy@MacBookPro:~/Documents/cfadmin] $ ./cfadmin
[2021-02-28 13:26:35,947] [@script/main.lua:94] [DEBUG] :"开始"
{
["acknowledged"] = true,
["insertedCount"] = 2,
}
{
[1] = {
["_id"] = "603b298bb7bacd21ef3c59d9",
["regex"] = "/先生/i",
["nickname"] = "車先生",
["age"] = 30,
["nullptr"] = userdata: 0x0,
["ts"] = 1614489995947,
["uuid"] = "ba1c1b0c-191b-4480-9d68-cb4c5755cd5d",
},
[2] = {
["_id"] = "603b298bb7bacd21ef3c59da",
["regex"] = "/太太/i",
["nickname"] = "車太太",
["guid"] = "f47bf20c-7a7f-4c61-603b-298b2507b289",
["age"] = 26,
["nullptr"] = userdata: 0x0,
["ts"] = 1614489995947,
},
}
{
["matchedCount"] = 1,
["modifiedCount"] = 1,
["acknowledged"] = true,
}
{
[1] = {
["_id"] = "603b298bb7bacd21ef3c59d9",
["regex"] = "/先生/i",
["nickname"] = "車先生",
["age"] = 30,
["nullptr"] = userdata: 0x0,
["ts"] = 1614489995947,
["uuid"] = "ba1c1b0c-191b-4480-9d68-cb4c5755cd5d",
},
[2] = {
["_id"] = "603b298bb7bacd21ef3c59da",
["regex"] = "/太太/i",
["nickname"] = "車先生",
["guid"] = "f47bf20c-7a7f-4c61-603b-298b2507b289",
["age"] = 26,
["nullptr"] = userdata: 0x0,
["ts"] = 1614489995947,
},
}
{
["acknowledged"] = true,
["deletedCount"] = 2,
}
[2021-02-28 13:26:35,961] [@script/main.lua:135] [DEBUG] :"结束"2. 聚合操作
require"utils"localmongo=require"mongo"localbson=require"mongo.bson"localm=mongo:new {
db="mydb", -- 授权DB名称username="admin", -- 授权用户名称password="admin", -- 授权用户密码
}
require"logging":DEBUG("开始")
localok, err=m:connect()
ifnotokthenreturnprint(false, err)
endlocaldatabase, collect="mydb", "table"localtab, idtab, err=m:count(database, collect, {})
ifnottabthenreturnprint(false, err)
endvar_dump(tab)
tab, id=m:aggregate(database, collect
,{
{ ["$match"] = {age=26} },
{ ["$sort"] = {_id=-1} },
}
,{
-- cursor = 8896207551195673826, -- 游标的写法.-- size = 3, -- 聚合函数内单独指定size无意义, 如有必要请在fileters里使用$limit
}
)
ifnottabthenreturnprint(false, err)
endprint(tab, #tab, id)
-- var_dump(tab)require"logging":DEBUG("结束")输出如下:
Candy@CandyMi MSYS ~/stt_trade
$ ./cfadmin.exe
[2021-03-02 14:15:47,675] [@script/main.lua:92] [DEBUG] :"开始"
{
["acknowledged"] = true,
["count"] = 122,
}
table: 0x8000f9f30 101 4732584172034615803
[2021-03-02 14:15:47,685] [@script/main.lua:130] [DEBUG] :"结束"3. 索引操作
localmongo=require"mongo"require"utils"localm=mongo:new { db="mydb" }
ifnotm:connect() thenreturnprint(false, "连接失败")
endlocaltab, info=m:create_indexes("mydb", "test", { nickname=1, age=-1}, { unique=1, background=1})
ifnottabthenreturnprint(false, info)
endvar_dump(tab)
localtab, info=m:get_indexes("mydb", "test")
ifnottabthenreturnprint(false, info)
endvar_dump(tab)
localtab, info=m:drop_indexes("mydb", "test", "nickname_1_age_1")
ifnottabthenreturnprint(false, info)
endvar_dump(tab)4. GridFS操作
require"utils"localmongo=require"mongo"localm=mongo:new { db="mydb" }
ifnotm:connect() thenreturnprint(false, "连接失败")
endlocaltab, id=m.gridfs:gridfs_find("mydb", "fs", { })
ifnottabthenreturnprint(false, id)
endprint(id)
var_dump(tab)
ifid>0thenlocaltab, id=m.gridfs:gridfs_find("mydb", "fs", {}, id)
ifnottabthenreturnprint(false, id)
endprint(id)
var_dump(tab)
endlocaltab, info=m.gridfs:gridfs_delete("mydb", "fs", {})
ifnottabthenreturnprint(false, info)
endvar_dump(tab)5. DB的使用
require"utils"localDB=require"mongo.DB"localm=DB:new { db="mydb" }
ifnotm:connect() thenreturnprint(false, "连接失败")
endlocaltab, id=m:find("mydb", "test", { })
ifnottabthenreturnprint(false, id)
endvar_dump(tab)
localtab, id=m:delete("mydb", "test", { })
ifnottabthenreturnprint(false, id)
endvar_dump(tab)
localtab, id=m:find("mydb", "test", { })
ifnottabthenreturnprint(false, id)
endvar_dump(tab)如需删除数据请一定注意使用方法的行为是否如你预期.
对
bson库的性能有一定的要求, 请务必编译lbson.so库文件出来.当某字段需要插入
空数组的时候, 可以使用内置的bson.empty_array()方法进行构造.查询未指定
cursor但是指定了size, 数据如果超出size就会返回游标ID.一些特殊数据类型(
bson.objectid)需要bson的构造方法来编码传递, 如果有疑问可以咨询作者.授权仅支持用户名/密码授权(SCRAM-SHA-1);
本驱动仅支持MongoDB 3.6及以上版本;
使用
VS CODE安装lua-language-server插件后使用会自动得到上述代码不全与编码提示;如果您有更好的需求与建议请留言到ISSUE, 作者在收到后会尽快回复并与您进行沟通;