论 如何把NoSQL用成RDBMS

好好的Mongodb,本来是一个非关系型数据库,楞是让我用成了强关系型,我也是很懵逼呀。到现在接手的一个项目(迄今为止仅有的一个项目)用的Mongodb作为后台,就是我们小程序的项目。

明天要考英语了,背单词是不可能背单词的,心里烦就回顾一下我是怎么在Nodejs里用Mongodb的吧。

本文中操作Mongodb使用的包为 mongoose

Mongodb直观感觉

Mongodb给我最直观的感觉就是像JSON,整个数据库就像是一个JS的对象一样,这无疑是一个极大的特色。我们可以操作使用条件查找、可以添加方法、可以添加对象、删除,一切就像是使用JS的Object一样方便,如果关系处理得当,性能也不会慢多少。

具体使用

数据表

这就是我说把Mongodb用成Mysql的地方,在这里我要先定义好一个数据表中每项数据的结构。每个结构都是从Mongoose.Schemanew 出来的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let user_info = new Schema({
openid: String,
session_key: String,
group_list: [{ type: mongoose.Schema.ObjectId, ref: "group_info" }],
file_list: [{ type: mongoose.Schema.ObjectId, ref: "file_info" }],
password: String,
uuid: String,
unionid: String,
avatar_url: String,
full_info: {
nick_name: String,
gender: String,
city: String,
province: String,
country: String
}
});

这个例子可以清晰地看出我声明的结构的样子,像JSON一样。

注意到其中以一个比较特殊的对象 mongoose.Schema.ObjectId。这一项是用来联查其他数据表中数据的,联查的结果会变成对象放在对应的位置,联查的命令下面再说。

但是我们现在仅仅是建立了一个数据表中的一行,那么这个数据表叫什么呢?在这里定义

1
2
3
4
mongoose.connect(global.conf.mongodb.url, { config: { autoIndex: false } });
user_info.index({ openid: 1 });

let user = mongoose.model('user_info', user_info);

呵呵,直接就连数据库都连上了。后面那个自动索引,忘了从哪里看的了,说是作者都推荐关闭以提高性能。emmmmm既然这样你为啥要写呢。无论如何,自动索引也不知道他索的什么,干脆就关了吧,然后觉得需要查询的项自己写个手动索引。

上文中生成的数据表中的一项经过model()这个方法添加到数据库中,这个函数有两个参数,一个是这个数据表叫啥,他会自动在Mongodb中自动创建一个对应的数据表,比如这个user_info对应user_infos没错,加了个s(我绝对不会告诉你,当时眼瞎没看见,手动添加测试数据死活用不了的),这时候就要说说上面那个联查的ref:是干什么用的了,他是告诉Mongoose,这个需要联查的数据表叫什么用的。

现在,我们就得到了一个可以操作的对象了。

数据表添加方法

在只有数据结构的时候,我们就可以给Schema添加方法了:

1
2
3
user_info.statics.find_openid = function(openid, callback) {
return this.findOne({ openid: new RegExp(openid, 'i') }, callback);
}

通过statics在里面添加一个方法。注意这时就不能用我熟悉的()=>{}来声明函数了,一定要用function(){}来声明函数,因为下面会用到this,前者的this作用域会继承之前的环境,这就没法用了。

在函数内部的this就是是这个数据表了,他就是Model类型,有查询之类的方法,随意使用。

当然在实例化之后我们也可以添加方法。准确的说是用函数包装数据表变成一个方法。

1
2
3
4
5
6
7
let find_user_by_openid = (openid) => {
return new Promise((res, rej) => {
user.find_openid(openid, (err, rec) => {
res(rec);
});
});
};

这里脱裤子放屁机智的实例就是把上面的方法直接用了一下。交叉看一下应该差不多了吧。

数据表的操作

其实主要用到的就几个,更多的请看API-Model-MongooseDoc

Model.find()

API-find()-MongooseDoc

1
2
3
user.find({/*....*/}, (err,docs)=>{
// ....
})

第一个参数就是条件,{ name: 'john', age: { $gte: 18 }}类似,可以查找范围,也可以或什么的,没用过,需要的话再取看官方文档。

之后是回调函数,也可以没有,最后使用.exec()里面再给回调函数。

如果查询出点什么意外(不知道能出啥意外,没出过)err就会有值,后面可以使用

1
if (err) console.error(err);

后面那个就是查询结果了,是个数组,里面就是符合条件的项。使用for..of一个一个取出食用

Model.findById()

API-findById()-MongooseDoc

众所周知,Mongodb所有的项都会一个独一无二的ID,这就是根据Id找项用的。

第一个穿Id进去,后面回调同上。

这个API还可以加点简单操作的东西:

  • Model.findByIdAndDelete()
  • Model.findByIdAndRemove()
  • Model.findByIdAndUpdate() 这个有些不同,没用过,需要的自取文档食用

不过多解释。

Model.findOne()

API-findOne()-MongooseDoc

根据条件找一个,找第一个(?应该是吧,没实验过)符合条件的。

同样可以跟And,同上,回调函数同上上。

new Model()

这就是添加记录了后面跟着和之前 Schema定义的一样的数据结构进去就行了。

1
2
3
4
5
6
let add_user = async(user_info) => {
let a_user = new user(user_info);
let rec = await a_user.save();
if (global.conf.debug) console.log(rec);
return rec;
};

(不要问我为啥定义这么简单的函数,我乐意 (傲娇

记得调用save,要不就消失了,如果你的方法中有修改查询到的结果,也记得要保存一下。

populate() 联查

在NoSQl中使用联查是不好的习惯,这证明你的数据库设计有问题,需要改正。

1
2
3
user.findById(user_id).
populate({ path: "file_list", select: "-real_url -upload_user_id -download_user_list -size" }).
exec((err, rew) => {})

path就是需要联查哪一项,后面是限制条件,比如不把哪些信息联查出来等。

如果不联查的话,取到哪里就只有一个mongoose.Schema.ObjectId对象,现在就是个含有数据的项了。如果还想取id就用._id


2018-6-29更新

感谢高渐离同学,对我笔记中错别字,词语滥用,概念混淆做出指正。


有啥不会就百度。

db的一种写法:sql.js-Questionnaire_nodejs