mangoDB面试题及详细答案 117道(096-117)

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

96. 如何查询两个字段值相等的文档?

使用$expr结合$eq操作符,比较同一文档中的两个字段。

示例:查询products集合中price等于originalPrice的文档

db.products.find({
  $expr: { $eq: ["$price", "$originalPrice"] }
})

查询price大于discountPrice的文档:

db.products.find({
  $expr: { $gt: ["$price", "$discountPrice"] }
})

原理$expr允许在查询中使用聚合表达式,$eq等操作符可比较同一文档的不同字段,实现字段间的条件判断。

97. 如何在MongoDB中实现数据的批量导入(从JSON数组)?

使用mongoimport工具导入JSON数组文件,或在shell中使用insertMany()

示例1:mongoimport导入

mongoimport --uri "mongodb://localhost:27017/mydb" --collection users --file users.json

示例2:shell中批量插入

const users = [
  { name: "A", age: 20 },
  { name: "B", age: 25 }
];
db.users.insertMany(users);

原理

98. 如何在MongoDB中实现基于时间的分片策略?

基于时间字段(如createTime)进行分片,可将不同时间段的数据分布到不同分片,便于历史数据归档和查询优化。

示例:对logs集合按createTime字段进行范围分片

  1. 启用分片(假设已配置分片集群)
use admin
db.runCommand({ enableSharding: "logdb" }) // 启用数据库分片
  1. 创建分片键索引
use logdb
db.logs.createIndex({ createTime: 1 })
  1. 配置分片策略
db.runCommand({
  shardCollection: "logdb.logs",
  key: { createTime: 1 } // 按createTime范围分片
})

原理:范围分片根据分片键的值将数据划分为连续的块(chunk),不同范围的块分布到不同分片。基于时间的分片让新数据写入最新分片,历史数据存储在旧分片,方便对历史数据进行单独管理(如迁移到低成本存储)。

99. 如何查询数组中所有元素都满足条件的文档?

使用$all操作符查询数组包含所有指定元素的文档,或$elemMatch配合$not查询数组元素均满足条件的文档。

示例1:查询tags数组同时包含"mongodb"和"database"的文档

db.articles.find({ tags: { $all: ["mongodb", "database"] } })

示例2:查询scores数组中所有成绩都大于60的文档(scores为数字数组)

db.students.find({
  scores: { $not: { $elemMatch: { $lte: 60 } } }
})

原理$all用于匹配包含所有指定元素的数组;$not结合$elemMatch表示不存在满足相反条件的元素,即所有元素都满足目标条件,实现对数组元素的全量校验。

100. 如何在MongoDB中实现数据的版本回溯?

通过创建历史集合记录文档的每次变更,实现数据版本回溯功能。

示例:为products集合的文档变更记录历史版本

  1. 创建products_history集合存储历史版本
db.createCollection("products_history")
  1. 更新文档时记录历史版本
const updateProduct = function(id, newData) {
  // 查询当前文档
  const current = db.products.findOne({ _id: id });
  if (!current) return;
  
  // 记录历史版本(添加版本号和时间戳)
  db.products_history.insertOne({
    originalId: id,
    version: current.version || 1,
    data: current,
    updatedAt: new Date()
  });
  
  // 更新原文档(递增版本号)
  db.products.updateOne(
    { _id: id },
    { $set: { ...newData, version: (current.version || 1) + 1 } }
  );
};

// 使用函数更新文档
updateProduct(ObjectId("60d21b4667d0d8992e610c88"), { price: 199.99 });

原理:每次更新文档前,将当前版本的数据复制到历史集合并标记版本号和时间,原文档更新后版本号递增。需要回溯时,从历史集合查询指定版本的数据即可恢复,适用于需要审计或回滚操作的场景。

101. 如何在MongoDB中实现按字段值对文档进行分组统计并过滤分组结果?

使用$group进行分组后,通过$match过滤满足条件的分组结果。

示例:统计orders集合中每个用户的订单总金额,并筛选出总金额大于1000的用户

db.orders.aggregate([
  { $group: { _id: "$userId", totalAmount: { $sum: "$amount" } } }, // 按userId分组并求和
  { $match: { totalAmount: { $gt: 1000 } } } // 过滤总金额>1000的分组
])

