MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MongoDB Shell中的数据类型与操作技巧

2022-08-287.2k 阅读

MongoDB Shell 中的数据类型

基本数据类型

  1. 字符串(String)
    • 在 MongoDB 中,字符串是 UTF - 8 编码的字符序列。字符串必须用双引号(")或单引号(')括起来。例如:
    db.users.insertOne({name: "John Doe"});
    
    • 字符串类型常用于存储文本信息,如用户名、描述等。在查询时,可以使用各种字符串匹配操作符。例如,要查找名字以“J”开头的用户:
    db.users.find({name: /^J/});
    
  2. 数值(Number)
    • MongoDB 支持多种数值类型。在 32 位系统上,默认的数值类型是 NumberInt,它是 32 位有符号整数。例如:
    db.products.insertOne({price: NumberInt(10)});
    
    • 在 64 位系统上,默认的数值类型是 NumberLong,用于表示 64 位有符号整数。对于浮点数,可以直接使用 JavaScript 的原生 Number 类型,MongoDB 会将其存储为 Double 类型。例如:
    db.products.insertOne({weight: 1.5});
    
    • 数值类型在各种计算和比较操作中广泛使用。例如,要查找价格大于 50 的产品:
    db.products.find({price: {$gt: 50}});
    
  3. 布尔值(Boolean)
    • 布尔类型只有两个值:truefalse。常用于表示逻辑状态,如用户是否激活、产品是否有库存等。例如:
    db.users.insertOne({isActive: true});
    
    • 在查询时,可以根据布尔值进行筛选。例如,查找所有激活的用户:
    db.users.find({isActive: true});
    
  4. 日期(Date)
    • 在 MongoDB Shell 中,日期类型通过 new Date() 创建。日期存储为自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)以来的毫秒数。例如:
    var today = new Date();
    db.orders.insertOne({orderDate: today});
    
    • 可以对日期进行各种操作,如查找特定日期范围内的订单。例如,查找最近一周内的订单:
    var oneWeekAgo = new Date();
    oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
    db.orders.find({orderDate: {$gte: oneWeekAgo}});
    
  5. 空值(Null)
    • null 类型表示一个空值或不存在的值。在 MongoDB 中,它可用于表示文档中某个字段缺失或明确设置为空值。例如:
    db.users.insertOne({bio: null});
    
    • 在查询时,可以查找某个字段为 null 的文档。例如:
    db.users.find({bio: null});
    

复合数据类型

  1. 数组(Array)
    • 数组是值的有序集合。在 MongoDB 中,数组可以包含不同数据类型的元素。例如,可以创建一个包含字符串和数值的数组:
    db.books.insertOne({
        title: "Programming Languages",
        authors: ["John Smith", "Jane Doe"],
        ratings: [4, 5]
    });
    
    • 数组在 MongoDB 中有强大的查询和更新操作。例如,要查找有“John Smith”作为作者的书籍:
    db.books.find({authors: "John Smith"});
    
    • 还可以使用数组操作符对数组进行更复杂的操作。例如,$push 操作符用于向数组中添加元素:
    db.books.updateOne(
        {title: "Programming Languages"},
        {$push: {ratings: 3}}
    );
    
  2. 文档(Document)
    • 文档是 MongoDB 中数据的基本组织单位,它是一个键值对的无序集合。文档可以嵌套,即一个文档的字段值可以是另一个文档。例如:
    db.addresses.insertOne({
        street: "123 Main St",
        city: "Anytown",
        state: "CA",
        zip: "12345",
        location: {
            lat: 37.7749,
            lon: -122.4194
        }
    });
    
    • 在查询嵌套文档时,需要使用点表示法。例如,要查找纬度大于 37 的地址:
    db.addresses.find({"location.lat": {$gt: 37}});
    
  3. 对象 ID(ObjectId)
    • ObjectId 是 MongoDB 为每个文档自动生成的唯一标识符。它是一个 12 字节的 BSON 类型。ObjectId 的前 4 个字节表示文档创建的时间戳。例如,在插入文档时,MongoDB 会自动为其生成 ObjectId
    var result = db.users.insertOne({name: "Alice"});
    var objectId = result.insertedId;
    
    • 可以使用 ObjectId 进行文档的精确查找。例如:
    db.users.find({_id: objectId});
    
    • 在 JavaScript 中,可以通过 ObjectId 构造函数将字符串形式的 ObjectId 转换为 ObjectId 对象,以便进行查询。例如:
    var objectIdStr = "5f4f7a9b9c8a370c50f9e57f";
    var objectIdObj = ObjectId(objectIdStr);
    db.users.find({_id: objectIdObj});
    

