天天看點

MongoDB查詢内嵌數組(限定傳回符合條件的數組中的資料)(1)

項目背景

最近在項目中使用mongdb來儲存壓測結果中的監控資料,那麼在擷取監控資料時,遇到這樣一個問題: 一個doucument中包含一個内嵌數組,其中内嵌數組也是分成好幾類的數組(可以通過辨別判斷),那麼我隻需要傳回特定的數組,而不是傳回内嵌數組的所有資料。

原始資料:

{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "addTime" : ISODate("2018-03-16T03:05:04.363Z"),
    "tag" : "test",
    "userInfo" : [ 
        {
            "name" : "cj",
            "address" : "江蘇",
            "age" : ,
            "userTag" : "stu"
        }, 
        {
            "name" : "hj",
            "address" : "江蘇",
            "age" : ,
            "userTag" : "stu"
        }, 
        {
            "name" : "lbl",
            "address" : "美國",
            "age" : ,
            "userTag" : "teach"
        }
    ]
}
           

查詢條件是 tag =“test” userTag=”teach” 的學生的資訊。期望傳回的結果如下所示:

{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "userInfo" : [ 
        {
            "name" : "lbl",
            "address" : "美國",
            "age" : ,
            "userTag" : "teach"
        }
    ]
}
           

但大多是find 的結果 是這樣的

db.test.find({"userInfo.userTag":"teach","tag":"test"})
{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "addTime" : ISODate("2018-03-16T03:05:04.363Z"),
    "tag" : "test",
    "userInfo" : [ 
        {
            "name" : "cj",
            "address" : "江蘇",
            "age" : ,
            "userTag" : "stu"
        }, 
        {
            "name" : "hj",
            "address" : "江蘇",
            "age" : ,
            "userTag" : "stu"
        }, 
        {
            "name" : "lbl",
            "address" : "美國",
            "age" : ,
            "userTag" : "teach"
        }
    ]
}
           

$elemMatch 介紹

其實我們在學習一個新的東西的時候,我建議是去官方文檔檢視一下,畢竟官方的才是最權威的。官方位址:

https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/#proj._S_elemMatch.

我們可以看到 $elemMatch 是在projections方法下面,projections代表是限制傳回字段的方法。

修改後的方法:

db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch{"userTag":"teach"}}})
           

執行結果

{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "userInfo" : [ 
        {
            "name" : "lbl",
            "address" : "美國",
            "age" : ,
            "userTag" : "teach"
        }
    ]
}
           

終于得到我想要的結果了 hhhhhhhhhhhhhhhhhhhhh。

然後我又想擷取userInfo.userTag = “stu” 的資料,很簡單啊

db.test.find({"userInfo.userTag":"teach","tag":"test"},{"userInfo":{$elemMatch:{"userTag":"stu"}}})
           

但是結果出人意料:

{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "userInfo" : [ 
        {
            "name" : "cj",
            "address" : "江蘇",
            "age" : ,
            "userTag" : "stu"
        }
    ]
}
           

明明是2條stu的結果,為什麼至傳回一條呢? 其實$elemMatch 的定義 在官網中已經說過了,這是原話:

The $elemMatch operator limits the contents of an <array> field from the query results to contain only the first element matching the $elemMatch condition.
           

注意 only the first element 也就是僅僅比對第一個合适的元素。

那麼 對于數組中隻有一個傳回元素,我們可以使用$elemMatch來查詢,但是對于多個元素$elemMatch 是不适應。

$Aggregation介紹

文檔位址:https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

  1. $unwind:

    https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#pipe._S_unwind

    大概意思就是将數組中的每一個元素轉為每一條文檔

  2. $match:

    https://docs.mongodb.com/manual/reference/operator/aggregation/match/

    簡單的過濾文檔,條件查詢。

  3. $project

    https://docs.mongodb.com/manual/reference/operator/aggregation/project/

    修改輸入文檔的結構

執行指令:

db.test.aggregate([{"$unwind":"$userInfo"},
{"$match":{"userInfo.userTag":"stu","tag":"test"}},
{"$project":{"userInfo":}}])
           

結果

/* 1 */
{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "userInfo" : {
        "name" : "cj",
        "address" : "江蘇",
        "age" : ,
        "userTag" : "stu"
    }
}

/* 2 */
{
    "_id" : ObjectId("5aab3460353df3bd352e0e15"),
    "userInfo" : {
        "name" : "hj",
        "address" : "江蘇",
        "age" : ,
        "userTag" : "stu"
    }
}
           

這樣的一個結果就是我們想要的。感興趣的同學可以分别執行下這個三個操作(比較看看三個結果有什麼不同),你就能了解 $unwind、$match、$project 三個方法的作用

db.test.aggregate([{"$unwind":"$userInfo"}])
db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}}])
db.test.aggregate([{"$unwind":"$userInfo"},{"$match":{"userInfo.userTag":"stu","tag":"test"}},{"$project":{"userInfo":}}])
           

總結

  1. 之前查詢内嵌數組時,采用的方法是将整條document查詢出來之後,在對内嵌數組進行代碼過濾。隻是覺得這種查詢方式并沒有用到mongodb的其他的一些方法,還是需求驅動學習。
  2. 學習一個新的東西建議從官方文檔開始學習。

結束語:

下一篇開始介紹在spring中如何查詢mongdb。