mongo简介——查询(续)
mongo简介——查询(续)mongo简介——查询http://www.zzzyk.com/database/201302/190196.html这一篇继续说一些查询相关的问题。上一篇提到的查询都是等值条件查询,但是我们更多的时候需要模糊查询、非等值查询、模式匹配等。mongo不是key-value存储,它支持非常灵活复杂的查询方式,甚至比rdbms还要灵活的多,当然也复杂的多。www.zzzyk.com另外,需要多说一点,用nosql归类这些数据库并不准确,只是RDBMS都是用SQL的,而它们都是不用SQL的,所以就用nosql来归类这些数据库了。其实从技术上考虑,完全可以实现一个非RDBMS而继续使用SQL的全部特性来操作和管理数据库。当然为了表达方便这一系列文章仍然使用nosql这一并不准确的名词。既然没有了sql,要操作mongo自然就要使用其它的方式了。前面的文章都已经出现过一些了,就是用mongo定义的数据库操作api配合它的文档形式的操作参数完成数据库的创建、修改、删除和数据的增删改查。关于查询的参数在上一篇几乎已经说完,还剩下的就是find*和count的第一个参数。由于find*的第一个参数和count参数都一样,本文就只以find函数做说明。www.zzzyk.com查询某个字段比指定值小:$lt//假设存在集合USERdb.USER.find({REGIST_DATE:{$lt:new Date(2013,0,1)}});/*前面提到过mongo完全遵守JAVASCRIPT语法,在JAVASCRIPT里面,月份是从0开始的,即上面的查询是查询2013-1-1以前注册的的用户。*/查询某个字段比指定值大:$gtdb.USER.find({REGIST_DATE:{$gt:new Date(2013,0,1)}});/*$lt表示小于,表示大于的自然就是$gt了*/大于等于:$gte 小于等于:$ltedb.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 indb.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}});//存在前面介绍B上一个:Oracle数据库定时器Job
下一个:mongo tips简析