MongoDB学习专题(二)核心操作

发布于:2025-08-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

1、复合主键

2、逻辑操作符匹配

1、$not

2、$and

2、$or

3、字段匹配

4、排序&分页查询&正则表达式查询

1、指定排序

2、分页查询

1、处理分页问题 – 巧分页

2、处理分页问题 – 避免使用 count

3、正则表达式匹配查询

6、更新文档

1、更新文档的操作符

2、更新单个文档

3、增减数值

4、更新多个文档

5、upsert命令

6、无操作符 update的 replace 语义

7、findAndModify命令

7、删除文档

1、使用 remove 删除文档

2、使用 delete 删除文档(推荐)

3、返回被删除文档

4、删除集合


1、复合主键

可以使用文档作为复合主键

db.demeDoc.insert(
{
    _id: { 
        product_name: 1,  // 主键字段1
        product_type: 2   // 主键字段2
   },
    supplierId: "001",  // 普通字段
    create_Time: new Date() // 自动生成时间戳
})

注意复合主键,字段顺序换了,会当做不同的对象被创建,即使内容完全一致

2、逻辑操作符匹配

$not(非(¬)) : 匹配筛选条件不成立的文档

$and(与(∧)): 匹配多个筛选条件同时满足的文档

$or (或(∨)​): 匹配至少一个筛选条件成立的文档

$nor(或非(¬∨)) : 匹配多个筛选条件全部不满足的文档

首先构造一组数据:

db.members.insertMany([
{
  nickName:"曹操",
  points:1000
},
{
  nickName:"刘备",
  points:500
}
]);

1、$not

用法:

{ field: { $not : { operator-expression} }}

积分不小于100 的

db.members.find({points: { $not: { $lt: 100}}} );

$not 也会筛选出并不包含查询字段的文档,相当于只会在自己领域用排除法

2、$and

用法

{ $and : [ condition expression1 , condition expression2 ..... ]}

1、昵称等于曹操, 积分大于 1000 的文档

db.members.find({$and : [ {nickName:{ $eq : "曹操"}}, {points:{ $gt:1000}}]});

2、当作用在不同的字段上时 可以省略 $and(nickName和points就不同)

db.members.find({nickName:{ $eq : "曹操"}, points:{ $gt:1000}});

3、当作用在同一个字段上面时可以简化为

db.members.find({points:{ $gte:1000, $lte:2000}});

2、$or

用法

{ $or :{ condition1, condition2, condition3,... }}
db.members.find(
{$or : [ 
      {nickName:{ $eq : "刘备"}}, 
      {points:{ $gt:1000}}]}
);

如果都是等值查询的话, $or 和 $in 结果是一样的

3、字段匹配

$exists:匹配包含查询字段的文档

{ field : {$exists:  <boolean>} }

比如上方的: 

db.members.find({points:{$exists:true}});

返回结果:
[
  {
    _id: ObjectId('6891a8c446238faadaeec4ae'),
    nickName: '曹操',
    points: 1000
  },
  {
    _id: ObjectId('6891a8c446238faadaeec4af'),
    nickName: '刘备',
    points: 500
  }
]

4、排序&分页查询&正则表达式查询

1、指定排序

在 MongoDB 中使用 sort() 方法对数据进行排序

指定按收藏数(favCount)降序返回(1 为升序排列,而 -1 是用于降序排列)

db.books.find({type:"travel"}).sort({favCount:-1})

当同时应用 sort, skip, limit 时 ,应用的顺序为 sort, skip, limit 

2、分页查询

  • skip 用于指定跳过记录数
  • limit 用于限定返回结果数量

可以在执行find命令的同时指定skip、limit参数,以此实现分页的功能。

比如,假定每页大小为8条,查询第3页的book文档:

db.books.find().skip(8).limit(4)

1、处理分页问题 – 巧分页

数据量大的时候,应该避免使用skip/limit形式的分页。

替代方案:基于 ​​游标分页使用查询条件+唯一排序条件

例如:

第一页:
db.posts.find({}).sort({_id: 1}).limit(20);

第二页:
db.posts.find({_id: {$gt: }}).sort({_id: 1}).limit(20);

第三页:
db.posts.find({_id: {$gt: }}).sort({_id: 1}).limit(20);

2、处理分页问题 – 避免使用 count

尽可能不要计算总页数,特别是数据量大和查询条件不能完整命中索引时。

