mongo查询操作符

一  基本概念

MongoDB中数据的结构为:库、集合、文档

1 数据库

多个集合可以组成数据库。MongoDb的单个实例可以容纳多个独立的数据库,每个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

数据库通过名字来标识,库名可以是满足以下条件的任意UTF-8字符串:

不能是空字符串("")。

不得含有' '(空格)、.、$、/、\和\0 (空宇符)。

应全部小写。

最多64字节。

Mongo保留库

admin:从权限角度看,这是“root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务端命令也只能从这个数据库运行,比如关闭数据库。

local:这个数据库不会被复制。可以用来存储限于需要储存在本地单台服务器的集合

config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的信息。

2 集合

集合容纳文档,没有模式。比起关系型数据库,集合可以存放任意文档。但把各种文档放进一个集合在管理和效率是都不是好的方式,推荐用关系型数据库的思维来管理文档结构。

集合通过名字来标识,集合名可以是满足以下条件的任意UTF-8字符串:

集合名不能是空字符串""。

集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。

集合名不能以"system."开头,这是为系统集合保留的前缀。

用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

3 文档

文档是MongoDB中数据的基本单元(类似于关系型数据库中的行)。多个键及其关键的值有序的放置在一起便是文档。MongoDB的文件储存格式为BSON。

示例:{"name":"dongbo","age":"100"}

注意:

文档一定是用“{}”括起来

文档的键是双引号括起来的字符串,不能重复

不能含有\0 (空字符)。这个字符用来表示键的结尾。

.和$有特别的意义,只有在特定环境下才能使用。

以下划线"_"开头的键是保留的(不是严格要求的)。

文档的值可以是字符串、数字、数组、文档等mongo支持的数据类型。字符串需用双引号括起来。

MongoDB区分类型和大小写

键值对是有序的。

二 查询

find函数查询集合中所有符合查询条件的文档并返回结果为游标的文档集合。findOne查询一个符合查询条件的文档。

在mongo shell中我们不需要JavaScript游标处理方法就可以直接访问作为查询结果的文档集合。当执行查询操作时,mongo shell直接自动的对游标执行迭代操作并显示前20条文档,输入"it"显示接下来的20条文档。

设置显示文档数:DBQuery.shellBatchSize=50,设置完后执行find函数就能显示出前50条文档记录。

如果我们通过变量保存find返回的游标,其不会自动进行遍历:

var cursor = db.inventory.find()

实际发生的是,调用find后,此时并不会真正的访问数据库,而是等待开始要求获取结果的时候才向数据库发送查询请求。当调用hasNext或者next方法时才会真正访问数据库,这就是懒加载的过程。

先插入一些数据:

db.inventory.insert({"name":"t1","amount":16,"tags":["apple", "banana"]})

db.inventory.insert({"name":"t2","amount":50,"tags":["banana", "orange"]})

db.inventory.insert({"name":"t3","amount":58,"tags":["orange", "apple"]})

1 游标操作

处理游标有三个函数:limit、skip、sort。limit用来限制返回的文档数量,skip用来指定忽略前面的n条文档,sort用来对文档进行排序。三个函数有优先级关系:先sort排序,然后skip跳过前面n个值,最后limit限制文档最大返回数量。

db.inventory.find({"amount":{$lt:60}}).sort({"amount":1}).skip(1).limit(2)

