NeDB是使用Nodejs实现的一个NoSQL嵌入式数据库操作模块,可以充当内存数据库,也可以用来实现本地存储,甚至可以在浏览器中使用
为了实现项目的轻量化,对项目中原本使用的MongoDB
进行替换,替换成相似的嵌入式数据库Nedb
。
这两个数据库在使用上是极其相似的,但是在数据库替换过程中也因为数据库之间存在的细小区别导致出现了一些bug,因此记录下来方便有相同需求的使用者参考。
1.ObjectID
MongoDB存_id的方式:
let ObjectId = require('mongodb').ObjectID;
let id = new ObjectId(); //object
NeDB存_id 的方式:
由于NeDB没有创建ObjectID的方法,因此引入了bson来实例化ObjectID,且传入数据库中的 _id需转为字符串类型
let ObjectId = require('bson').ObjectID;
let id = new ObjectId().toString(); //string
2.全文检索
nedb 没有全文检索,环境匹配的时候并不能解析原来代码的 $text 操作
// MongoDB代码
stuModel.find({$text:{$search:name + " " + city}},{score:{$meta:"textScore"}})
.sort({score:{$meta:"textScore"}})
.exec((err, docs) => {
if (!err)
console.log("find:",docs);
});

因此在改写成nedb的时候使用普通的查询进行替换
// Nedb
var query = {
$text:{
//查找 demand.name 或者 demand.platform存在的记录
$search:demand.name + ' ' + demand.platform
// $caseSensitive:false
}
};
var query = { $or: [{ name: demand.name }, { platform: demand.platform }] }
3.aggregate方法
nedb没有aggregate函数,无法分组,因此需自行编写代码分组,然后通过count进行聚合
错误写法一:
如下写法打印的result为空
外层find函数执行完后,在回调函数能得到数据库中的记录,在内层调用count却查不到数据
其中的原因是外层函数执行完后游标已经移动到了记录的最后,后面没有数据了,因此用count就查不到结果
nedbConn.find({}, (err, ret) => {
if (!err){
let data = ret;
let tempAttr = [];
let result = [];
for (let i = 0; i < data.length; i++) {
if (tempAttr.indexOf(data[i].ms_id) == -1){
nedbConn.count({ms_id : data[i].ms_id}, (err, count) => {
if (err)
return console.log(err);
else {
tempAttr.push(data[i].ms_id);
let item = {
_id: {ms_id : data[i].ms_id},
count: count
};
result.push(item);
}
});
}
}
console.log("tempAttr:",tempAttr); //[]
console.log("result:",result); //[]
}
})

错误写法二:
在find方法下面count方法上面打印tempAttr的长度,结果为0,这就意味着数据库查询是异步操作,因此不可以将查询和计数放在同一层级下
let tempAttr = [];
nedbConn.find({}, (err, ret) => {
if (!err){
let data = ret;
for (let i = 0; i < data.length; i++) {
if (tempAttr.indexOf(data[i].ms_id) == -1)
tempAttr.push(data[i].ms_id);
}
console.log("tempAttr:",tempAttr); //[]
}
})
let results = [];
console.log(tempAttr.length); //0
for (let i = 0; i < tempAttr.length; i++) {
nedbConn.count({ms_id : tempAttr[i]}, (err, count) => {
if (err)
return console.log(err);
else {
let item = {
_id : {ms_id : tempAttr[i]},
count : count
}
results.push(results);
}
});
console.log(results);
}
正确写法:
使用Promise
// nedb实现按prop分组后统计count操作 (by 7b)
ModelSerRun.aggregateCount = function(prop, callback) {
let findAll = new Promise(function(resolve, reject){
let tempAttr = [];
MSR.find({}, (err, ret) => {
if (!err){
let data = ret;
for (let i = 0; i < data.length; i++) {
if (tempAttr.indexOf(data[i][prop]) == -1)
tempAttr.push(data[i][prop]);
}
// console.log("tempAttr:",tempAttr); //[]
resolve({tempAttr: tempAttr, prop:prop});
}
})
});
findAll.then(({tempAttr, prop}) => {
let results = [];
for (let i = 0; i < tempAttr.length; i++) {
MSR.count((function () {
let query = {};
query[prop] = "";
query[prop] = tempAttr[i];
return query;
})(), (err, count) => {
if (err){
return callback(err);
}
let item = {_id : {}, count : count};
item._id[prop] = tempAttr[i];
results.push(item);
if (i == tempAttr.length - 1) {
console.log("results:", results);
callback(null, results);
}
});
}
})
}
4.Nedb的更新策略
虽然执行更新操作的时候每次都会在数据库中添加新的记录,但是在更新后会把相同_id的删除,仅保留最后更新的那个记录
5.数据库中默认的_id
在改写ModerSerRun时,出现了如下的错误,错误显示,传递进去的参数必须是一个Buffer 或者 是一个12字节的字符串 或者 是一个24个十六进制字符的字符串
项目中的代码
在项目中的代码里,保存模型运行记录时创建的对象中并没有_id
属性,所以在实例化ModelSerRun的时候,this._id
所储存的是undefined,_id
为undefined(或者未为_id赋值)时,在nedb中会自动创建一个十六个字节的字符串,而在MongoDB中会创建一个24个十六进制字符的字符串,因此在进行数据库操作的时候,使用nedb便会报如上的错误
下面是进行的测试
MongoDB:
Nedb:
6.Mongoose有Schema进行约束,而nedb没有
插入数据
在改写ModerSerRun时,当点击运行模型按钮,跳转到模型运行状态页面时,一直显示的时loading…
对比请求返回的数据,如下
modelServiceRun_n (改写):
modelServiceRun (原始):
对比上面两张图可以看到,使用mongodb时,msr_logs是空数组[],而使用nedb时并没有msr_logs属性,因此可以知道报错的原因:
在react文件中,当logs为空数组时赋值为msr对象的logs属性,而后者此时是undefined,因此进行到下面这行map方法时,对undefined调用map方法就报错了,导致一直停留在loading界面
查看项目代码,发现了错误的原因:在创建模型运行实例后,可以看到msr_log为undefined, 进入ModelSerRun中,发现msr_log属性少了一个s,所以当请求获取模型运行信息的时候并没有 msr_logs 属性
但是为什么mongodb中该bug会被忽视掉呢?
在nedb中,当对象中的某一个属性为undefined时,该属性并不会被添加到数据库中
而在mongodb中使用mongoose,有Schema(Schema 对象定义约束了数据库中的文档结构)规范表结构,测试中并没有传入test,而是传入值为undefined的test1,由于Schema已经先规范了值为数组的test属性,所以在数据库中存储的是一个值为空数组的test,而不是test1
更新数据
模型运行结束后,请求返回的数据中 状态没有变为1 且返回的只有一个runninginfo
Nedb:
MongoDB:
为什么用nedb 的时候只返回了runninginfo的信息呢?
找到项目中更新runninginfo的代码,参照nedb的更新写法之后找到了问题所在:对单个属性进行更新需要用到$set
操作符,而项目中的代码中并不是这样子写的
mongodb进行更新时,若是不使用$set是不会出现错误的,但是nedb会将传入的对象作为一个新的记录存储到数据库中,所以查询的时候只返回了新对象的信息`
测试如下:
MongoDB:
Nedb:
Nedb:

