【MongoDB】如何對陣列欄位進行查詢


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

上一篇:【MongoDB】如何撰寫查詢語法

下一篇:【MongoDB】使用操作符號來更新資料

留言