MongoDB - 聚合阶段 $match、$sort、$limit

文章目录

    • [1. $match 聚合阶段](#1. $match 聚合阶段)
      • [1. 构造测试数据](#1. 构造测试数据)
      • [2. $match 示例](#2. $match 示例)
      • [3. $match 示例](#3. $match 示例)
    • [2. $sort 聚合阶段](#2. $sort 聚合阶段)
      • [1. 排序一致性问题](#1. 排序一致性问题)
      • [2. $sort 示例](#2. $sort 示例)
    • [3. $limit 聚合阶段](#3. $limit 聚合阶段)

1. $match 聚合阶段

$match 接受一个指定查询条件的文档。

$match 阶段语法:

{ $match: { <query> } }

$match 查询语法与读取操作查询语法相同,即 $match 不接受原始聚合表达式。要在 $match 中包含聚合表达式,请使用 $expr 查询表达式:

{ $match: { $expr: { <aggregation expression> } } }

尽可能早地将 $match 放在聚合管道中,由于 $match 限制了聚合管道中的文档总数,因此早期的 $match 操作会最大限度地减少管道中的处理量。

1. 构造测试数据

db.articles.drop()

db.articles.insertMany([
    {
        "_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
    }
])

2. $match 示例

使用 $match 来执行简易等值匹配,$match 会选择 author 字段等于 dave 的文档,而聚合返回以下内容:

db.articles.aggregate(
    [ { $match : { author : "dave" } } ]
);

// 1
{
    "_id": ObjectId("512bc95fe835e68f199c8686"),
    "author": "dave",
    "score": 80,
    "views": 100
}

// 2
{
    "_id": ObjectId("512bc962e835e68f199c8687"),
    "author": "dave",
    "score": 85,
    "views": 521
}

SpringBoot 整合 MongoDB 实现:

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void aggregateTest() {
        // $match阶段
        Criteria criteria = Criteria.where("author").is("dave");
        MatchOperation match = Aggregation.match(criteria);

        Aggregation aggregation = Aggregation.newAggregation(match);

        // 执行聚合管道操作
        AggregationResults<Article> results
                = mongoTemplate.aggregate(aggregation, Article.class, Article.class);
        List<Article> mappedResults = results.getMappedResults();

        // 打印结果
        mappedResults.forEach(System.out::println);
        //Article(id=512bc95fe835e68f199c8686, author=dave, score=80, views=100)
        //Article(id=512bc962e835e68f199c8687, author=dave, score=85, views=521)
    }
}

这里输出文档直接使用了Article.class,可以重新定义实体类接收输出文档的字段:

java 复制代码
@Data
public class AggregationResult {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}
java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void aggregateTest() {
        // $match阶段
        Criteria criteria = Criteria.where("author").is("dave");
        MatchOperation match = Aggregation.match(criteria);

        Aggregation aggregation = Aggregation.newAggregation(match);

        // 执行聚合管道操作
        AggregationResults<AggregationResult> results
                = mongoTemplate.aggregate(aggregation, Article.class, AggregationResult.class);
        List<AggregationResult> mappedResults = results.getMappedResults();

        // 打印结果
        mappedResults.forEach(System.out::println);
        //AggregationResult(id=512bc95fe835e68f199c8686, author=dave, score=80, views=100)
        //AggregationResult(id=512bc962e835e68f199c8687, author=dave, score=85, views=521)
    }
}

3. $match 示例

使用 $match 管道操作符选择要处理的文档,然后将结果导入到 $group 管道操作符,以计算文档的数量:

db.articles.aggregate( [
  // 第一阶段
  { $match: { $or: [ { score: { $gt: 70, $lt: 90 } }, { views: { $gte: 1000 } } ] } },
  // 第二阶段
  { $group: { _id: null, count: { $sum: 1 } } }
] );

第一阶段:

$match 阶段选择 score 大于 70 但小于 90views 大于或等于 1000 的文档。

第二阶段:

将 m a t c h 阶段筛选的文档通过管道传送到 ' match 阶段筛选的文档通过管道传送到 ` match阶段筛选的文档通过管道传送到'group` 阶段进行计数。

// 1
{
    "_id": null,
    "count": 5
}

SpringBoot 整合 MongoDB 实现:

java 复制代码
@Data
@Document(collection = "articles")
public class Article {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}
java 复制代码
@Data
public class AggregationResult {
    private String id;
    private Integer count;
}
java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void aggregateTest() {
        // 第一阶段
        Criteria criteria = new Criteria();
        criteria.orOperator(Criteria.where("score").gt(70).lt(90), Criteria.where("views").gte(1000));
        MatchOperation match = Aggregation.match(criteria);

        // 第二阶段
        GroupOperation group = Aggregation.group().count().as("count");

        // 组合上面的2个阶段
        Aggregation aggregation = Aggregation.newAggregation(match,group);

        // 执行聚合管道操作
        AggregationResults<AggregationResult> results
                = mongoTemplate.aggregate(aggregation, Article.class, AggregationResult.class);
        List<AggregationResult> mappedResults = results.getMappedResults();

        // 打印结果
        mappedResults.forEach(System.out::println);
        // AggregationResult(id=null, count=5)
    }
}

2. $sort 聚合阶段

$sort 将所有输入文档进行排序,然后按照排序将其返回至管道。

{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }

$sort 接受排序依据的字段及相应排序顺序的文档。当 sort order=1时升序排序,sort order=-1 降序排序。

如果对多个字段进行排序,则按从左到右的顺序进行排序。例如,在上面的表单中,文档首先按 field1 排序。然后,具有相同 field1 值的文档将按 field2 进一步排序。

1. 排序一致性问题

MongoDB 不按特定顺序将文档存储在集合中。对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。如果需要一致的排序顺序,请在排序中至少纳入一个包含唯一值的字段。

db.restaurants.drop()

db.restaurants.insertMany( [
   { "_id" : 1, "name" : "Central Park Cafe", "borough" : "Manhattan"},
   { "_id" : 2, "name" : "Rock A Feller Bar and Grill", "borough" : "Queens"},
   { "_id" : 3, "name" : "Empire State Pub", "borough" : "Brooklyn"},
   { "_id" : 4, "name" : "Stan's Pizzaria", "borough" : "Manhattan"},
   { "_id" : 5, "name" : "Jane's Deli", "borough" : "Brooklyn"},
] )

以下命令使用 $sort 阶段对 borough 字段进行排序:

db.restaurants.aggregate(
   [
     { $sort : { borough : 1 } }
   ]
)

在此示例中,排序顺序可能不一致,因为 borough 字段包含 ManhattanBrooklyn 的重复值。文档按 borough 的字母顺序返回,但具有 borough 重复值的文档的顺序在多次执行同一排序中可能不相同。

要实现一致的排序,可以在排序中添加一个仅包含唯一值的字段。以下命令使用 $sort 阶段对 borough 字段和 _id 字段进行排序:

db.restaurants.aggregate(
   [
     { $sort : { borough : 1, _id: 1 } }
   ]
)

由于 _id 字段始终保证包含唯一值,因此在同一排序的多次执行中返回的排序顺序将始终相同。

2. $sort 示例

db.articles.drop()

db.articles.insertMany([
    {
        "_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": 55,
        "views": 50
    },
    {
        "_id": ObjectId("55f5a1d3d4bede9ac365b25a"),
        "author": "li",
        "score": 94,
        "views": 999
    },
    {
        "_id": ObjectId("55f5a1d3d4bede9ac365b25b"),
        "author": "ty",
        "score": 95,
        "views": 1000
    }
])

对于要作为排序依据的一个或多个字段,可以将排序顺序设置为 1-1 以分别指定升序或降序。

db.articles.aggregate(
   [
     { $sort : { score : -1, views: 1 } }
   ]
)

// 1
{
    "_id": ObjectId("55f5a1d3d4bede9ac365b25b"),
    "author": "ty",
    "score": 95,
    "views": 1000
}

// 2
{
    "_id": ObjectId("55f5a1d3d4bede9ac365b25a"),
    "author": "li",
    "score": 94,
    "views": 999
}

// 3
{
    "_id": ObjectId("512bc962e835e68f199c8687"),
    "author": "dave",
    "score": 85,
    "views": 521
}

// 4
{
    "_id": ObjectId("512bc95fe835e68f199c8686"),
    "author": "dave",
    "score": 80,
    "views": 100
}

// 5
{
    "_id": ObjectId("55f5a192d4bede9ac365b257"),
    "author": "ahn",
    "score": 60,
    "views": 1000
}

// 6
{
    "_id": ObjectId("55f5a1d3d4bede9ac365b259"),
    "author": "annT",
    "score": 55,
    "views": 50
}

// 7
{
    "_id": ObjectId("55f5a192d4bede9ac365b258"),
    "author": "li",
    "score": 55,
    "views": 5000
}

SpringBoot 整合 MongoDB:

java 复制代码
@Data
@Document(collection = "articles")
public class Article {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}

@Data
public class AggregationResult {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}
java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void aggregateTest() {
        // $sort阶段
        SortOperation sortOperation = Aggregation
                .sort(Sort.by(Sort.Direction.DESC, "score"))
                .and(Sort.by(Sort.Direction.ASC, "views"));

        Aggregation aggregation = Aggregation.newAggregation(sortOperation);

        // 执行聚合查询
        AggregationResults<AggregationResult> results
                = mongoTemplate.aggregate(aggregation, Article.class, AggregationResult.class);
        List<AggregationResult> mappedResults = results.getMappedResults();

        // 打印结果
        mappedResults.forEach(System.out::println);
        //AggregationResult(id=55f5a1d3d4bede9ac365b25b, author=ty, score=95, views=1000)
        //AggregationResult(id=55f5a1d3d4bede9ac365b25a, author=li, score=94, views=999)
        //AggregationResult(id=512bc962e835e68f199c8687, author=dave, score=85, views=521)
        //AggregationResult(id=512bc95fe835e68f199c8686, author=dave, score=80, views=100)
        //AggregationResult(id=55f5a192d4bede9ac365b257, author=ahn, score=60, views=1000)
        //AggregationResult(id=55f5a1d3d4bede9ac365b259, author=annT, score=55, views=50)
        //AggregationResult(id=55f5a192d4bede9ac365b258, author=li, score=55, views=5000)
    }
}

3. $limit 聚合阶段

$limit 聚合阶段限制传递至管道。$limit 取一个正整数,用于指定传递的最大文档数量:

{ $limit: <positive 64-bit integer> }

如果将 $limit 阶段与以下任何一项一起使用:

  • $sort 聚合阶段
  • sort() 方法
  • sort 命令

在将结果传递到$limit阶段之前,请务必在排序中至少包含一个包含唯一值的字段。

db.articles.drop()

db.articles.insertMany([
    {
        "_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([
   { $limit : 5 }
]);

// 1
{
    "_id": ObjectId("512bc95fe835e68f199c8686"),
    "author": "dave",
    "score": 80,
    "views": 100
}

// 2
{
    "_id": ObjectId("512bc962e835e68f199c8687"),
    "author": "dave",
    "score": 85,
    "views": 521
}

// 3
{
    "_id": ObjectId("55f5a192d4bede9ac365b257"),
    "author": "ahn",
    "score": 60,
    "views": 1000
}

// 4
{
    "_id": ObjectId("55f5a192d4bede9ac365b258"),
    "author": "li",
    "score": 55,
    "views": 5000
}

// 5
{
    "_id": ObjectId("55f5a1d3d4bede9ac365b259"),
    "author": "annT",
    "score": 55,
    "views": 50
}

SpringBoot 整合 MongoDB:

java 复制代码
@Data
@Document(collection = "articles")
public class Article {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}

@Data
public class AggregationResult {
    @Id
    private String id;
    private String author;
    private int score;
    private int views;
}
java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanLoadServiceTest {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void aggregateTest() {
        // $sort阶段
        LimitOperation limitOperation = Aggregation.limit(5);

        Aggregation aggregation = Aggregation.newAggregation(limitOperation);

        // 执行聚合查询
        AggregationResults<AggregationResult> results
                = mongoTemplate.aggregate(aggregation, Article.class, AggregationResult.class);
        List<AggregationResult> mappedResults = results.getMappedResults();

        // 打印结果
        mappedResults.forEach(System.out::println);
        //AggregationResult(id=512bc95fe835e68f199c8686, author=dave, score=80, views=100)
        //AggregationResult(id=512bc962e835e68f199c8687, author=dave, score=85, views=521)
        //AggregationResult(id=55f5a192d4bede9ac365b257, author=ahn, score=60, views=1000)
        //AggregationResult(id=55f5a192d4bede9ac365b258, author=li, score=55, views=5000)
        //AggregationResult(id=55f5a1d3d4bede9ac365b259, author=annT, score=55, views=50)
    }
}
相关推荐
努力算法的小明4 分钟前
SQL 复杂查询
数据库·sql
斗-匕7 分钟前
MySQL 三大日志详解
数据库·mysql·oracle
代码中の快捷键13 分钟前
MySQL数据库存储引擎
数据库·mysql
只因在人海中多看了你一眼13 分钟前
数据库体系
数据库
尘浮生36 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
六月闻君1 小时前
MySQL 报错:1137 - Can‘t reopen table
数据库·mysql
SelectDB技术团队1 小时前
兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
大数据·数据库·数据仓库·数据分析·doris
inventecsh1 小时前
mongodb基础操作
数据库·mongodb
白云如幻1 小时前
SQL99版链接查询语法
数据库·sql·mysql
爱吃烤鸡翅的酸菜鱼2 小时前
MySQL初学之旅(4)表的设计
数据库·sql·mysql·database