Mongo
lua mongo driver implemented by cfadmin.
Install / Use
/learn @cfadmin-cn/MongoREADME
Lua MongoDB Driver
基于cfadmin框架实现的MongoDB Driver.
特性
-
[x] 最新版的协议(
OP_MSG), 交互更加高效; -
[x] 封装出了统一的
CURD、聚合、统计等接口方法; -
[x] 对
GridFS的上传、查询、删除等操作, 并且支持分片传输; -
[x] 拥有社区最完善的
BSON解析器, 使用更加简单、解析更加高效; -
[x] 更简洁的语法降低学习成本, 无需复杂的调用即可完成功能;
-
[x] 完善的使用示例, 可以自行测试实际使用情况;
类型
-
字符串(
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"
1. 构造方法
创建
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
此方法无返回值.
2. CRUD操作
查询语句
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.
3. 聚合操作
统计查询
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的使用方法.
<details> <summary>1. CRUD操作</summary>require"utils"
local mongo = require "mongo"
local bson = require "mongo.bson"
local m = mongo:new {
db = "mydb", -- 授权DB名称
username = "admin", -- 授权用户名称
password = "admin", -- 授权用户密码
}
require "logging":DEBUG("开始")
local ok, err = m:connect()
if not ok then
return print(false, err)
end
local database, collect = "mydb", "table"
local tab
tab, 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() },
})
if not tab then
return print(false, err)
end
var_dump(tab)
tab, err = m:find(database, collect)
if not tab then
return print(false, err)
end
var_dump(tab)
tab, err = m:update(database, collect, { nickname = "車太太" }, { ["$set"] = { nickname = "車先生" }})
if not tab then
return print(false, err)
end
var_dump(tab)
tab, err = m:find(database, collect)
if not tab then
return print(false, err)
end
var_dump(tab)
tab, err = m:delete(database, collect, { nickname = "車先生" } )
if not tab then
return print(false, err)
end
var_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] : "结束"
</details>
<details>
<summary>2. 聚合操作</summary>
require"utils"
local mongo = require "mongo"
local bson = require "mongo.bson"
local m = mongo:new {
db = "mydb", -- 授权DB名称
username = "admin", -- 授权用户名称
password = "admin", -- 授权用户密码
}
require "logging":DEBUG("开始")
local ok, err = m:connect()
if not ok then
return print(false, err)
end
local database, collect = "mydb", "table"
local tab, id
tab, err = m:count(database, collect, {})
if not tab then
return print(false, err)
end
var_dump(tab)
tab, id = m:aggregate(database, collect
,{
{ ["$match"] = {age = 26} },
{ ["$sort"] = {_id = -1} },
}
,{
-- cursor = 8896207551195673826, -- 游标的写法.
-- size = 3, -- 聚合函数内单独指定size无意义, 如有必要请在fileters里使用$limit
}
)
if not tab then
return print(false, err)
end
print(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] : "结束"
</details>
<details>
<summary>3. 索引操作</summary>
local mongo = require "mongo"
require "utils"
local m = mongo:new { db = "mydb" }
if not m:connect() then
return print(false, "连接失败")
end
local tab, info = m:create_indexes("mydb", "test", { nickname = 1, age = -1}, { unique = 1, background = 1})
if not tab then
return print(false, info)
end
var_dump(tab)
local tab, info = m:get_indexes("mydb", "t
Related Skills
node-connect
349.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
