https://flic.kr/p/qakxAH
MongoDB 的 document 欄位除了可以是物件,也能是一個陣列。MongoDB 同樣為此提供了有關陣列欄位的查詢符號。本文會解說如何撰寫對於陣列元素的查詢條件,也會從官方文件中挑選一些查詢符號來示範用法。若讀者想了解一般欄位的查詢方式,請參考【MongoDB】如何撰寫查詢語法。
一、範例資料介紹
在學習撰寫查詢條件前,我們先認識本文使用的範例資料,以便了解情境。我們假設要練習操作的 collection 叫做「student」,有著以下的資料。
這個 collection 儲存了學生資料的 document。欄位包含:名字(name)、科系(departments)與修習課程(courses)。從下一節開始,筆者會介紹各種查詢方式。
二、陣列包含某元素
若查詢條件是陣列包含某個指定的元素,查詢語法正好與「某欄位等於某值」的寫法相同。以下的範例是找出「財務金融」系的學生。即便一人身兼多個科系,只要陣列中有包含該值,仍符合條件。
db.student.find( { "departments": "財務金融" } ) // Vincent、Dora
三、陣列元素的欄位
若陣列的元素是一個個的物件,那麼以陣列元素的欄位來查詢,其寫法與 embedded document 相同。也就是以「.」符號區隔不同層次的欄位名稱,形成類似路徑的樣子。以下的範例是查詢有修習「會計學」的學生。
db.student.find( { "courses.name": "會計學" } ) // Vincent、Winnie、Mario
四、完全相等的陣列
筆者在第二節示範過,如果查詢語法寫成「陣列欄位等於某個值」,條件相當於陣列包含某個值。那麼,如果將傳入參數從一個值改為一個陣列,其意義就變成查詢具有完全相等陣列的 document。也就是含有的元素與它們的順序均相同。
以下的範例是查詢 departments 陣列,第一個元素是「資訊管理」,第二個元素是「財務金融」,僅有這兩個元素的 document。
db.student.find( { "departments": ["資訊管理", "財務金融"] } ) // Vincent
五、陣列包含多個指定元素
如果只在意陣列有一至數個我們指定的元素,而 document 本身的陣列長度與元素順序不限,則可使用 $all 符號。這個符號是「contains all」的概念。以下的範例是查詢 departments 陣列包含「通識」與「財務金融」的 document。
db.student.find( { "departments": { "$all": ["通識", "財務金融"] } } ) // Dora
六、陣列長度
(一)指定陣列長度
如果想要藉由陣列有多少個元素來查詢 document,可以使用 $size 符號。以下的範例是查詢修習剛好 3 門課程的學生。
db.student.find( { "courses": { "$size": 3 } } ) // Dora、Mario
(二)指定陣列長度範圍
如果查詢條件是陣列的元素數量在某個範圍,請參考「MongoDB - find document whose array length is less than or equal to 5」這篇問答。這會使用到 aggregation 的做法,它是一種類似 SQL 的 JOIN 的操作。
但假設查詢條件是陣列長度大於等於某個值,我們還是可以使用一點技巧。撰寫查詢欄位時,在陣列的欄位名稱後面加上「.」符號與一個數字,即可表示出該索引位置的元素(索引值從 0 開始計數)。接著再搭配 $exists 符號,我們可以理解:假設陣列存在索引 2 的元素,那該陣列就至少有 3 個元素。以下的範例是查詢至少修習 3 門課程的學生。
db.student.find( { "courses.2": { "$exists": true } } ) // Vincent、Dora、Mario
七、任一陣列元素符合條件
(一)使用 $elemMatch 符號
如果想撰寫的查詢條件,是陣列的任一元素符合我們所給定的條件,亦可使用 $elemMatch 查詢符號。該符號較特別,它接收的參數是另一個查詢條件(用來檢查元素),而不是一個用來比對的欄位值。以下的範例是查詢有修習大於 3 學分的課程的學生。
db.student.find( { "courses": { "$elemMatch": { "point": { "$gt": 3 } } } } ) // Vincent、Mario
(二)省略 $elemMatch 符號
事實上,以上的查詢條件可以簡化成如下的寫法。
db.student.find( { "courses.point": { "$gt": 3 } } ) // Vincent、Mario
雖然 $elemMatch 符號被省略了,但官方文件特別提醒,若是給予 $elemMatch 符號的條件中包含 $ne 與 $not 這種帶有「反向」意涵的符號,簡化前與後的意義是不一樣的。我們來比較兩者的差別。
以下的範例未使用 $elemMatch 符號。意義為查詢 departments 陣列中,所有元素都不是「財務金融」的 document。
db.student.find( { "departments": { "$ne": "財務金融" } } ) // Winnie、Mario
以下的範例使用了 $elemMatch 符號。條件變成只要 departments 陣列中有任一元素不是「財務金融」,就符合條件。於是找出更多 document。
db.student.find( { "departments": { "$elemMatch": { "$ne": "財務金融" } } } ) // Vincent、Dora、Winnie、Mario
留言
張貼留言