前言
在mongo中資料型別有很多種,常見的包括:
資料型別 | 例子 | 描述 |
---|---|---|
String | { "x" : "foot" } |
字串。儲存資料常用的資料型別。在 MongoDB 中,UTF-8 編碼的字串才是合法的。 |
Integer | { "x" : 1 } |
整型數值。用於儲存數值。根據你所採用的伺服器,可分為 32 位或 64 位。 |
Object | { "x" : { "y" : "foot" } } |
用於內嵌文件 |
Array | { "x" : [ "a" , "b" ] } |
用於將陣列或列表或多個值儲存為一個鍵。 |
有一種很常見的查詢,就是過濾陣列中的一些資料,只返回符合要求的資料。資料如下,將下面travel中的vehicle=train的記錄保留,過濾掉其他的元素,並返回整個文件。
{
"name": "tom",
"travel": [
{
"vehicle" : "train",
"city" : "北京"
},
{
"vehicle" : "plane",
"city" : "上海"
},
{
"vehicle" : "train",
"city" : "深圳"
}
]
}
想要實現陣列的過濾有三種方法,包括:
- 聚合查詢 使用
$unwind
將travel
陣列打散,獲取結果集後用$match
篩選符合條件的資料,最後使用$group
進行聚合獲取最終結果集 - 聚合查詢 使用
$match
過濾符合條件的根文件結果集,然後使用$projec
t返回對應欄位的同時,在travel
陣列中使用$filter
進行內部過濾,返回最終結果集 - 普通查詢 先篩選記錄,然後透過投影查詢過濾陣列
下面來分析這三種方法能否實現需求。
新增資料
假設有兩條記錄,每條記錄是一個人的資訊,包括姓名、職業、旅遊過的城市。旅遊過的城市是一個陣列,包含城市的名字以及交通工具。
db.test.insertOne({
"uid" : "1000001",
"name" : "zhangsan",
"job": "coder",
"travel" : [
{
"vehicle" : "train",
"city" : "北京"
},
{
"vehicle" : "plane",
"city" : "上海"
},
{
"vehicle" : "train",
"city" : "深圳"
}
]
})
db.test.insertOne({
"uid" : "1000002",
"name" : "lisi",
"job": "coder",
"travel" : [
{
"vehicle" : "plane",
"city" : "北京"
},
{
"vehicle" : "car",
"city" : "上海"
},
{
"vehicle" : "train",
"city" : "深圳"
}
]
})
db.test.find()
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel:
[ { vehicle: 'train', city: '北京' },
{ vehicle: 'plane', city: '上海' },
{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
job: 'coder',
travel:
[ { vehicle: 'plane', city: '北京' },
{ vehicle: 'car', city: '上海' },
{ vehicle: 'train', city: '深圳' } ] }
驗證三種方法
需求說明
現在的目標是:篩選的出所有記錄中透過火車去旅遊的城市,也就是travel陣列中vehicle=train的記錄,過濾掉非目標記錄。
方法一
方法一:使用$unwind
將travel
陣列打散,獲取結果集後用match
篩選符合條件的資料,最後使用$group
進行聚合獲取最終結果集。
db.getCollection('test').aggregate(
[
{
$unwind: "$travel"
},
{
$match : {
"job":"coder",
"travel.vehicle": "train"
}
},
{
$group : {
"_id" : "$uid",
"travel": { $push: "$travel" }
}
}
]
)
結果:
{ _id: '1000002', travel: [ { vehicle: 'train', city: '深圳' } ] }
{ _id: '1000001', travel: [ { vehicle: 'train', city: '北京' }, { vehicle: 'train', city: '深圳' } ] }
分析:
unwind 可以將一個陣列拆分,例如unwind的效果如下:
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel: { vehicle: 'train', city: '北京' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel: { vehicle: 'plane', city: '上海' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel: { vehicle: 'train', city: '深圳' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
job: 'coder',
travel: { vehicle: 'plane', city: '北京' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
job: 'coder',
travel: { vehicle: 'car', city: '上海' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
job: 'coder',
travel: { vehicle: 'train', city: '深圳' } }
然後透過match篩選出符合條件的資料
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel: { vehicle: 'train', city: '北京' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
job: 'coder',
travel: { vehicle: 'train', city: '深圳' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
job: 'coder',
travel: { vehicle: 'train', city: '深圳' } }
最後透過group進行聚合,以_id為聚合依賴,合併相同_id的資料。
總結:
這種方法是能夠達到過濾陣列的要求,但是有一個問題,拆分陣列比較簡單,想要再合併起來就不容易了。group只能以某一個變數為基準聚合,其他變數都會丟失。比如最後的結果只保留了_id和travel,其他變數都丟失了。
方法二
方法二:使用$match
過濾符合條件的根文件結果集,然後使用$project
返回對應欄位的同時,在travel
陣列中使用$filter
進行內部過濾,返回最終結果集
db.getCollection('test').aggregate(
[
{
$match : { "job": "coder" }
},
{
$project: {
"uid": 1,
"name": 1,
"travel": {
$filter: {
input: "$travel",
as: "item",
cond: { $eq : ["$$item.vehicle","train"] }
}
}
}
}
]
)
結果分析:
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
travel: [ { vehicle: 'train', city: '北京' },{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
travel: [ { vehicle: 'train', city: '深圳' } ] }
分析:
mongo中查詢分為兩種:普通查詢和高階查詢。高階查詢包括聚合查詢,用aggregate關鍵字實現。
MongoDB的聚合管道將MongoDB文件在一個管道處理完畢後將結果傳遞給下一個管道處理。管道操作是可以重複的。
這裡我們介紹一下聚合框架中常用的幾個操作:
$project
:修改輸入文件的結構。可以用來重新命名、增加或刪除域,也可以用於建立計算結果以及巢狀文件。$match
:用於過濾資料,只輸出符合條件的文件。$match使用MongoDB的標準查詢操作。$limit
:用來限制MongoDB聚合管道返回的文件數。$skip
:在聚合管道中跳過指定數量的文件,並返回餘下的文件。$unwind
:將文件中的某一個陣列型別欄位拆分成多條,每條包含陣列中的一個值。$group
:將集合中的文件分組,可用於統計結果。$sort
:將輸入文件排序後輸出。$geoNear
:輸出接近某一地理位置的有序文件。
這裡首先使用match過濾所有job=coder,然後使用project修改輸出的結構。在project中使用了filter來過濾陣列中的元素。
filter的定義如下:
根據指定條件選擇要返回的陣列的子集。返回僅包含與條件匹配的那些元素的陣列。返回的元素按原始順序。
$filter
具有以下語法:
{ $filter: { input: <array>, as: <string>, cond: <expression> } }
領域 | 規格 |
---|---|
input | 解析為陣列的表示式。 |
as | 可選的。代表陣列中每個單獨元素的變數名稱input。如果未指定名稱,則變數名稱預設為this |
cond | 該表示式可解析為布林值,該布林值用於確定輸出陣列中是否應包含元素。該表示式input使用在中指定的變數名稱分別引用陣列的每個元素as。 |
https://mongodb.net.cn/manual/reference/operator/aggregation/filter/
在cond將vehicle=train的元素留下,排除其他元素。
總結:
這種方法可以完成查詢目標,既可以過濾掉陣列中的元素,也可以返回完整的文件。
方法三
方法三:
透過投影查詢,先選擇符合條件的記錄,在透過使用投影運算子,需要返回的欄位,以及排除特定的欄位。
db.test.find(
{
job: "coder"
},
{
uid: 1,
name: 1,
travel: {
$filter: {
input: "$travel",
as: "item",
cond: { $eq : ["$$item.vehicle","train"] }
}
}
}
)
結果:
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),
uid: '1000001',
name: 'zhangsan',
travel:
[ { vehicle: 'train', city: '北京' },
{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),
uid: '1000002',
name: 'lisi',
travel: [ { vehicle: 'train', city: '深圳' } ] }
分析:
什麼是投影查詢?
在MongoDB中,投影查詢是一種查詢操作,用於選擇性地返回文件中的欄位。透過使用投影運算子,我們可以指定需要返回的欄位,以及是否要排除特定的欄位。
投影查詢語法如下所示:
db.collection.find({ <query> }, { <projection> })
其中,
在projection中保留欄位、排除欄位、選擇或排除陣列中的特定元素。利用選擇或排除陣列中的特定元素的特性也可以達到目的。
例如:
如果我們只想返回每個文件中的第一個標籤,我們可以這樣做:
db.products.find({}, { tags: { $slice: 1 } })
在本篇中透過filter方法來過濾陣列,保留符合條件的元素。
總結:
該方法能夠完成查詢目標,並且是一種簡潔的實現,普通查詢複雜度低,而且沒有太多關鍵字的使用。
參考文件
:
https://geek-docs.com/mongodb/mongodb-questions/393_mongodb_mongo_query_with_projection.html
https://segmentfault.com/a/1190000016629733
https://mongodb.net.cn/manual/reference/operator/aggregation/filter/
https://blog.csdn.net/weixin_44009447/article/details/115479348