MongoDB Shell 中的操作技巧

查询操作技巧

  1. 使用投影(Projection)限制返回字段
    • 在查询时,默认会返回文档的所有字段。通过投影,可以选择只返回需要的字段。例如,要查找用户的名字和电子邮件,而不返回其他字段:
    db.users.find({}, {name: 1, email: 1, _id: 0});
    
    • 在投影中,将字段值设为 1 表示包含该字段,设为 0 表示排除该字段。_id 字段比较特殊,默认是包含的,如果要排除,必须显式设置为 0。
  2. 复合查询条件(Logical Operators)
    • 可以使用逻辑操作符($and$or$not)组合多个查询条件。例如,要查找年龄大于 30 且城市为“New York”的用户:
    db.users.find({
        $and: [
            {age: {$gt: 30}},
            {city: "New York"}
        ]
    });
    
    • 使用 $or 操作符查找年龄大于 30 或者城市为“New York”的用户:
    db.users.find({
        $or: [
            {age: {$gt: 30}},
            {city: "New York"}
        ]
    });
    
    • $not 操作符用于对单个条件取反。例如,查找年龄不大于 30 的用户:
    db.users.find({age: {$not: {$gt: 30}}});
    
  3. 范围查询(Range Queries)
    • 除了 $gt(大于)、$lt(小于),还有 $gte(大于等于)、$lte(小于等于)。例如,要查找价格在 50 到 100 之间(包括 50 和 100)的产品:
    db.products.find({price: {$gte: 50, $lte: 100}});
    
  4. 数组查询技巧
    • 查询数组元素:可以直接查询数组中是否包含某个元素。例如,要查找有“JavaScript”标签的文章:
    db.articles.find({tags: "JavaScript"});
    
    • 查询数组长度:使用 $size 操作符可以查询具有特定长度的数组。例如,要查找有 3 个作者的书籍:
    db.books.find({authors: {$size: 3}});
    
    • 查询数组中的对象:如果数组元素是对象,可以使用点表示法查询对象的字段。例如,假设订单中的订单项是对象数组,要查找包含价格大于 10 的订单项的订单:
    db.orders.find({"orderItems.price": {$gt: 10}});
    

更新操作技巧

  1. 原子更新操作符
    • $set 操作符:用于设置字段的值。例如,要更新用户的电子邮件:
    db.users.updateOne(
        {name: "John Doe"},
        {$set: {email: "john@example.com"}}
    );
    
    • $inc 操作符:用于对数值字段进行递增或递减。例如,要将产品的库存数量减少 5:
    db.products.updateOne(
        {productName: "Widget"},
        {$inc: {stock: -5}}
    );
    
    • $unset 操作符:用于删除字段。例如,要删除用户的 bio 字段:
    db.users.updateOne(
        {name: "Jane Smith"},
        {$unset: {bio: ""}}
    );
    
  2. 数组更新操作符
    • $push 操作符:如前文所述,用于向数组中添加元素。可以通过 $each 修饰符一次添加多个元素。例如,要向文章的标签数组中添加多个标签:
    db.articles.updateOne(
        {title: "MongoDB Tips"},
        {$push: {tags: {$each: ["database", "nosql"]}}}
    );
    
    • $pull 操作符:用于从数组中删除符合条件的元素。例如,要从用户的爱好数组中删除“swimming”:
    db.users.updateOne(
        {name: "Bob"},
        {$pull: {hobbies: "swimming"}}
    );
    
    • $pop 操作符:用于从数组的开头或结尾删除一个元素。例如,要从订单的订单项数组中删除最后一个订单项:
    db.orders.updateOne(
        {orderNumber: "12345"},
        {$pop: {orderItems: -1}}
    );
    
    • 这里 -1 表示从数组末尾删除,1 表示从数组开头删除。