原理$group先完成分组统计,$match在分组结果上进行筛选,只保留符合条件的分组,避免对不需要的分组进行后续处理,提高效率。

102. 如何查询MongoDB中字段值为null的文档?

使用$type: 10$eq: null查询字段值为null的文档(注意区分字段不存在和值为null)。

示例1:查询email字段值为null的文档(字段存在且值为null)

db.users.find({ email: null })

示例2:严格查询字段存在且值为null的文档(排除字段不存在的情况)

db.users.find({ 
  email: { $type: 10 }, // 10对应BSON中的null类型
  $exists: true 
})

原理null在MongoDB中是特殊值,{ email: null }会匹配emailnullemail字段不存在的文档;$type: 10仅匹配值为null的文档,结合$exists: true可精确筛选字段存在且值为null的文档。

103. 如何在MongoDB中实现数组的交集查询?

使用$in$size组合,或$setIntersection聚合操作符查询数组与目标数组的交集。

示例:查询tags数组与["tech", "news"]有交集的文档

db.articles.find({ tags: { $in: ["tech", "news"] } })

示例:计算tags数组与目标数组的交集并返回

db.articles.aggregate([
  {
    $addFields: {
      commonTags: { $setIntersection: ["$tags", ["tech", "news"]] }
    }
  },
  { $match: { $expr: { $gt: [{ $size: "$commonTags" }, 0] } } }
])

原理$in匹配数组中存在任一目标元素的文档;$setIntersection计算两个数组的交集,结合$size可筛选出交集非空的文档,还能获取具体的交集元素。

104. 如何限制MongoDB的单个文档大小?

MongoDB默认单个文档大小上限为16MB,可通过应用层逻辑拆分大文档,避免超出限制。

示例:拆分超过10MB的文档(伪代码逻辑)

function insertLargeData(data) {
  const maxSize = 10 * 1024 * 1024; // 10MB
  const chunks = splitDataIntoChunks(data, maxSize); // 自定义拆分函数
  
  // 插入主文档记录分片信息
  const mainDocId = new ObjectId();
  db.largeData.insertOne({
    _id: mainDocId,
    chunkCount: chunks.length,
    createdAt: new Date()
  });
  
  // 插入分块文档
  chunks.forEach((chunk, index) => {
    db.largeDataChunks.insertOne({
      mainId: mainDocId,
      chunkIndex: index,
      data: chunk
    });
  });
}

原理:16MB限制是为了保证性能和网络传输效率,超过该大小的文档会导致查询和更新性能下降。应用层通过拆分大文档为多个小文档,用主文档关联分块文档,可规避单文档大小限制。

105. 如何在MongoDB中实现基于字段的条件更新?

使用$cond聚合操作符或更新条件,根据字段值动态更新文档。

示例:根据score字段值更新grade字段(90分以上为"A",否则为"B")

db.students.updateMany(
  {},
  [
    {
      $set: {
        grade: {
          $cond: {
            if: { $gte: ["$score", 90] },
            then: "A",
            else: "B"
          }
        }
      }
    }
  ]
)

原理$cond是条件表达式,根据if中的条件返回thenelse的值,在更新操作中使用可实现基于字段值的动态更新,无需多次查询判断。

106. 如何查询MongoDB中嵌套数组满足条件的文档?

使用$elemMatch查询嵌套数组中满足条件的元素,或$filter聚合操作符筛选嵌套数组的元素。

示例:查询comments数组中存在author为"John"且rating大于4的文档(comments为嵌套对象数组)

db.posts.find({
  comments: {
    $elemMatch: {
      author: "John",
      rating: { $gt: 4 }
    }
  }
})

示例:筛选出comments数组中满足条件的元素并返回

db.posts.aggregate([
  {
    $addFields: {
      filteredComments: {
        $filter: {
          input: "$comments",
          as: "c",
          cond: { $and: [
            { $eq: ["$$c.author", "John"] },
            { $gt: ["$$c.rating", 4] }
          ]}
        }
      }
    }
  },
  { $match: { $expr: { $gt: [{ $size: "$filteredComments" }, 0] } } }
])

原理$elemMatch用于查询包含满足条件的嵌套数组元素的文档;$filter在聚合中筛选嵌套数组的元素,仅保留符合条件的部分,便于后续处理。

