做爲非關係型數據庫中的佼佼者,MongoDB一大優點在於可以在一條文檔中存儲對象類型的數據,適當增長冗餘來讓數據庫更好用。文檔中一個對象類型的字段在MongoDB中被稱爲內嵌文檔(Embedded),也是MongoDB推薦的存儲形式。本文將基於官方文檔介紹內嵌文檔的查詢方法。python
對於初創企業的技術團隊來講,快速變更的需求致使沒有必要花費過多精力設計關係嚴格的數據表,而是能夠直接將相關聯的數據字段放在一塊兒,好比如下設計:web
db.books.insertMany( [
{ _id: 1, name: "python", price: 25, size: { h: 14, w: 21}, reading: ["Tom","John"] },
{ _id: 2, name: "mongo", price: 50, size: { h: 8.5, w: 11}, reading: ["John","Dave"] },
{ _id: 3, name: "webGL", price: 80, size: { h: 8.5, w: 11}, reading: ["Lily"] },
]);
複製代碼
若是使用相似關係型數據主鍵的將_id做爲引用的方式以下:數據庫
db.books.insertMany( [
{ _id: 1, name: "python", price: 25, h: 14, w: 21, reading: ["Tom","John"] },
{ _id: 2, name: "mongo", price: 50, h: 8.5, w: 11, reading: ["John","Dave"] },
{ _id: 3, name: "webGL", price: 80, h: 8.5, w: 11, reading: ["Lily"] },
]);
db.reading.insertMany( [
{ _id: 4, reader: "Tom", book_id:1 },
{ _id: 5, reader: "John", book_id:1 },
{ _id: 6, reader: "John", book_id:2 },
{ _id: 7, reader: "Dave", book_id:2 },
{ _id: 8, reader: "Lily", book_id:3 },
]);
複製代碼
相比之下,內嵌的方式有如下優勢:bash
內嵌文檔能夠下降字段修改對調用者的影響,好比在size字段下增長一個名爲"l"的子字段表示書籍長度,對於調用者來講只須要在用到時從size對象中拿出來便可,不須要再去額外獲取新的字段。 所以,在內嵌文檔較小,更新頻率不高時推薦使用內嵌文檔來存儲數據。spa
以1中數據爲例,對於單值的字段查詢,只須要寫一個查詢字典(query filter)便可:設計
db.books.find( { price: 25 } );
複製代碼
當查詢條件涉及內嵌文檔中的子字段時,使用"."(可遞進使用):code
db.books.find( { "size.h": 8.5 } );//針對字典對象
-->[
{ _id: 2, name: "mongo", price: 50, size: { h: 8.5, w: 11}, reading: ["John","Dave"] },
{ _id: 3, name: "webGL", price: 80, size: { h: 8.5, w: 11}, reading: ["Lily"] },
]
db.books.find( { "reading.0": "Tom" } );//針對列表
-->[
{ _id: 1, name: "python", price: 25, h: 14, w: 21, reading: ["Tom","John"] },
]
複製代碼
注意,不使用"."的話將會嚴格匹配內嵌文檔:對象
db.books.find( { "size": { h: 8.5} } );//不存在size字段爲{ h: 8.5}的文檔
-->[]
複製代碼
同普通查詢同樣,可使用運算符Query Operator:文檔
db.books.find( { "size.w": { $lt: 21} } );
-->[
{ _id: 2, name: "mongo", price: 50, size: { h: 8.5, w: 11}, reading: ["John","Dave"] },
{ _id: 3, name: "webGL", price: 80, size: { h: 8.5, w: 11}, reading: ["Lily"] },
]
複製代碼
示例數據string
db.books.insertMany( [
{ _id: 1, name: "python", price: 25, size: [14,21], reading: ["Tom","John"] },
{ _id: 2, name: "mongo", price: 50, size: [8.5,11], reading: ["John","Dave"] },
{ _id: 3, name: "webGL", price: 80, size: [8.5,11], reading: ["Lily"] },
]);
複製代碼
以1中數據爲例,指定列表,將嚴格按照全部元素及其順序查詢:
db.books.find( { reading: ["Tom","John"]" }); -->[ { _id: 1, name: "python", price: 25, size: size: [14,21], reading: ["Tom","John"] }, ] 複製代碼
若只要求指定元素存在且不要求順序,使用$all:
db.books.find( { reading: { $all: ["John"] } });
-->[
{ _id: 1, name: "python", price: 25, size:[14,21], reading: ["Tom","John"] },
{ _id: 2, name: "mongo", price: 50, size:[8.5,11], reading: ["John","Dave"] },
]
複製代碼
db.books.find( { reading: "John" });//只要列表中有一元素的值爲"John"即知足
-->[
{ _id: 1, name: "python", price: 25, size:[14,21], reading: ["Tom","John"] },
{ _id: 2, name: "mongo", price: 50, size:[8.5,11], reading: ["John","Dave"] },
]
db.books.find( { size: { $gt: 16, $lt: 15} });//只要列表中有一元素的值大於16,還有一元素小於15即知足
-->[
{ _id: 1, name: "python", price: 25, size: [14,21], reading: ["Tom","John"] },
]
複製代碼
使用$elemMatch
db.books.find( { size: { $elemMatch: { $gt: 22, $lt: 30 } } });
-->[
{ _id: 1, name: "python", price: 25, size: [14,21], reading: ["Tom","John"] },
]
複製代碼
使用$size
db.books.find( { reading: { $size: { $gt: 1} } });
-->[
{ _id: 3, name: "webGL", price: 80, size: [8.5,11], reading: ["Lily"] },
]
複製代碼
總結,對於列表類型的內嵌文檔,$elemMatch給出的並列條件要求至少有一個元素同時知足,不使用elemMatch時並列的條件只須要各自至少有一個元素知足便可。