考虑以下场景:假设集合总共有 1000w 条数据,在没有索引的情况下考虑以下查询:

4.0+ 弃用方案:

(警告被丢弃:DeprecationWarning: Collection.count() is deprecated. Use countDocuments or estimatedDocumentCount)

//查询集合 coll中字段x等于100的文档,并限制返回结果最多 ​​50 条​​
db.coll.find({x: 100}).limit(50); 

//统计集合 coll中字段x等于100的文档总数
db.coll.count({x: 100}); 
  • 前者只需要遍历前 n 条,直到找到 50 条 x=100 的文档即可结束;
  • 后者需要遍历完 1000w 条找到所有符合要求的文档才能得到结果。 为了计算总页数而进行的 count() 往往是拖慢页面整体加载速度的原因

 方案1:使用 countDocuments()(精确计数)​

// 计算所有文档数量(等效于旧版 count())
db.members.countDocuments({});

// 计算满足条件的文档数量(如 status: "A")
db.members.countDocuments({ status: "A" });

方案2:使用聚合管道 $count

// 统计跳过 1 条后限制 1 条的文档数(实际应用场景较少)
db.members.aggregate([
  { $skip: 1 },
  { $limit: 1 },
  { $count: "total" }
]);

方案3:使用 estimatedDocumentCount()(快速估算)​

快速估算集合文档总数(忽略 skip/limit)

db.members.estimatedDocumentCount();

3、正则表达式匹配查询

MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。

//使用正则表达式查找type包含 so 字符串的book
db.books.find({type:{$regex:"so"}})

//或者
db.books.find({type:/so/})

6、更新文档

update命令对指定的数据进行更新,命令的格式如下:

db.collection.update(query,update,options)
  • query:更新的查询条件;
  • update:更新的动作及新的内容;
  • options:更新的选项

可选: 

  • upsert:如果不存在update的记录,是否插入新的记录。默认false,不插入
  • multi: 是否按条件查询出的多条记录全部更新。 默认false,只更新找到的第一条记录
  • writeConcern :决定一个写操作落到多少个节点上才算成功。

1、更新文档的操作符

操作符

格式

描述

$set

{$set:{field:value}}

指定一个键并更新值,若键不存在则创建

$unset

{$unset : {field : 1 }}

删除一个键

$inc

{$inc : {field : value } }

对数值类型进行增减

$rename

{$rename : {old_field_name : new_field_name } }

修改字段名称

$push

{ $push : {field : value } }

将数值追加到数组中,若数组不存在则会进行初始化

$pushAll

{$pushAll : {field : value_array }}

追加多个值到一个数组字段内

$pull

{$pull : {field : _value } }

从数组中删除指定的元素

$addToSet

{$addToSet : {field : value } }

添加元素到数组中,具有排重功能

$pop

{$pop : {field : 1 }}

删除数组的第一个或最后一个元素

2、更新单个文档

// 将 _id=1 的文档的 price 改为 699
db.products.update(
  { _id: 1 },
  { $set: { price: 699 } }
)

3、增减数值

// 将 _id=2 的 stock 减少 2
db.products.update(
  { _id: 2 },
  { $inc: { stock: -2 } }
)

4、更新多个文档

使用multi选项

multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新

// 将所有 tags 包含 "electronics" 的文档 price 增加 50
db.products.update(
  { tags: "electronics" },
  { $inc: { price: 50 } },
  { multi: true }
)

update命令的选项配置较多,为了简化使用还可以使用一些快捷命令:

  • updateOne:更新单个文档。
    // 将第一个 name 为 "Phone" 的文档的 price 改为 699
    db.products.updateOne(
      { name: "Phone" },
      { $set: { price: 699 } }
    );
  • updateMany:更新多个文档。
    // 将所有 price < 800 的文档的 stock 增加 5
    db.products.updateMany(
      { price: { $lt: 800 } },
      { $inc: { stock: 5 } }
    );
  • replaceOne:替换单个文档。
    // 将 _id: 1 的文档完全替换为新内容
    db.products.replaceOne(
      { _id: 1 },
      { name: "Smartphone", price: 799, color: "black" }
    );

5、upsert命令

upsert是一种特殊的更新,其表现为如果目标文档不存在,则执行插入命令

旧指令:

// 如果 _id=3 不存在,则插入新文档
db.products.update(
  { _id: 3 },
  { $set: { name: "Tablet", price: 299 } },
  { upsert: true }
)

现代指令:

// 使用 updateOne 替代
db.books.updateOne(
  { title: "my book" },
  { $set: { 
      tags: ["nosql", "mongodb"],
      type: "none",
      author: "fox" 
  }},
  { upsert: true }
);

结果:

{
  acknowledged: true,                                // 操作已确认
  insertedId: ObjectId('6891c426ad6f8a20a28faffc'),  // 新插入文档的 _id
  matchedCount: 0,                                   // 匹配的文档数(0 表示未找到)
  modifiedCount: 0,                                  // 修改的文档数
  upsertedCount: 1                                   // 插入的文档数
}

6、无操作符 update的 replace 语义

update命令中的更新描述(update)通常由操作符描述,如果更新描述中不包含任何操作符,那么MongoDB会实现文档的replace语义

db.books.update(
    {title:"my book"},
    {justTitle:"my first book"}
)    
  • 若找到匹配文档,会用 { justTitle: "my first book" }​完全替换​​原文档(仅保留 _id)。

  • ​原文档的其他字段(如 tagsauthor)将被删除​​!

7、findAndModify命令

findAndModify兼容了查询和修改指定文档的功能,findAndModify只能更新单个文档

//将某个book文档的收藏数(favCount)加1
db.books.findAndModify({
    query:{_id:ObjectId("61caa09ee0782536660494dd")},
    update:{$inc:{favCount:1}}
})

该操作会返回符合查询条件的文档数据,并完成对文档的修改。

默认情况下,findAndModify会返回修改前的“旧”数据。如果希望返回修改后的数据,则可以指定new选项

db.books.findAndModify({
    query:{_id:ObjectId("61caa09ee0782536660494dd")},
    update:{$inc:{favCount:1}},
    new: true
})

与findAndModify语义相近的命令如下:

  • findOneAndUpdate:更新单个文档并返回更新前(或更新后)的文档。
  • findOneAndReplace:替换单个文档并返回替换前(或替换后)的文档。

7、删除文档

1、使用 remove 删除文档

  • remove 命令需要配合查询条件使用;
  • 匹配查询条件的文档会被删除;
  • 指定一个空文档条件会删除所有文档;

示例:

db.user.remove({age:28})        // 删除age 等于28的记录 
db.user.remove({age:{$lt:25}})  // 删除age 小于25的记录 
db.user.remove( { } )           // 删除所有记录 db.user.remove() //报错

remove命令会删除匹配条件的全部文档,如果希望明确限定只删除一个文档,则需要指定justOne参数,命令格式如下:

db.collection.remove(query,justOne)

例如:删除满足type:novel条件的首条记录

db.books.remove({type:"novel"},true)
// 只删除第一条 name 以 "B" 开头的文档
db.users.remove(
  { name: /^B/ }, 
  { justOne: true }
);

2、使用 delete 删除文档(推荐)

官方推荐使用 deleteOne() deleteMany() 方法删除文档,语法格式如下:

db.books.deleteMany ({})                //删除集合下全部文档
db.books.deleteMany ({ type:"novel" })  //删除 type等于 novel 的全部文档
db.books.deleteOne ({ type:"novel" })   //删除 type等于novel 的一个文档

注意: remove、deleteMany等命令需要对查询范围内的文档逐个删除,如果希望删除整个集合,则使用drop命令会更加高效

3、返回被删除文档

remove、deleteOne等命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档的完整信息,则可以使用findOneAndDelete命令

books 集合里,找到第一个类型是“novel”的书,然后把它删掉。在删除之后,它会返回这个被删除的文档

//在 books 集合里,找到第一个类型是“novel”的书,然后把它删掉。在删除之后,它会返回这个被删除的文档
db.books.findOneAndDelete({type:"novel"})

除了在结果中返回删除文档,findOneAndDelete命令还允许定义“删除的顺序”,即按照指定顺序删除找到的第一个文档

//根据 favCount 字段的值从小到大进行排序,删除排在最前面的那一个文档
db.books.findOneAndDelete({type:"novel"},{sort:{favCount:1}})

remove、deleteOne等命令只能按默认顺序删除,利用这个特性,findOneAndDelete可以实现队列的先进先出

4、删除集合

db.stock_queue.drop()


网站公告

今日签到

点亮在社区的每一天
去签到