107. 如何在MongoDB中实现字段的条件累加?

使用$cond结合$sum聚合操作符,根据条件对字段值进行累加。

示例:统计orders集合中,status为"completed"的订单总金额

db.orders.aggregate([
  {
    $group: {
      _id: null,
      totalCompletedAmount: {
        $sum: {
          $cond: {
            if: { $eq: ["$status", "completed"] },
            then: "$amount",
            else: 0
          }
        }
      }
    }
  }
])

原理$cond根据条件返回字段值或0,$sum对结果累加,实现仅对满足条件的记录进行求和,避免先过滤再求和导致的额外步骤。

108. 如何在MongoDB中实现文档的批量移动?

使用aggregate$out操作符将文档从一个集合移动到另一个集合,再删除原集合中的文档。

示例:将temp_data集合中valid: true的文档移动到正式_data集合

// 步骤1:复制符合条件的文档到目标集合
db.temp_data.aggregate([
  { $match: { valid: true } },
  { $out: "正式_data" }
])

// 步骤2:删除原集合中已移动的文档
db.temp_data.deleteMany({ valid: true })

原理$out会将聚合结果写入目标集合(若集合存在则覆盖),结合删除操作实现批量移动,适用于数据清洗后迁移数据的场景。

109. 如何查询MongoDB中字段值在指定范围内的文档?

使用$gte(大于等于)、$lte(小于等于)、$gt(大于)、$lt(小于)操作符组合查询范围。

示例:查询age在20到30之间(含20和30)的用户

db.users.find({
  age: { $gte: 20, $lte: 30 }
})

示例:查询createTime在2023年1月1日之后的文档

db.orders.find({
  createTime: { $gt: new Date("2023-01-01") }
})

原理:范围操作符可用于数字、日期等可比较类型的字段,通过组合实现闭区间、开区间等多种范围查询,广泛应用于筛选年龄、时间、价格等场景。

110. 如何在MongoDB中实现数组的差集操作?

使用$setDifference聚合操作符计算两个数组的差集(存在于第一个数组但不存在于第二个数组的元素)。

示例:计算tags数组与excludedTags数组的差集

db.articles.aggregate([
  {
    $addFields: {
      remainingTags: {
        $setDifference: ["$tags", ["old", "obsolete"]]
      }
    }
  }
])

原理$setDifference接收两个数组参数,返回第一个数组中不存在于第二个数组的元素,实现数组差集计算,常用于过滤数组中的无效元素。

111. 如何查询MongoDB中字段值为数组且不为空的文档?

使用$expr结合$gt$size查询数组字段长度大于0的文档。

示例:查询hobbies数组不为空的用户

db.users.find({
  $expr: { $gt: [{ $size: "$hobbies" }, 0] }
})

原理$size返回数组长度,$gt: 0表示数组长度大于0(即不为空),$expr允许在查询中使用聚合表达式,实现对数组非空性的判断。

112. 如何在MongoDB中实现基于字段的排序后取前N条数据?

使用sort()limit()组合,先按指定字段排序,再取前N条数据。

示例:查询sales集合中按amount降序排列的前10条销售记录

db.sales.find().sort({ amount: -1 }).limit(10)

示例:查询users集合中按registerTime升序排列的前5条最早注册的用户

db.users.find().sort({ registerTime: 1 }).limit(5)

原理sort()指定排序字段和方向(1升序,-1降序),limit(N)限制返回结果为前N条,数据库会优化该操作,避免全表排序后再取前N条,而是直接获取排序后的前N条,性能较高。

113. 如何在MongoDB中实现字段的类型转换?

使用$convert$toInt$toDate等类型转换操作符,在查询或聚合中转换字段类型。

示例:将ageStr字段(字符串类型)转换为整数类型并查询

db.users.aggregate([
  {
    $addFields: {
      age: { $toInt: "$ageStr" } // 将字符串转换为整数
    }
  },
  { $match: { age: { $gte: 18 } } }
])

