MongoDB查询以及投影操作符的实践
当使用MongoDB提供复制操作时,需要简单的CURD操作基础上使用一些操作符(如同$eq这样的操作符),mongoDB官网文档[1]有其详细介绍。需要注意的是,Node端所使用的Mongoose工具,在API上与原生的驱动还是有细微的区别。,本文结合mean.js(MongoDB, Express, AngularJS, and Node.js)开发实例来学习查询操作符的使用,先介绍做项目涉及到的操作然后结合实践中的涉及的内容。
####1. 比较操作符
比较查询操作符符则有两类,一类是大于($gt、$gte)、小于($lt、$lte)、等于($eq、$ne)这种2值比较,一类是是否与数组内元素匹配($in、$nin)。当数值型数据进行比较时,MongoDB会先转换再进行比较。
####2. 逻辑操作符
逻辑查询操作符有4种,分别是:与($and)、或($or)、非($not)以及或非($nor)逻辑。$and、$or、$nor后接大括号来框住的所接受的条件。而$not的使用有点不同,需要在指定字段之后,可以匹配后接的子条件以及该字段不存在的情况。
####3. 元素操作符
元素选择器中$exists就是来选择指定字段存在数值的文档,$type则是选择字段的数值类型为指定类型,BSON当中的数据类型都有相应的数字来表示,这种类型数字就是在这里使用了。
####4. 数组操作符
这3种针对数据选择的操作器如字面意思理解就可以了。需要注意几点就是:$all相当于$and 的行为一样,也就是数组中的元素条件要同时满足所以条件,而$elemMatch则需要对象数组中满足$elemMatch条件的所有条件,并且只返回第一个匹配的数组元素,这里还可以联系依稀$in选择器,$in限定来选择条件为一组值来匹配数组,而$elemMatch匹配的是对象数组元素。
####5. 投影操作符
投影(project)的功能可以与SQL中的select类比,满足此条件的文档只返回包含指定字段的结果集。位置$符来匹配查询条件的第一个文档,$一般伴随着其他条件来限定数组元素的获取。比如这个条件 { semester: 1, grades: { $gte: 85 } }, { "grades.$": 1 }
就是在满足前面两个条件下,只获取grades数组第一个匹配的元素。$elemMatch如上述的应用一样返回符合匹配的第一个元素。
$meta的意义比较有意思,它的格式定义为{ $meta:
$slcie就如同数组操作slice一样,只返回指定数量、范围的元素,如db.posts.find( {}, { comments: { $slice: [ -20, 10 ] } } ),就只返回从comments数组倒数20开始的前10个元素。
照本宣科就到这里,还有一些类别(Evaluation、Geospatial)的操作符没有提到,是因为这些类别暂时没有使用过。下面汇总项目中使用到的查询实例,以便项目接盘侠能轻松上手。
####1. 针对数组查询案例
第一个场景是数据库集合中存储了数据处理的两个状态,一个是公共编辑的状态(‘commonStatus’),一个是用户编辑的私有状态(‘userStatus’)。业务上需要通过这两种状态的组合获取数据,比较麻烦的地方就是用户私有的信息需要涉及数组操作。具体数据结构如下:
commonStatus: {
type: String
},
privateInfo: [{
_creator: {
type: Schema.Types.ObjectId, ref: 'User'
},
score: {
type: Number
},
scoredTime: {
type: Date
},
userStatus: {
type: String
},
tag: [{
type: Schema.Types.ObjectId, ref: 'User.customTags'
}]
}],
私有状态的业务的查询是这样的,前端传递’userId’和’userStatus’给Node端,当’userStatus’为’unchecked’时在数据库中可能的情况有3种:第一种情况为’privateInfo’存在当前用户其userStatus为’unchecked’,第二种情况为privateInfo数组存在但没有当前用户数据,第三种情况为privateInfo数组为空的情况,也就是集合中文档的缺省值。查询语句的编写如下代码
//构造查询语句,使用$or操作符包含3中可能情况
queryCondition.$or = [{
//实际不可能出现的情况,但保证查询的robust
//条件一: privateInfo存在并有userId的数据其userStatus为'unchecked'
'privateInfo': {
//使用$all限定指定字段的数据
//不使用$all则严格限定privateInfo数据格式如查询条件
$all: {
'_creator': new ObjectId(condition.userId),
'userStatus': {
//使用$nin排除有其他意义的状态
$nin: ['scored', 'communicated']
}
}
}
}, {
//条件二: privateInfo 存在但没有userId的相关数据包括无数据
'privateInfo._creator': {
//使用$ne排除包含用户数据的数组
$ne: condition.userId
}
}, {
//条件三: 初始邮件,privateInfo无数据
//通过$size限定数组的大小匹配空数组
'privateInfo': { $size: 0 }
}];
这样就将’unchecked’状态囊括了,但其他私有条件还进行不同条件的查询,如下所示,前端传递个Node端私有状态的值提供查询。
if(condition.userStatus === 'unchecked'){
//如上
}else{
//其他私人状态只需满足上述条件一的形式
queryCondition.privateInfo = {
$all: [{
//将$elemMatch与$all结合
//当数据需要满足多种$elemMatch组合时
$elemMatch: {
'_creator': condition.userId,
'userStatus': condition.userStatus
}
}]
};
}
####2. 时间范围查询案例
接下来的案例是获取时间时大于指定时间的情况,直接使用$gte比较操作符就可以来,
Mails.find({
'mail.date': {
$gte: contactDate
},
'mail.from': {
$elemMatch: {
'address': fromAddr
}
}
})
.select('_id mail.subject').sort({'mail.date': -1}).lean().exec(function(err, mails){
//后续操作
});
###总结
本文简单罗列来mongoDB的查询以及投影操作符的含义并解释来两个实例应用。其实还有一些需要实践经验才能知道的,比如参考后两个就是比较典型的例子,总体来说mongoDB操作符不难,多多实践就可以来。mongoDB下一部分为更新操作符涉及来数组的操作以及$符的占位作用等,这些都在暑假实践过,更加有意思。
####参考