这一篇继续说一些查询相关的问题。
上一篇提到的查询都是等值条件查询,但是我们更多的时候需要模糊查询、非等值查询、模式匹配等。mongo不是key-value存储,它支持非常灵活复杂的查询方式,甚至比rdbms还要灵活的多,当然也复杂的多。
另外,需要多说一点,用nosql归类这些数据库并不准确,只是RDBMS都是用SQL的,而它们都是不用SQL的,所以就用nosql来归类这些数据库了。其实从技术上考虑,完全可以实现一个非RDBMS而继续使用SQL的全部特性来操作和管理数据库。当然为了表达方便这一系列文章仍然使用nosql这一并不准确的名词。
既然没有了sql,要操作mongo自然就要使用其它的方式了。前面的文章都已经出现过一些了,就是用mongo定义的数据库操作api配合它的文档形式的操作参数完成数据库的创建、修改、删除和数据的增删改查。
关于查询的参数在上一篇几乎已经说完,还剩下的就是find*和count的第一个参数。
由于find*的第一个参数和count参数都一样,本文就只以find函数做说明。
查询某个字段比指定值小:$lt
//假设存在集合USERdb.USER.find({REGIST_DATE:{$lt:new Date(2013,0,1)}});
/*前面提到过mongo完全遵守JAVASCRIPT语法,在JAVASCRIPT里面,月份是从0开始的,即上面的查询是查询2013-1-1以前注册的的用户。*/
查询某个字段比指定值大:$gt
db.USER.find({REGIST_DATE:{$gt:new Date(2013,0,1)}});
/*$lt表示小于,表示大于的自然就是$gt了*/
大于等于:$gte 小于等于:$lte
db.USER.find({REGIST_DATE:{$gte:new Date(2013,0,1),$lte:new Date(2013,0,31)}});
/*关于这个时间的问题看起来似乎有些别扭哈,没办法啦,MONGO就是这样啦。习惯就好啦。*/
/*上面的一行查询就是针对REGIST_DATE的组合查询形式,表示查询出所有在2013-1-1到2013-1-31注册的用户*/
接下来的查询方式就比较复杂了
正则表达式,mongo里面没有类似sql的like特性,不过可以用正则表达式代替
使用正则表达式查询有两种情况,在支持正则表达式字面值(标量)的语言里可以直接使用正则表达式字面值,比如RUBY NODEJS等。
db.USER.find({NAME:/^run/i});//以javascript为例,这个查询出所有用户名以run开头的用户,且不分大小写
像JAVA这样不支持正则表达式标量的语言怎么办呢?就有些麻烦了,需要借助MONGO api完成从字符串到正则表达式的转化。
db.USER.find(NAME:{$regex:'^run',$options:'i'});//这行命令完成跟上一行一样的工作。
其中,$options是正则表达式的选项,它一共有三个字母的任意组合可选,这三个字母分别是g i m,这三个字母可以任意组合表达不同的意义。
g:表示针对整个字符串做匹配,如果不加正则表达式在匹配到第一个符合的子串时就返回了。(global)
i:忽略大小写(insenssitive)
m:一个字符串内如果存在换行符,将作为多行模式匹配(multiple)
除了i以外其它两个选项在查询的时候恐怕用不到。
$in---相当于sql的in,它可以利用索引
db.USER.find(NAME:{$in:['tom','jerry']});
/*如果为NAME字段创建了索引,它就会从索引里面查找*/
/*mongo是区分大小写的,所以集合和文档属性的名字必须与创建它们的时候一致。*/
/*也就是NAME和name是两个不同的属性,它们可以同时存在于一个文档内*/
/*在一个集合内如果即存在NAME属性的文档,又存在name属性的文档,那么上面的那条命令不会把name属性查询出来*/
$nin---$in的相反操作,相当于sql的not in
db.USER.find(NAME:{$nin:['tom','jerry']});
注意:$nin不会利用索引,也就是说上面的命令$nin不会使用针对NAME属性的索引。
$all---没有sql类似的特性与之类比了,它的意义在于:查询条件是一个简单值数组,只有返回属性满足数组内的所有值的文档。这种查询条件只有在属性值是一个数组的情况下。
以我的这篇博文为例。要查询出所有含有nosql和mongo这两个标签的文档可以这么做
假设iteye要把数据库迁移到mongo,博客文章的数据模型就可以这样定义
首先定义一个名为blog的集合。
这篇blog可以如下方式存储
{
_ID:ObjectID(............),
subject:'mongo简介——查询(续)',
category:'database',
user_category:['nosql','mongo'],
content:'.............',
tags:['nosql','mongo'],
origrinal:true,
pub:'blog'
}
/*origrinal表示是否原创;由于没有附件,本文的文档就不包含附件属性,由于我不知道iteye如何定义频道,我就用字符串表示了*/
下面如果要查询包含‘nosql’标签的所有博文
db.blog.find(tags:'nosql');//这样就可以了
下面要查询同时包含'nosql'和'mongo'这两个标签的博文
db.blog.find(tags:{$all:['nosql','mongo']});
如果有的博文除了包含'nosql'和'mongo'标签,还包含'MONGO' 'Mongo' 'mongodb' 'MongoDB' ‘NOSQL’等标签,上面的那行命令也会一起返回。
如果要进行忽略大小写的查询,我又不想使用正则表达式做模糊匹配该怎么办呢?
答案是不能。
而这样的需求还是很常见的,那么惟一的做法就是,在用户保存博文的时候,程序根据以前已经存在的标签找出相似词,自动创建几个可能会出现的不同大小写的标签。比如我保存这篇文章的时候程序再自动创建上面提到的那几个我没有指定的标签。
$ne---不等性比较,它接收单值或数组
db.blog.find(tags:{$ne:'nosql'});//返回所有不包含nosql标签的博文
db.blog.find(tags:{$ne:['nosql','mongo']});//返回所有不包含nosql和mongo这两个标签的博文
$size---检查一个数组的尺寸,不利用索引,不能做范围查询,以后可能会增加这方面的支持
有时iteye会给用户快递一些奖品,iteye可能会把用户曾经填过的地址保存下来。可以这么保存
在USER集合里增加一个address属性,没有留过地址的可以没有。
一个用户不一定只有一个地址,这个address就可以创建为一个字符串数组。
比如要返回所有只留了一个地址的用户。
db.USER.find(address:{$size:1});
有些时候,如果ITEYE想要知道用户更详细的地址信息,就要用更复杂的文档保存地址。比如:
{
_ID:ObjectID(.........),
accountName:'runfriends',
address:[{category:'home',city:'北京',district:'东城',street:'.....'},
{category:'company',city:'北京',district:'海淀',street:'........'}]
}
如果要查出所有家在北京的用户,要怎么做呢?
可能会这样写:
db.USER.find({'address.category':'home','address.city':'北京'})。
但是这样是错的!这行命令的意义是查询出所有地址里面分类包含home,而且地址所在城市包含北京的用户。有些用户的公司地址在北京,而家庭地址不是,这些用户也会被匹配到。
那么接下来就用到了$elemMatch
它只在需要匹配子文档的多个属性时才会用到
db.USER.find(address:{$elemMatch:{category:'home',city:'北京'}});
$not---取反值,只有在没有指定相反操作时才需要用到它。因为绝大部分操作符都有对应的相反操作,所以应当尽量使用相反操作符,比如正则表达式匹配没有相反操作
假如有一天,ITEYE只允许用户名以字母开头了就可以把所有不以字母开头的用户查出来给他们发一封邮件,让他们改名。
db.USER.find(accountName:{$not:/^[a-zA-Z]/})
当然$not也接收单值,但是不建议使用
$exists----检查某个属性的存在性。
比如要把所有包含附件的博文查询出来。
db.blog.find({attachment:{$exists:true}});
没有附件的就是db.blog.find({attachment:{$exists:false}});
或才可以这样做:
db.log.find({attachment:null});//不存在
db.blog.find({attachment:{$ne:null}});//存在
前面介绍BSON的时候说过空值使用nil,但是这里却用了null,是因为nil是BSON的定义,这里是JAVASCRIPT的语法
$mod-----求余数,不利用索引
假如说某天ITEYE心血来潮要给所有年龄能被4整队的用户快递一份奖品。
db.USER.find({age:{$mod:[4,0]}});//数组的第一个值是除数,第二个值是期望的余数
$type---以属性类型查询
虽然不建议在同一集合的不同文档相同属性名保存着不同类型的数据,但是有时由于程序bug或设计不严谨可能会出现这种情况。现在要把这种情况找出来,比如_ID属性有的是ObjectID类型,有的是整数。下面把所有_ID是字符串的文档找出来。
db.USER.find(_id:{$type:2})
db.USER.find(_id:{$not:{$type:7}});//把所有主键ObjectID类型的文档找出来
$or $and----逻辑运算
它们的意义就不多解释了。不过它们的用法非常有意思
比如找出所有月收入在20000以上或2000以下的用户
db.USER.find({$or:[{salary:{$gt:20000}},{salary:{$lt:2000}}]});
找出所有月收入在8000以上,20000以下的用户
db.USER.find({$and:[{salary:{$gte:8000}},{salary:{$lte:20000}}]});
查询嵌套文档和数组元素
前面的内容已经简单介绍一些嵌套文档和数组元素的查询。嵌套文档和数组的查询遵守相同的语法规则
1. 它们都采用点号分割嵌套文档的属性,如果是数组的索引就用从0开始的数字表示。
db.USER.find({'address.category':'home'});//这个是查出所有留了家庭地址的用户
如果想知道用户留下的第一个地址是不是家庭地址可以这么做:
db.USER.find({'address.0.category':'home'});
那么如果想只返回留了家庭地址的用户而又只返回家庭地址却不返回其它的地址该怎么做呢?
目前采用的数据模型恐怕做不到这一点,如果有这样的需求,恐怕只能为不同的地址定义不同的字段了。
当然如果代码规范规定第一个地址必须是家庭地址,那么可以这样做:
db.USER.find({'address.0.category':'home'},{'accountName':1,'address.0':1});
不过通常情况下,把第一个地址定义为默认地址更好一些。
$where-----接收一段javascript代码作为查询条件,不利用索引
假如说要查询所有闰年出生的用户
db.USER.find({$where:'var year=birthday.getFullYear();return year%4==0 && year%100>0 || year%400==0';});
或:
db.USER.find($where:'function(){var year=this.birthday.getFullYear();return year%4==0 && year%100>0 || year%400==0'}');
好了,关于mongo的查询方式就这么多了。下一篇将要简单说下聚合。
mongo真是个神奇的数据库,它还能很方便的完成很多数据分析工作。很多使用mongo的公司也确实在用它做数据分析。
相关推荐
使用MongoVue进行查询的方法,包含map及reduce的使用介绍
mongotemplate按日期聚合查询,实现 $year,$month聚合功能
一款mongodb的可视化查询软件...个人觉得还算好用...
如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。它的输出有以下几列:类似于MySQL的slowlog,MongoDB可以监控所有慢的以及不慢的查询。Profiler默认是关闭的,你...
mongodb_exporter监控 https://github.com/dcu/mongodb_exporter.git 下19年4月的编译结果文件 编译很麻烦 ,注意修改文件的可执行权限
Mongo 的查询语言基于 JSON 形式的标记,允许用户使用复杂的查询表达式来查询数据。 3. 完整的索引支持:包括文档内嵌对象及数组。 Mongo 的索引机制可以对文档中的内嵌对象及数组建立索引,极大地提高了查询效率。...
阿里mongo同步工具mongo-shake正是为了解决这些问题而设计的。 Mongo-shake 是阿里巴巴开发的一款高效、稳定且易于使用的MongoDB数据迁移工具,主要用于实现MongoDB集群之间的数据同步。它支持全量数据迁移和增量...
"MongoDB 使用 MongoTemplate 实现统计和分组" MongoDB 是一个非常流行的 NoSQL 数据库,它可以存储大量的数据,但是有时候我们需要对这些数据进行分析和利用。在本文中,我们将介绍如何使用 MongoTemplate 实现...
2. **强大的查询功能**:MongoVUE支持复杂的查询构建器,用户可以方便地创建和执行MongoDB查询语句(MongoDB Query Language,MQL),包括基本查询和聚合框架。这使得对数据库的查询和数据分析变得直观且高效。 3. ...
PHP为了方便开发者与MongoDB数据库打交道,提供了专门的MongoDB扩展——php_mongo。本文将详细介绍标题为"php_mongo_5.2.6.6"的扩展,它是专为PHP 5.2.6版本设计的一个重要组件。 一、php_mongo扩展简介 php_mongo...
它支持丰富的查询选项,包括基本查询、聚合查询以及正则表达式匹配,帮助用户精确定位所需的数据。 3. 文档编辑:MongoVUE提供了可视化的文档编辑器,让用户可以方便地以树状视图或表视图查看和编辑文档内容,同时...
mongoVUE.1.6.9.破解文件,解压后覆盖安装目录中的MongoVUE.exe文件 安装文件下载地址:http://www.mongovue.com/downloads/
3. **查询编辑器**:MongoVUE内置了强大的查询编辑器,支持编写和执行MongoDB的查询语句(如`find()`、`aggregate()`等)。用户可以利用查询编辑器进行复杂的查询操作,实时查看结果,这对于数据挖掘和问题排查非常...
5. **索引管理**:对于优化查询性能,MongoVUE允许用户创建、查看和删除索引。这使得数据库管理员能够根据查询模式调整索引策略,提高查询效率。 6. **脚本支持**:MongoVUE内置了MongoDB shell,用户可以直接在...
mongo docker 镜像
- **查询(Query)**:MongoTemplate提供了丰富的查询功能,如`findAll()`, `findById()`, `query()`, `findOne()`等,可以通过Query对象指定查询条件。 - **更新(Update)**:使用`updateFirst()`, `...
4. 查询构建器:对于不熟悉MongoDB查询语法的用户,MongoVUE的查询构建器是个很好的工具。它允许用户通过图形化界面构建查询,逐步选择需要的条件,最后自动生成对应的查询语句。 5. 性能监控:MongoVUE可以实时...
1. 查询构造器:Bugu-Mongo 1.12引入了一个强大的查询构造器,允许开发者用链式语法创建复杂查询,极大地提高了代码的可读性和可维护性。 2. 性能提升:通过对内部操作的优化,1.12版本的Bugu-Mongo在读写速度上有所...