{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

示例中操作语句的写法只是示意优先级关系,返回结果跟3个函数的书写顺序无关。

示例中按照amount排序,因为amount的值都是整数,所以排序依据数字的大小而来。然而Mongo中对键并不会强制要求是什么类型,一个集合中多个文档的某一个键对应的值可能是mongodb支持的不同类型,应对这种情况,MongoDb预先定义了一个顺序,从小到大依次为:

(1):最小值

(2):null

(3):数字(整型,长整型,双精度)

(4):字符串

(5):对象/文档

(6):数组

(7):二进制数据

(8):对象ID

(9):布尔值

(10):日期型

(11):时间戳

(12):正则表达式

(13):最大值

按照这个顺序,便可以跨类型排序。

排序示例:

db.inventory.find().sort({"amount":-1})

{ "_id" :ObjectId("55a13b27b26f97b960389a5f"), "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

限制文档行数示例:

db.inventory.find().limit(2)

{ "_id" :ObjectId("55a13b26b26f97b960389a5d"), "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "_id" :ObjectId("55a13b26b26f97b960389a5e"), "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

2 包装

包装查询会用到一些额外的键:

$query

$orderby

$maxscan : integer指定查询时最多扫描文档的数量

$min : document查询的开始条件

$max : document查询的结束条件

$hint : document指定服务器使用哪些索引进行查询

$explain : boolean获取查询细节,如用到的索引,结果数量,耗时等,类似于关系数据库这边查看执行计划。并不会真正执行查询

$snapshot:boolean确保查询的结果是在查询执行那一刻的一致快照。

示例:

db.inventory.find({$query:{amount:{$lt:58}},$orderby:{amount:-1},$hint:{"name":1}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

3 查询指定的键

示例:

db.inventory.find({},{"_id":0,"name":1,"tags":1})

{ "name" :"t1", "tags" : [ "apple", "banana" ] }

{ "name" :"t2", "tags" : [ "banana", "orange" ] }

{ "name" :"t3", "tags" : [ "orange", "apple" ] }

db.inventory.find({"amount":{$eq:50}},{"_id":0,"name":1,"tags":1})

{ "name" :"t2", "tags" : [ "banana", "orange" ] }

“0”表示不显示,“1”表示显示

4 使用正则表达式

示例:

db.inventory.find({"name":/1/},{"_id":0,"name":1,"tags":1})

{ "name" :"t1", "tags" : [ "apple", "banana" ] }

返回name中匹配“1”的结果

5 去除重复

示例:

db.inventory.distinct("name")

[ "t1","t2", "t3" ]

去重的作用是对某个键查询的结果除去重复结果,关系型数据库中distinct的作用也一样

6 查询集合中总文档数:

db.inventory.count()

7 查询内嵌文档:

查询文档有两种方式,一种是完全匹查询,另一种是针对键/值对查询。

db.profile.insert({"name" : [{"first" : "db", "last":"z"}]})

db.profile.insert({"name" : [{"first" : "bing", "last" : "pan" }]})

db.profile.insert({"name" : [{"first" : "bol", "last" : "z" }]})

内嵌文档的完全匹配查询和数组的完全匹配查询一样,内嵌文档内键值对的数量,顺序都必须一致才会匹配:

db.profile.find({"name.first":"bol"})

{ "_id" :ObjectId("55a15e53b26f97b960389a6c"), "name" : [ {"first" : "bol", "last" : "z" } ] }

采用针对键/值对查询,通过点来精确表示内嵌文档的键。当内嵌文档变得复杂后,如键的值为内嵌文档的数组,内嵌文档的匹配需要些许技巧,例如使用$elemMatch操作符。

db.profile.find({"name":{$elemMatch:{"first":"bing","last":"pan"}}})

{ "_id" :ObjectId("55a15e53b26f97b960389a6b"), "name" : [ {"first" : "bing", "last" : "pan" } ] }

elemMatch投影操作符将限制查询返回的数组字段的内容只包含匹配elemMatch条件的数组元素。

注意:

数组中元素是内嵌文档。

如果多个元素匹配$elemMatch条件,操作符返回数组中第一个匹配条件的元素。

8 控制查询返回的数组中元素的个数

使用$slice操作符,$slice:1的意思是返回数组值中前1个元素

db.inventory.find({amount:{$gt:50}},{"_id":0})

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

db.inventory.find({amount:{$gt:50}},{tags:{$slice:1},"_id":0})

{ "name" :"t3", "amount" : 58, "tags" : ["orange" ] }

$slice:[1,1],第一个1表示在数组中跳过的项目数,第二个值表示返回的项目数。

db.inventory.find({amount:{$gt:50}},{tags:{$slice:[1,1]},"_id":0})

{ "name" :"t3", "amount" : 58, "tags" : [ "apple"] }

9 查询操作符:

数组查询操作符:

$all

用法:{ field : {$all:[value1,value2]}},查询数组中包含指定值的文档,条件的数组是文档中已有数组的子集

示例:

db.inventory.find({"tags":{$all:["apple"]}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

当只需要匹配数组的一个值时,简便写法:

db.inventory.find({"tags":"apple"},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$size

用法:{ field:{ $size:size } },查询数组中元素的数量等于size的文档,size必须是数字

示例:

db.inventory.find({"tags":{$size:2}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$elemMatch

用法:{field:{$elemMatch:{exp}}} ,文档内部存在由文档作为元素组成数组,如果要以数组中的文档作为查询条件,就可以用$elemMatch操作符指向内部文档。

算术操作符

$gt

用法:{ field : { $gt:value } },查询键值大于指定值的所有文档

示例:

db.inventory.find({"amount":{$gt:50}},{"_id":0})

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$gte

用法:{ field: { $gte: value } },查询键值不小于指定值的所有文档。

$lt

用法:{ field: { $lt: value } },查询键值小于指定值的所有文档。

$lte

用法:{ field: { $lte: value } },查询键值不大于指定值的所有文档。

$eq

用法:{ field: { $eq: value } },查询键值等于指定值的所有文档。

$ne

用法:{ field: { $ne: value } },查询键值不等于指定值的所有文档。

$in

用法:{ field: { $in: [value1,value2] } },查询键值等于指定数组中任意值的文档

示例:

db.inventory.find( { amount : {$in:[50,58]} },{"_id":0})

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$nin

用法:{ field: { $nin: [ value1, value2]} },查询键不存在或者键值不等于指定数组的任意值的文档

示例:

db.inventory.find({amount:{$nin:[50,58]}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

逻辑操作符

$and

用法:{ $and: [ { }, { } ] },$and指定一个至少包含两个表达式的数组,选择出满足该数组中所有表达式的文档。and操作符使用短路操作,若第一个表达式的值为“false”,余下的表达式将不会执行。

示例:

db.inventory.find({$and:[{"amount":{$gt:50}},{"amount":{$lt:70}}]},{"_id":0})

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$or

用法:{ $or: [ { }, { }] },$or执行逻辑OR运算,指定一个至少包含两个表达式的数组,选择出至少满足数组中一条表达式的文档。

示例:

db.inventory.find({$or:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$nor

用法:{ $nor: [ { }, { }] },$nor执行逻辑NOR运算,指定一个至少包含两个表达式的数组,选择出都不满足该数组中所有表达式的文档。

示例:

db.inventory.find({$nor:[{"amount":{$gt:50}},{"amount":{$lt:50}}]},{"_id":0})

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

$not

用法:{ field: { $not: { } } },$not执行逻辑NOT运算,选择出不能匹配表达式的文档,包括没有指定键的文档。$not操作符不能独立使用,必须跟其他操作一起使用(除$regex)。

示例:

db.inventory.find({"amount":{$not:{$gt:50}}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

元素操作符

$exists

用法:{ field: { $exists: } },断言字段是否存在,如果$exists的值为true,选择存在该字段的文档;若值为false则选择不包含该字段的文档。

示例:

db.inventory.find({qty:{$exists:false}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

db.inventory.find({amount:{$exists:true,$nin:[16,58]}},{"_id":0})

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

$mod

用法:{ field: { $mod: [ divisor, remainder ]} },匹配字段值对(divisor)取模,值等于(remainder)的文档。

示例:

db.inventory.find({amount:{$mod:[5,0]}},{"_id":0})

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

$type

用法:{ field: { $type: } },选择字段值为指定的BSON数据类型的文档.

type>使用下面类型对应的编号:

类型    类型    编号

Double    双精度    1

String    字符串    2

Object    对象    3

Array    数组    4

Binary data    二进制对象    5

Object id    对象id    7

Boolean    布尔值    8

Date    日期    9

Null    未定义    10

Regular  Expression    正则表达式    11

JavaScript    JavaScript代码    13

Symbol    符号    14

JavaScript (with  scope)    JavaScript代码(带范围)    15

32-bit integer    32位整数    16

Timestamp    时间戳     17

64-bit integer     64位整数    18

Min key    最小键    255

Max key    最大键    127

如果文档的键值是一个数组。那么$type将对数组里面的元素进行类型匹配而不是键值数组本身。

示例:

db.inventory.find({"amount":{$type:1}},{"_id":0})

{ "name" :"t1", "amount" : 16, "tags" : ["apple", "banana" ] }

{ "name" :"t2", "amount" : 50, "tags" : ["banana", "orange" ] }

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

MinKey和Maxkey表示可能的最小值和最大值,查询示例:

db.post.insert({x:MinKey})

db.post.find({"x":{$type:-1}})

{ "_id" :ObjectId("55a14d2eb26f97b960389a61"), "x" : {"$minKey" : 1 } }

JavaScript查询操作符

$regex

regex操作符查询中可以对字符串的执行正则匹配。MongoDB使用Perl兼容的正则表达式(PCRE)库来匹配正则表达式,可以使用正则表达式对象或者regex操作符。

options(regex提供四个选项标志)

i如果设置了这个修饰符,忽略大小写。

m如果设置了这个修饰符,忽略换行。

s如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个修饰符,点号不匹配换行符。

x如果设置了这个修饰符,模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略,并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。这个修饰符使被编译模式中可以包含注释。注意:这仅用于数据字符。空白字符还是不能在模式的特殊字符序列中出现,比如序列。

注:JavaScript只提供了i和m选项,x和s选项必须使用$regex操作符。

示例:查询name键值以“3”结尾的文档

db.inventory.find({name:/3/i},{"_id":0});

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

db.inventory.find( { name: { $regex: '.3',$options: 'i' } },{"_id":0} )

{ "name" :"t3", "amount" : 58, "tags" : ["orange", "apple" ] }

$where

$where操作符功能强大而且灵活,他可以使用任意的JavaScript作为查询的一部分,包含JavaScript表达式的字符串或者JavaScript函数。不是非常必要时,一定要避免使用"where"査询,因为它们在速度上要比常规査询慢很多,每个文档都要从BSON转换成JavaScript对象,然后通过"where"的表达式来运行,同时还不能利用索引。所以,只在走投无路时才考虑"$where"这种用法。

示例:比较文档中的两个键的值是否相等。

db.fruit.insert({"apple":1,"banana": 4, "peach" : 4})

db.fruit.insert({"apple":3,"banana": 3, "peach" : 4})

查找出banana等于peach键值的文档(4种方法):

db.fruit.find( { $where: "this.banana== this.peach" } )

{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }

db.fruit.find( { $where: "obj.banana== obj.peach" } )

{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }

db.fruit.find( { $where: function() {return (this.banana == this.peach) } } )

{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }

db.fruit.find( { $where: function() {return obj.banana == obj.peach; } } )

{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }

查出文档中存在的两个键的值相同的文档

db.fruit.find({$where:function () {

    for (var current in this) {

        for (var other in this) {

            if (current != other &&this[current] == this[other]) {

                return true;

            }

        }

    }

    return false;

 }});

{ "_id" :ObjectId("55a14f35b26f97b960389a62"), "apple" : 1,"banana" : 4, "peach" : 4 }

{ "_id" :ObjectId("55a14f35b26f97b960389a63"), "apple" : 3,"banana" : 3, "peach" : 4 }

三 语法总结

1、find操作的永远是文档,所以find()中一定要有{}包裹限定条件

2、限定条件文档编写时,如果是key,后面都是跟文档,如果是操作符,后面跟数组或字符串

3、$and$or$nor$not逻辑或、与、非操作符都是后面跟数组,并在数组中将多个限定文档作为数组的元素。

4、$gt $lt $gte $lte$eq $ne算术操作符后面都是跟数字

5、$all查询的是条件数组问文档数组的子集,$in查询的是键的值在条件数组中

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容