示例:处理转换失败的情况(转换失败返回null

db.data.aggregate([
  {
    $addFields: {
      num: {
        $convert: {
          input: "$strNum",
          to: "int",
          onError: null, // 转换失败返回null
          onNull: null
        }
      }
    }
  }
])

原理:类型转换操作符可将字段从一种类型转换为另一种(如字符串转数字、日期字符串转日期类型),onErroronNull可处理转换失败或输入为null的情况,确保数据处理的健壮性。

114. 如何查询MongoDB中多个字段同时满足条件的文档?

使用$and操作符或直接在查询条件中包含多个字段,实现多字段同时满足条件的查询。

示例1:查询age大于25且city为"Shanghai"的用户

db.users.find({
  $and: [
    { age: { $gt: 25 } },
    { city: "Shanghai" }
  ]
})

示例2:简化写法(多个字段条件默认是and关系)

db.users.find({
  age: { $gt: 25 },
  city: "Shanghai"
})

原理:MongoDB查询条件中多个字段默认是and关系,即所有条件必须同时满足;$and用于显式指定多个条件的and关系,适用于同一字段需要多个条件的场景(如{ $and: [{ age: { $gt: 25 } }, { age: { $lt: 40 } }] })。

115. 如何在MongoDB中实现数组的追加与替换?

使用$push追加元素到数组,$set直接替换整个数组。

示例1:向hobbies数组追加一个元素

db.users.updateOne(
  { name: "Alice" },
  { $push: { hobbies: "painting" } }
)

示例2:替换hobbies数组为新数组

db.users.updateOne(
  { name: "Bob" },
  { $set: { hobbies: ["reading", "traveling"] } }
)

示例3:向数组追加多个元素($each

db.users.updateOne(
  { name: "Charlie" },
  { $push: { hobbies: { $each: ["gaming", "hiking"] } } }
)

原理$push用于在数组末尾添加元素,$each可一次添加多个元素;$set直接覆盖原数组,适用于需要完全更新数组内容的场景。

116. 如何查询MongoDB中字段值为指定类型的文档?

使用$type操作符查询字段值为指定BSON类型的文档。

示例1:查询age字段为整数类型(BSON类型16)的文档

db.users.find({ age: { $type: 16 } })

示例2:查询createdAt字段为日期类型(BSON类型9)的文档

db.orders.find({ createdAt: { $type: "date" } })

原理$type可接收BSON类型编号或类型名称(如"string""int""date"),用于筛选字段值为特定类型的文档,常用于数据清洗(如找出类型错误的字段)。

117. 如何在MongoDB中实现基于数组索引的查询?

通过数组索引位置(从0开始)查询或更新数组中指定位置的元素。

示例:查询scores数组中第2个元素(索引1)大于90的文档

db.students.find({ "scores.1": { $gt: 90 } })

示例:更新hobbies数组中第1个元素(索引0)为"coding"

db.users.updateOne(
  { name: "John" },
  { $set: { "hobbies.0": "coding" } }
)

原理:数组索引通过点符号(数组字段.索引)访问,可直接定位数组中特定位置的元素,实现精确的查询和更新,适用于数组结构固定的场景。

内容概要:文章基于4A架构(业务架构、应用架构、数据架构、技术架构),对SAP的成本中心和利润中心进行了详细对比分析。业务架构上,成本中心是成本控制的责任单元,负责成本归集与控制,而利润中心是利润创造的独立实体,负责收入、成本和利润的核算。应用架构方面,两者都依托于SAP的CO模块,但功能有所区分,如成本中心侧重于成本要素归集和预算管理,利润中心则关注内部交易核算和获利能力分析。数据架构中,成本中心与利润中心存在多对一的关系,交易数据通过成本归集、分摊和利润计算流程联动。技术架构依赖SAP S/4HANA的内存计算和ABAP技术,支持实时核算与跨系统集成。总结来看,成本中心和利润中心在4A架构下相互关联,共同为企业提供精细化管理和决策支持。 适合人群:从事企业财务管理、成本控制或利润核算的专业人员,以及对SAP系统有一定了解的企业信息化管理人员。 使用场景及目标:①帮助企业理解成本中心和利润中心在4A架构下的运作机制;②指导企业在实施SAP系统时合理配置成本中心和利润中心,优化业务流程;③提升企业对成本和利润的精细化管理水平,支持业务决策。 其他说明:文章不仅阐述了理论概念,还提供了具体的应用场景和技术实现方式,有助于读者全面理解并应用于实际工作中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是大剑师兰特

打赏一杯可口可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值