《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 96. 如何查询两个字段值相等的文档?
- 97. 如何在MongoDB中实现数据的批量导入(从JSON数组)?
- 98. 如何在MongoDB中实现基于时间的分片策略?
- 99. 如何查询数组中所有元素都满足条件的文档?
- 100. 如何在MongoDB中实现数据的版本回溯?
- 101. 如何在MongoDB中实现按字段值对文档进行分组统计并过滤分组结果?
- 102. 如何查询MongoDB中字段值为null的文档?
- 103. 如何在MongoDB中实现数组的交集查询?
- 104. 如何限制MongoDB的单个文档大小?
- 105. 如何在MongoDB中实现基于字段的条件更新?
- 106. 如何查询MongoDB中嵌套数组满足条件的文档?
- 107. 如何在MongoDB中实现字段的条件累加?
- 108. 如何在MongoDB中实现文档的批量移动?
- 109. 如何查询MongoDB中字段值在指定范围内的文档?
- 110. 如何在MongoDB中实现数组的差集操作?
- 111. 如何查询MongoDB中字段值为数组且不为空的文档?
- 112. 如何在MongoDB中实现基于字段的排序后取前N条数据?
- 113. 如何在MongoDB中实现字段的类型转换?
- 114. 如何查询MongoDB中多个字段同时满足条件的文档?
- 115. 如何在MongoDB中实现数组的追加与替换?
- 116. 如何查询MongoDB中字段值为指定类型的文档?
- 117. 如何在MongoDB中实现基于数组索引的查询?
一、本文面试题目录
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
字段进行范围分片
- 启用分片(假设已配置分片集群)
use admin
db.runCommand({ enableSharding: "logdb" }) // 启用数据库分片
- 创建分片键索引
use logdb
db.logs.createIndex({ createTime: 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
集合的文档变更记录历史版本
- 创建
products_history
集合存储历史版本
db.createCollection("products_history")
- 更新文档时记录历史版本
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 }
会匹配email
为null
或email
字段不存在的文档;$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
中的条件返回then
或else
的值,在更新操作中使用可实现基于字段值的动态更新,无需多次查询判断。
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
}
}
}
}
])
原理:类型转换操作符可将字段从一种类型转换为另一种(如字符串转数字、日期字符串转日期类型),onError
和onNull
可处理转换失败或输入为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" } }
)
原理:数组索引通过点符号(数组字段.索引
)访问,可直接定位数组中特定位置的元素,实现精确的查询和更新,适用于数组结构固定的场景。