pipeline操作符號
幫助你進行複雜的操作,每個符號都會接受documents,並對這些document做些相應的操作,然後再將結果傳至下一個pipeline直到最後結果出現。
db.getCollection('sales').insertMany([
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : 5, "quantity" : 20, "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-04-04T21:23:13.331Z") }
]);
db.sales.aggregate( [ { $project : { _id: 0, item : 1 , price : 1 } } ] )
// 只有被設置進去的字段才會顯示出來,其他列就不會被顯示。
$project
假設一個collection裡面有數十個欄位,而我們真正需要的欄位只有一兩個,此時就可以使用project
$match
$match
主要用於對一群檔案的篩選
$group
$group
它的功能就是用來分組,你可以決定要依照什麼來分組。
$unwind
$unwind
英文解釋就是『拆分』,他可以將陣列欄位的每一個值拆分為單獨的document
$sort
$sort
它可以根據任何欄位進行排序,是的與搜尋時的用法一樣,但是有件事要注意。
如果大量的資料要進行排序,建議在管道的第一節進行排序,因為可以用索引。
$lookup
Mongodb 3.2版本 新增的聚合框架中的一种查询方式,它會在當前的collection返回的結果中新增一欄位。
官網上的說法太過文言文,簡單來說是當你想在當前的collection中新增一個欄位關連到另一個欄位,而關聯的key值存在於兩個collection中就可以用來匹配。
來看一下主要用法:
db.collection.aggregate([{
$lookup: {
from: "<collection to join>",
localField: "<field from the input documents>",
foreignField: "<field from the documents of the from collection>",
as: "<output array field>"
}
})
Field | Description |
---|---|
from | 從数据库中指定要执行连接的collection |
localField | 當前collection的欄位名稱 |
foreignField | 從from指定的collection裡面的欄位,如果from集合中的文档不包含foreignField,则$lookup会将值视为null以便进行匹配。 |
as | 存放結果而新增的欄位名稱 |
來看範例:
在資料庫中名為collection的order 插入資料
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
])
接下來再從inventory 中插入資料
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, description: "Incomplete" },
{ "_id" : 6 }
])
使用 $lookup:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
返回結果
{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
對數組使用$ lookup
首先有兩個 collection
comment
{
"_id" : ObjectId("5ec5e48b7f7fa92fe266d246"),
"content" : "测试",
"reply" : [
{
"content" : "回复1",
"userId" : ObjectId("5ec5e4c37f7fa92fe266d27e")
},
{
"content" : "回复2",
"userId" : ObjectId("5ec5e4ca7f7fa92fe266d285")
},
{
"content" : "回复3",
"userId" : ObjectId("5ec5e4dd7f7fa92fe266d290")
}
]
}
以及 user
/* 1 */
{
"_id" : ObjectId("5ec5e4c37f7fa92fe266d27e"),
"name" : "u1"
}
/* 2 */
{
"_id" : ObjectId("5ec5e4ca7f7fa92fe266d285"),
"name" : "u2"
}
/* 3 */
{
"_id" : ObjectId("5ec5e4dd7f7fa92fe266d290"),
"name" : "u3"
}
當需要在comment 表中 關聯到user,並輸出用戶的名稱時,我們需要用到comment的集合中的reply欄位,而這個欄位它是數組,此時我們可以先使用 unwind
將此數組拆分
db.comment.aggregate([
{
$unwind: '$reply' // 首先拆分了reply这个数组
},
{
$lookup: {
from: 'user', // 从哪个Schema中查询
localField: 'reply.userId', // 本地关联的字段
foreignField: '_id', // user中用的关联字段
as: 'userInfo' // 查询到所有user后放入的字段名,这个是自定义的,是个数组类型。
}
},
{
$unwind:'$userInfo' //因为lookup 关联到的数据会返回的是数组,所以继续拆分一下(这里看个人需要)
},
{
// 按照_id 分组
$group:{
_id:'$_id',
content:{$first:'$content'},
reply:{
$push:{
'content':'$reply.content',
'userId':'$reply.userId',
'user':'$userInfo.name'
}
}
}
}
])
從 MongoDB 3.4 開始,如果localField是一個數組,則可以將數組元素與標量foreignField匹配,而無需$unwind階段。這部分等有時間再來實際操作實驗看看
2021/1/30 - TODO
在官方文檔中說明在$lookpup
中如果使用了pipeline(管道)是無法直接訪問文檔中的字段
這時候就需要先在 let 中進行定義,然後才能在pipeline裡進行引用。
要訪問pipeline中的let變量,請使用$expr運算符。
例如:
// order collection
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
])
// warehouses collection
db.warehouses.insert([
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
])
db.orders.aggregate([
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
])
結果:
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2,
"stockdata" : [ { "warehouse" : "A", "instock" : 120 }, { "warehouse" : "B", "instock" : 60 } ] }
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1,
"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60,
"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
pipeline表達式
TODO 待更新
$first
$sum
$subtract
$eq = (等于)
$gt > (大于)
$gte >= (大于等于)
$lt < (小于)
$lte <= (小于等于)
$ne != (不等于)
$nin !in (not in)
$$ROOT
查詢運算(邏輯)符
$in
// people collction
[
{ "_id" : ObjectId("5ca7a4b0219efd687462f965"), "id" : 1, "name" : "jack", "age" : 73 ,"hobby" : [ "taichi" ] }
{ "_id" : ObjectId("5ca7a4c4219efd687462f968"), "id" : 4, "name" : "xiaogang", "age" : 13, "hobby" : [ "Shuttlecock", "basketball", "football" ] }
{ "_id" : ObjectId("5ca7a4JK2K3H5JK2H4K38"), "id" : 3, "name" : "anne", "age" : 13, "hobby" : [ "pingpong", "football" ] }
]
假設我們需要尋找興趣是包含桌球或是籃球的人
db.people.find({ id : { $in : ["pingpong","basketball"] } } );
$all
延續上面的例子,假設需求為興趣同時包含籃球和足球的人
db.people.find( { id: { $all:["basketball","football"] }})
$or
$or
查詢多個或關係的多值
繼續之前的例子,假設需求為興趣同時包含 籃球和足球 又或者 包含 太極或者桌球的人
db.user.find({
$or: [
{id:{$all:["basketball","football"]},},
{id:{$in:["taichi","pingpong"]},}
]
})
$and
繼續之前的例子,假設需求為興趣同時包含 籃球和足球 並且 包含 太極或者桌球的人
db.user.find({
$and: [
{id:{$all:["basketball","football"]},},
{id:{$in:["taichi","pingpong"]},}
]
})
2021-02-05 待更新
Todo: $push