聚合操作技巧

  1. 基本聚合操作
    • $group 操作符:用于按指定的字段对文档进行分组,并对每个组应用累加器函数。例如,要按城市统计用户数量:
    db.users.aggregate([
        {$group: {_id: "$city", count: {$sum: 1}}}
    ]);
    
    • 在这个例子中,_id 字段指定了分组依据,$sum 是累加器函数,用于计算每个组中的文档数量。
    • $match 操作符:用于在聚合管道中筛选文档,类似于 find 方法中的查询条件。例如,要先筛选出年龄大于 30 的用户,再按城市统计数量:
    db.users.aggregate([
        {$match: {age: {$gt: 30}}},
        {$group: {_id: "$city", count: {$sum: 1}}}
    ]);
    
  2. 复杂聚合操作
    • $lookup 操作符:用于执行左外连接,将来自不同集合的数据组合在一起。例如,假设有一个 orders 集合和一个 customers 集合,要在订单文档中添加对应的客户信息:
    db.orders.aggregate([
        {$lookup: {
            from: "customers",
            localField: "customerId",
            foreignField: "_id",
            as: "customerInfo"
        }}
    ]);
    
    • 这里 from 指定要连接的集合,localField 是当前集合中的字段,foreignField 是连接集合中的字段,as 是结果数组的字段名。
    • $project 操作符:在聚合管道中用于投影,类似于查询中的投影。例如,在前面的聚合结果中,只保留订单号、客户姓名和订单金额:
    db.orders.aggregate([
        {$lookup: {
            from: "customers",
            localField: "customerId",
            foreignField: "_id",
            as: "customerInfo"
        }},
        {$project: {
            orderNumber: 1,
            customerName: "$customerInfo.name",
            orderAmount: 1,
            _id: 0
        }}
    ]);
    

索引操作技巧

  1. 创建索引
    • 单字段索引:可以为单个字段创建索引以提高查询性能。例如,要为 users 集合的 email 字段创建索引:
    db.users.createIndex({email: 1});
    
    • 这里 1 表示升序索引,-1 表示降序索引。
    • 复合索引:当需要根据多个字段进行查询时,可以创建复合索引。例如,要根据 cityage 字段创建复合索引:
    db.users.createIndex({city: 1, age: -1});
    
    • 复合索引的字段顺序很重要,查询条件应与索引字段顺序相匹配以获得最佳性能。
  2. 索引管理
    • 查看索引:可以使用 getIndexes 方法查看集合的所有索引。例如:
    db.users.getIndexes();
    
    • 删除索引:使用 dropIndex 方法删除不需要的索引。例如,要删除 email 字段的索引:
    db.users.dropIndex({email: 1});
    
    • 重建索引:在某些情况下,如索引损坏或需要优化索引结构时,可以重建索引。例如:
    db.users.reIndex();
    

性能优化技巧

  1. 查询优化
    • 分析查询:使用 explain 方法分析查询的执行计划,了解查询如何使用索引等。例如:
    db.users.find({age: {$gt: 30}}).explain("executionStats");
    
    • 分析结果会显示查询的执行时间、扫描的文档数、是否使用索引等信息,帮助优化查询。
    • 避免全表扫描:确保查询条件使用的字段有合适的索引,尽量避免查询条件中使用范围操作符(如 $gt$lt)在索引字段的开头,因为这可能导致索引无法完全利用,从而引发全表扫描。
  2. 服务器配置优化
    • 内存配置:MongoDB 依赖内存来缓存数据和索引,确保服务器有足够的内存分配给 MongoDB。可以通过修改配置文件(通常是 mongod.conf)来调整内存相关参数,如 wiredTiger.cache_sizeGB 用于设置 WiredTiger 存储引擎的缓存大小。
    • 存储配置:选择合适的存储设备,如 SSD 可以显著提高 I/O 性能。此外,合理规划数据目录和日志目录,避免 I/O 竞争。
  3. 副本集和分片优化
    • 副本集:合理配置副本集成员数量,确保有足够的冗余,同时避免过多成员导致性能开销。主节点和从节点的负载均衡也很重要,可以通过配置从节点的优先级和隐藏属性来优化。例如,将一些用于备份或分析的从节点设置为隐藏节点,避免它们参与读操作竞争。
    • 分片:根据数据量和查询模式合理选择分片键,确保数据均匀分布在各个分片上。定期监控分片集群的状态,使用 sh.status() 命令查看分片的负载情况,必要时进行手动均衡操作。

通过深入理解 MongoDB Shell 中的数据类型和掌握各种操作技巧,可以更高效地使用 MongoDB 进行数据管理和应用开发。在实际应用中,应根据具体的业务需求和数据特点,灵活运用这些知识,以达到最佳的性能和功能实现。同时,持续关注 MongoDB 的版本更新,了解新的数据类型和操作特性,进一步提升开发和管理效率。