虽然这些语句在开发当中不会使用,因为springdataMongoDB
封装的非常完美了。但是这里的语句思想和关系型数据库有些区别,所以需要拿出来详细的讲解一下。因为原生的语句没有搞明白,后面使用springdataMongoDB
拼接条件查询的时候心里没数,不知道该怎么去写。现在大多数教程讲的非常详细,分初级和高级,但是即使是高级,有很多经常使用到的语句依然没有讲到,这里还是很有必要去总结一下。
mongodb
是非关系型数据库,也就是nosql
。在使用起来是非常方便的,例如:在某个实体类中需要添加一个列,这样直接在实体类中添加就OK,不会影响mongodb
的,如果你添加一个列了,然后保存数据的时候,这个类存在数据,那么就以java
中保存这个集合的样子存入mongodb
中。所以在关系型数据库中说的表在mongodb
中不说表,说集合。
MongoDB是非关系型数据库当中最像关系型数据库的,所以我们通过它与关系型数据库的对比,来了解下它的概念。
SQL概念 | MongoDB概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
所以更改数据的时候需要注意,有可能我们更改的那个数据字段压根就不存在。所以在更改领域会出现一个方法叫做更改不存在就添加,然后返回编号 _id
。
注意:在非关系型数据库中是没有连表查询这种概念的,但是可以在集合中嵌套集合查询。因为我们在关系型数据库中的连表查询,最终的数据都是保存在集中。我们怎么保存在集合中,那么我们就以保存的这种方式直接存入mongodb就OK,但是mongodb还支持集合中嵌套集合的查询,可以给嵌套的那个集合添加条件筛选。
当前演示的mongodb
版本为 4.4 (这个就要注意一下,之前的版本或许有些操作不支持,之后也有不支持的情况,如果废气了呢???)。
观看关系型查询语句和mongodb
的语句对比,可以查看官方文档:https://docs.mongodb.com/master/reference/sql-comparison/
mongodb
使用的是 Bson
格式,Bson
是JSON
的一个升级版。语法和 JSON
都差不多。所以在书写的时候,不管条件怎么写一定都是一个JSON
格式的。这里第一个第括号是封装条件,那么条件之间使用 or
连接,or
里面可能有多个数据,那么又用了一个数组来存储,数组中又是一个一个的对象。从而就形成了 { $or: [ { status: "A" } , { age: 50 } ] }
这样的格式。
格式 | 符号描述 | 文字描述 |
---|---|---|
: (冒号) |
= |
等于 |
$lt |
< |
小于 |
$lte |
<= |
小于等于 |
$gt |
> |
大于 |
$gte |
>= |
大于等于 |
$ne |
!= |
不等于 |
$in |
in() |
包含 |
$nin |
不包含 |
create table people (
id mediumint not null
auto_increment,
user_id varchar(30),
age number,
status char(1),
primary key (id)
)
mongodb
创建表mongodb
不用创建表,如果你插入的数据指定的文档没有,那么会自动创建一个相应的文档,并保存。但是也可以显示的创建一个文档然后在向里面插入数据的。
db.people.insertOne( {
user_id: "abc123",
age: 55,
status: "A"
} )
sql
的方式insert into people(user_id,age,status) values ("bcd001",45,"a")
mongodb
方式
sql
和mongodb
查询对比注意
除非通过投影明确排除,否则该find()
方法始终将_id
字段包含在返回的文档中 。即使某些字段不包含在相应的查询中,下面的某些SQL查询也可能包含一个 反映该问题的字段。_id find()
投影:就只返回指定的字段。如果没有用投影查询,所有查询的语句都相当于sql
语句中的 select * from 表名
这样的语句。
MySQL
表MYSQL | mongodb |
select * from people;
语法:db.文档名.find();
db.people.find();
select id,user_id,status from people
db.people.find(
{},
{ user_id: 1, status: 1 }
)
注意:第一个 {},
不能去掉,表示筛选条件,例如sql
中的where
。后面这个大括号中的字段表示是否显示,如果为 1
显示,0
不显示。但是 _id
默认是显示的。
不显示编号 _id
SELECT user_id, status
FROM people
db.people.find(
{ },
{ user_id: 1, status: 1, _id: 0 }
)
SELECT *
FROM people
WHERE status = "A"
db.people.find(
{ status: "A" }
)
SELECT user_id, status
FROM people
WHERE status = "A"
db.people.find(
{ status: "A" },
{ user_id: 1, status: 1, _id: 0 }
)
SELECT *
FROM people
WHERE status != "A"
db.people.find(
{ status: { $ne: "A" } }
)
SELECT *
FROM people
WHERE status = "A"
AND age = 55
db.people.find(
{ status: "A",
age: 55 }
)
SELECT *
FROM people
WHERE status = "A"
OR age = 50
db.people.find(
{ $or: [ { status: "A" } , { age: 50 } ] }
)
注意:mongodb
使用的是 Bson
格式,Bson
是JSON
的一个升级版。语法和 JSON
都差不多。所以在书写的时候,不管条件怎么写一定都是一个JSON
格式的。
这里第一个第括号是封装条件,那么条件之间使用 or
连接,or
里面可能有多个数据,那么又用了一个数组来存储,数组中又是一个一个的对象。从而就形成了 { $or: [ { status: "A" } , { age: 50 } ] }
这样的格式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6Ct5bZI-1599817629990)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200911112212615.png)]
SELECT *
FROM people
WHERE age > 25
db.people.find(
{ age: { $gt: 25 } }
)
SELECT *
FROM people
WHERE age < 25
db.people.find(
{ age: { $lt: 25 } }
)
SELECT *
FROM people
WHERE age > 25
AND age <= 50
db.people.find(
{ age: { $gt: 25, $lte: 50 } }
)
SELECT *
FROM people
WHERE user_id like "%bc%"
db.people.find( { user_id: /bc/ } )
db.people.find( { user_id: { $regex: /bc/ } } )
SELECT *
FROM people
WHERE user_id like "bc%"
db.people.find( { user_id: /^bc/ } )
或者
db.people.find( { user_id: { $regex: /^bc/ } } )
user_id
升序排序,并且status = "A"
SELECT *
FROM people
WHERE status = "A"
ORDER BY user_id ASC
db.people.find( { status: "A" } ).sort( { user_id: 1 } )
注意:排序的时候,
sort()
方法是在find()
方法之外了。最后面这个1
表示升序排序,-1
表示降序排序。
SELECT *
FROM people
WHERE status = "A"
ORDER BY user_id DESC
db.people.find( { status: "A" } ).sort( { user_id: -1 } )
SELECT COUNT(*)
FROM people
db.people.count()
或者
db.people.find().count()
SELECT COUNT(user_id)
FROM people
db.people.count( { user_id: { $exists: true } } )
或者
db.people.find( { user_id: { $exists: true } } ).count()
SELECT COUNT(*)
FROM people
WHERE age > 30
db.people.count( { age: { $gt: 30 } } )
或者
db.people.find( { age: { $gt: 30 } } ).count()
例如:访问量人数,访问一次记录一次。那么我想统计访问过我的人,而不是访问量,这时就派的上用场了。
SELECT DISTINCT(status)
FROM people
db.people.aggregate( [ { $group : { _id : "$status" } } ] )
或者
db.people.distinct( "status" )
SELECT *
FROM people
LIMIT 1
db.people.findOne()
或者
db.people.find().limit(1)
findOne() 每次只能获取一条数据,如果我想获取2条3条呢?这时就需要使用下面这个语句了。
SELECT *
FROM people
LIMIT 5,10
db.people.find().limit(5).skip(10)
EXPLAIN SELECT *
FROM people
WHERE status = "A"
db.people.find( { status: "A" } ).explain()
详细查看该博客:https://www.cnblogs.com/tufujie/p/9413852.html
查询参数讲解:
id:选择标识符
select_type:表示查询的类型。
table:输出结果集的表
partitions:匹配的分区
type:表示表的连接类型
possible_keys:表示查询时,可能使用的索引
key:表示实际使用的索引
key_len:索引字段的长度
ref:列与索引的比较
rows:扫描出的行数(估算的行数)
filtered:按表条件过滤的行百分比
Extra:执行情况的描述和说明
status: "A"
所有数据select * from people where status: "A"
db.people.find(
{ status: "A" }
)
status: "A"
,返回 user_id,status
字段。select user_id,status from people where status: "A"
db.people.find(
{ status: "A" },
{ user_id: 1, status: 1, _id: 0 }
)
user_id,status
字段select user_id,status from people
db.people.find(
{ },
{ user_id: 1, status: 1, _id: 0 }
)
status: "D"
update people set status = 'D'
db.people.updateMany(
{},
{ $set: { status: "D" }}
)
status: "C"
的年龄更改为 20岁update people SET age = 20 where status = 'C'
db.people.updateMany(
{ status: "C" },
{ $set: { age: 20 } }
)
不管你是查询还是更改删除,在CRUD
操作的方法中。第一个个大括号都封装为条件,第二个大括号在做相应的操作。例如:
第一个大括号封装以什么样作为条件查询;
第二个大括号封装查询结果返回那些字段。
第一个大括号封装一什么样的条件更改;
第二个大括号封装将那些字段更改为什么样的数据。
注意:封装的条件写法都一样,不管是CRUD
操作,更改的都是一样的。这里所指的条件就类似于sql语句中的where条件。
关于文字搜索方面的东西,我们一般使用 Elasticsearch
来实现,所以mongodb
提供的我们暂时不研究,如果需要研究的参考官方文档:https://docs.mongodb.com/master/text-search/
向mongodb
中插入测试数据
db.inventory.insertMany([
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
db.inventory.find( {} )
select * from inventory
查询 status = "D"
的所有数据
db.inventory.find( { status: "D" } )
SELECT * FROM inventory WHERE status = "D"
$in查询
查询 status
等于 A
或者 B
的全部数据。
db.inventory.find( { status: { $in: [ "A", "D" ] } } )
SELECT * FROM inventory WHERE status in ("A", "D")
$lt
小于查询 status = "A"
并且 qty < 30
的全部数据
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
SELECT * FROM inventory WHERE status = "A" AND qty < 30
$or
或者查询 status = "A"
或者 qty < 30
的所有数据
db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
发现没有:如果多条件查询,没有 $or
标识的,默认是 and
来拼接。
SELECT * FROM inventory WHERE status = "A" OR qty < 30
查询 status: "A"
并且 qty < 30
或者 item
以p
结尾的全部数据。
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")
注意:$or
的后面有一个中括号的。
测试数据
db.inventory.insertMany( [
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
注意:这里的size
域不是一个值了,而是一个对象。将这个BSON
格式化后就很明确的发现这个问题。
{
item: "journal",
qty: 25,
size: {
h: 14,
w: 21,
uom: "cm"
},
status: "A"
}
size
这个对象中 h
域等于 14
并且 w=21
并且 uom=cm
的数据。db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
必选全部匹配才可以并且顺序也不能乱,例如:h
属性在w
属性前面的。我们封装条件时将w
封装在前面h
封装在w
后面,即使有相同数据存在,也查询不到的,所有顺序也很重要。
就像我们java
中判断一个对象是否相等,需要将这个对象中的每个属性值都必须相等才算这两个对象相等,这里也是相同的道理。
Book A = new Book("mongodb",89.56);
Book B = new Book("java",89.56);
如果我们判断这两个对象想不相等,不能直接 A == B ,这样永远不相等,而是要去判断对象里面的所有属性是否相等。应该要获取到A对象中的mongodb,与B对象中的java相互判断是否相等,在用A对象中的89.56和B对象中的89.56做判断。应该要这样去判断是否相等,这个思路搞清楚了,那么上面的mongodb按照条件查询方式就是这样判断的,如果有一个属性不匹配,那么都查询不到。
{
"_id" : ObjectId("5f5b37756259a58fcfc6c142"),
"item" : "journal",
"qty" : 25.0,
"size" : {
"h" : 14.0,
"w" : 21.0,
"uom" : "cm"
},
"status" : "A"
}
db.inventory.find( { size: { w: 21, h: 14, uom: "cm" } } )
size
这个对象中 h = 14 and w=21
的数据db.inventory.find( { size: { h: 14, w: 21 } } )
查询没有结果数据,因为size
对象中的数据全部都有 h、w、uom
这三个字段的。
如果需要在嵌套的字段上查询,使用点 .
来连接。
size
对象里面的 uom
属性值查询db.inventory.find( { "size.uom": "in" } )
{
"_id" : ObjectId("5f5b37756259a58fcfc6c143"),
"item" : "notebook",
"qty" : 50.0,
"size" : {
"h" : 8.5,
"w" : 11.0,
"uom" : "in"
},
"status" : "A"
}
{
"_id" : ObjectId("5f5b37756259a58fcfc6c144"),
"item" : "paper",
"qty" : 100.0,
"size" : {
"h" : 8.5,
"w" : 11.0,
"uom" : "in"
},
"status" : "D"
}
size
对象里面h
属性小于15的数据db.inventory.find( { "size.h": { $lt: 15 } } )
{
"_id" : ObjectId("5f5b37756259a58fcfc6c142"),
"item" : "journal",
"qty" : 25.0,
"size" : {
"h" : 14.0,
"w" : 21.0,
"uom" : "cm"
},
"status" : "A"
}
{
"_id" : ObjectId("5f5b37756259a58fcfc6c143"),
"item" : "notebook",
"qty" : 50.0,
"size" : {
"h" : 8.5,
"w" : 11.0,
"uom" : "in"
},
"status" : "A"
}
{
"_id" : ObjectId("5f5b37756259a58fcfc6c144"),
"item" : "paper",
"qty" : 100.0,
"size" : {
"h" : 8.5,
"w" : 11.0,
"uom" : "in"
},
"status" : "D"
}
{
"_id" : ObjectId("5f5b37756259a58fcfc6c146"),
"item" : "postcard",
"qty" : 45.0,
"size" : {
"h" : 10.0,
"w" : 15.25,
"uom" : "cm"
},
"status" : "A"
}
status: "D"
并且 size
对象中h<15
并且 size
对象中uom = in
的数据。db.inventory.find( { "size.h": { $lt: 15 }, "size.uom": "in", status: "D" } )
{
"_id" : ObjectId("5f5b37756259a58fcfc6c144"),
"item" : "paper",
"qty" : 100.0,
"size" : {
"h" : 8.5,
"w" : 11.0,
"uom" : "in"
},
"status" : "D"
}
测试数据
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
{ item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
{ item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
注意:这里的tags和dim_cm是一个数组。格式化一条数据出来更加清晰。
{
item: "journal",
qty: 25,
tags: ["blank", "red"],
dim_cm: [14, 21]
}
tags
数组为 ["red", "blank"]
的数据必须指定相等的条件,包括数组的顺序元素个数都不能变化。如果想指定某个域中包含数组的某个字段就可以匹配到,忽略其他元素,可以使用$all
运算符。$all
参考 https://docs.mongodb.com/master/reference/operator/query/all/#op._S_all
db.inventory.find( { tags: ["red", "blank"] } )
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14d"),
"item" : "notebook",
"qty" : 50.0,
"tags" : [
"red",
"blank"
],
"dim_cm" : [
14.0,
21.0
]
}
tags
域中的数组包含 red和blank
元素的数据db.inventory.find( { tags: { $all: ["red", "blank"] } } )
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14c"),
"item" : "journal",
"qty" : 25.0,
"tags" : [
"blank",
"red"
],
"dim_cm" : [
14.0,
21.0
]
}
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14d"),
"item" : "notebook",
"qty" : 50.0,
"tags" : [
"red",
"blank"
],
"dim_cm" : [
14.0,
21.0
]
}
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14e"),
"item" : "paper",
"qty" : 100.0,
"tags" : [
"red",
"blank",
"plain"
],
"dim_cm" : [
14.0,
21.0
]
}
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14f"),
"item" : "planner",
"qty" : 75.0,
"tags" : [
"blank",
"red"
],
"dim_cm" : [
22.85,
30.0
]
}
查询数组中全部满足条件的数据。如果需要查询某个元素满足条件,需要使用过滤器。https://docs.mongodb.com/master/core/document/#document-query-filter
dim_cm
数组中全部大于25的数据。db.inventory.find( { dim_cm: { $gt: 25 } } )
{
"_id" : ObjectId("5f5b3dad6259a58fcfc6c14f"),
"item" : "planner",
"qty" : 75.0,
"tags" : [
"blank",
"red"
],
"dim_cm" : [
22.85,
30.0
]
}
其实在第二部分mongodb和sql语句对比中已经提到过这个问题,这里在详细说一下。
因为在第一个大括号封装的全部都是条件,第二个大括号封装的是返回的字段。如果有第二个大括号存在,那么 _id
默认是显示的。
注意:在第二个大括号中封装返回字段时,如果只指定某些字段值为1,说明除了这几个字段值之外都不返回。如果只指定某些字段值为0,说明除了这几个字段值都返回。
注意:有些时候如果你觉得没有任何条件,你就将第一个大括号删除了,那么这个语句就只剩下一个大括号,就默认作为条件处理了。所以即使没有条件,第一个大括号也要保留,否则没有办法判断你指定的是条件还是指定返回字段。
演示数据
db.inventory.insertMany( [
{ item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
{ item: "notebook", status: "A", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
{ item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
{ item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
status: "A"
并返回 item和status
字段,_id
也默认返回了db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
第二个大括号中字段的后面这个
1
标识显示,0
不显示。
_id
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
item
字段和size
对象中的uom
域。db.inventory.find(
{ },
{ item: 1, "size.uom": 1 }
)
聚合操作处理数据记录并返回计算结果。聚合操作将来自多个文档的值分组在一起,并且可以对分组的数据执行各种操作以返回单个结果。MongoDB提供了三种执行聚合的方法:聚合管道,map-reduce函数和单一目的聚合方法。
aggregate()
聚合操作这里不是使用find()
方法了,而是 aggregate()
方法。
将数据根据指定条件查询出来,然后在进行结果在处理。
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 }
{ "_id" : ObjectId("512bc962e835e68f199c8687"), "author" : "dave", "score" : 85, "views" : 521 }
{ "_id" : ObjectId("55f5a192d4bede9ac365b257"), "author" : "ahn", "score" : 60, "views" : 1000 }
{ "_id" : ObjectId("55f5a192d4bede9ac365b258"), "author" : "li", "score" : 55, "views" : 5000 }
{ "_id" : ObjectId("55f5a1d3d4bede9ac365b259"), "author" : "annT", "score" : 60, "views" : 50 }
{ "_id" : ObjectId("55f5a1d3d4bede9ac365b25a"), "author" : "li", "score" : 94, "views" : 999 }
{ "_id" : ObjectId("55f5a1d3d4bede9ac365b25b"), "author" : "ty", "score" : 95, "views" : 1000 }
执行以下聚合操作
db.articles.aggregate(
[ { $match : { author : "dave" } } ]
);
查询出 author : "dave"
的所有数据
返回结果为
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 }
{ "_id" : ObjectId("512bc962e835e68f199c8687"), "author" : "dave", "score" : 85, "views" : 521 }
在执行以下操作
db.articles.aggregate( [
{ $match: { $or: [ { score: { $gt: 70, $lt: 90 } }, { views: { $gte: 1000 } } ] } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
查询 score > 70 并且 score < 90
或者 views >= 1000
的数据。然后在计算一共有多少条数据,查询结果为
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 }
{ "_id" : ObjectId("512bc962e835e68f199c8687"), "author" : "dave", "score" : 85, "views" : 521 }
以上都是 score > 70 and score < 90
{ "_id" : ObjectId("55f5a192d4bede9ac365b257"), "author" : "ahn", "score" : 60, "views" : 1000 }
{ "_id" : ObjectId("55f5a192d4bede9ac365b258"), "author" : "li", "score" : 55, "views" : 5000 }
{ "_id" : ObjectId("55f5a1d3d4bede9ac365b25b"), "author" : "ty", "score" : 95, "views" : 1000 }
查询结果
{ "_id" : null, "count" : 5 }
按指定的_id
表达式对输入文档进行分组,并针对每个不同的分组输出文档。_id
每个输出文档的字段都包含唯一的按值分组。输出文档还可以包含包含某些累加器表达式值的计算字段。
{
$group:
{
_id: <expression>, // 分组表达式
<field1>: { <accumulator1> : <expression1> },
...
}
}
_id
:需要分组的字段。如果指定_id null
或任何其他常数值,那么此$group阶段将整体计算所有输入文档的累积值。
field
:*可选的。*使用累加器运算符进行计算 。
累加运算:https://docs.mongodb.com/master/reference/operator/aggregation/group/#accumulator-operator
db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
{ "_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },
{ "_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
{ "_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])
db.sales.aggregate( [
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
] )
虽然使用到分组,但是
_id:null
,并没有指定具体的字段。count: { $sum: 1 }
是查询所以条数,count
相当于别名,随便写的。
SELECT COUNT(*) AS count FROM sales
{ "_id" : null, "count" : 8 }
item
字段分组db.sales.aggregate( [ { $group : { _id : "$item" } } ] )
{ "_id" : "abc" }
{ "_id" : "jkl" }
{ "_id" : "def" }
{ "_id" : "xyz" }
就只能返回item
不重复的数据了。
db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
{ "_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },
{ "_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
{ "_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])
db.sales.aggregate([
// 第一个阶段(筛选)
{
$match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } }
},
// 第二阶段(分组)
{
$group : {
_id : { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageQuantity: { $avg: "$quantity" },
count: { $sum: 1 }
}
},
// 第三阶段(排序)
{
$sort : { totalSaleAmount: -1 }
}
])
$dateToString
表达式参考:https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/index.html
$multiply
参考: https://docs.mongodb.com/manual/reference/operator/aggregation/multiply/index.html
过滤出 date > = 2014-01-01 and data < 2015-01-01
的数据。
按照日期分组,并将 price
字段乘以 quantity
字段,并求 quantity
字段的平均值,最后在计算总和。这了计算的总和是计算当前这一组有多少条数据。
例如:第一次过滤满足 date > = 2014-01-01 and data < 2015-01-01
这个条件的数据就只有一下这几条。
{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") }
按照时间分组,因为上面写的时间格式为 %Y-%m-%d
年与日,所以其他数据不参考。
2014-03-01
2014-03-15
2014-04-04
时间就只有这几个,那么在计算 2014-03-01
一共有几条数据,刚好只有2
条。_id为 1和2这两条数据
。时间为 2014-03-15
只有_id为3
的这一条数据。时间为 2014-04-04
的有 4和5
这两条数据。分组查询下来的结果就是一下这样。
{
"_id" : "2014-04-04",
"totalSaleAmount" : { "$numberDecimal" : "200" },
"averageQuantity" : 15.0,
"count" : 2.0
}
{
"_id" : "2014-03-15",
"totalSaleAmount" : { "$numberDecimal" : "50" },
"averageQuantity" : 10.0,
"count" : 1.0
}
{
"_id" : "2014-03-01",
"totalSaleAmount" : { "$numberDecimal" : "40" },
"averageQuantity" : 1.5,
"count" : 2.0
}
使用sql
语句就是这样。
SELECT date,
Sum(( price * quantity )) AS totalSaleAmount,
Avg(quantity) AS averageQuantity,
Count(*) AS Count
FROM sales
GROUP BY Date(date)
ORDER BY totalSaleAmount DESC
null
的时候之前我还以为这种方式不可取,用处不大。现在才发现,这种方式用于统计还是特备方便的。
db.sales.aggregate([
{
$group : {
_id : null,
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageQuantity: { $avg: "$quantity" },
count: { $sum: 1 }
}
}
])
统计:
price
乘以quantity
的总和。在求quantity
的平均值。
{
"_id" : null,
"totalSaleAmount" : { "$numberDecimal" : "452.5" },
"averageQuantity" : 7.875,
"count" : 8.0
}
sql实现的写法
SELECT Sum(price * quantity) AS totalSaleAmount,
Avg(quantity) AS averageQuantity,
Count(*) AS Count
FROM sales
更多的聚合管道表达式参考:https://docs.mongodb.com/master/reference/operator/aggregation-pipeline/