Mongodb elemmatch đối tượng lồng nhau

Chào Kris,

Đã lâu rồi kể từ khi bạn đăng câu hỏi này, bạn đã tìm thấy câu trả lời chưa?

Nếu bạn chỉ muốn truy xuất giá trị cao nhất của fileArray.version, bạn có thể sắp xếp chúng
Ví dụ

db.collection.aggregate([
    {"$match"  : { "inventory.name":"Fish"} },
    {"$unwind" : "$shapes" }, 
    {"$unwind" : "$shapes.fileArray" }, 
    {"$sort"   : {"shapes.fileArray.version": -1} }, 
    {"$limit"  : 1 }
]);

Trân trọng,
Vạn

MongoDB giúp dễ dàng tổ chức và lưu trữ dữ liệu trong cấu trúc tài liệu phân cấp. Tuy nhiên, việc tìm nạp dữ liệu cụ thể được nhúng trong các mảng con lồng nhau của tài liệu phân cấp sâu có thể là một thách thức, đặc biệt là khi bạn chỉ muốn tìm nạp dữ liệu bạn cần từ cơ sở dữ liệu và không muốn tìm nạp gì thêm. Tôi sẽ minh họa một kỹ thuật để tìm nạp các tài liệu con được lồng sâu một cách hiệu quả trong MongoDB, bao gồm cả việc triển khai giải pháp trong Golang bằng trình điều khiển mgo

Ghi chú. Các ví dụ mã đã được thử nghiệm với Golang 1. 10. 1, MongoDB 3. 6 và mgo 2. 0

Hiểu vấn đề

Giả sử chúng ta có một bộ sưu tập MongoDB để lưu trữ danh mục các mặt hàng thời trang cao cấp. Đây là một tài liệu JSON mẫu từ một bộ sưu tập như vậy

{
"_id" : "5ab3a7f781aeb8000181a6b7",
"catalogName" : "Handbags",
"publishDate" : "2018-02-28T12:00:04.258699255-05:00",
"brands" : [
{
"brandName" : "Gucci",
"retailers" : [
"Zara",
"Dillards",
"Lord & Taylor"
],
"items" : [
{
"name" : "Olphia",
"origin" : "Italy",
"price" : 1200.0
},
{
"name" : "Mormont",
"origin" : "Italy",
"price" : 1300.0
},
{
"name" : "Impreza",
"origin" : "UK",
"price" : 1400.0
}
]
},
{
"brandName" : "Coach",
"retailers" : [
"Macys",
"Harrahs",
"Lord & Taylor"
],
"items" : [
{
"name" : "Rogue",
"origin" : "United States",
"price" : 1500.0
},
{
"name" : "Charlie",
"origin" : "France",
"price" : 900.0
}
]
}
]
}

Mỗi tài liệu trong bộ sưu tập này đại diện cho một danh mục các mặt hàng, với các mặt hàng được sắp xếp thêm theo nhãn hiệu. Đây là một cấu trúc lồng nhau, với các mảng vật phẩm xuất hiện ở cấp độ thứ ba. Nếu bạn đã làm việc với các truy vấn MongoDB, có lẽ bạn đã quen với thực tế là khung truy vấn chủ yếu tập trung vào việc tìm nạp toàn bộ tài liệu phù hợp với tiêu chí truy vấn. Ví dụ: xem xét truy vấn sau trên bộ sưu tập này

db.catalog.find({  
"brands.items":{
$elemMatch:{"origin":"Italy",price:{ $gte:500 }
}
}
})

Truy vấn trên sẽ mang lại tất cả các tài liệu danh mục trong bộ sưu tập có một hoặc nhiều mặt hàng có nguồn gốc từ Ý và có giá từ 500 trở lên. Trong trường hợp bộ sưu tập của chúng tôi, chúng tôi sẽ lấy lại toàn bộ danh mục “Túi xách”, với tất cả các nhãn hiệu và mặt hàng của nó, ngay cả khi chúng tôi chỉ quan tâm đến những mặt hàng đáp ứng tiêu chí của chúng tôi. Nó sẽ tùy thuộc vào ứng dụng khách để lặp qua các mục và trích xuất dữ liệu liên quan. MongoDB có khung chiếu để giới hạn kết quả truy vấn, nhưng nó chỉ hoạt động ở cấp độ tài liệu con đầu tiên của bộ sưu tập. Ví dụ: truy vấn trên có thể sử dụng phép chiếu như sau

db.catalog.find({  
"brands.items":{
$elemMatch:{"origin":"Italy",price:{ $gte:500 }
}
}
},
{
"brands.$":1
})

Điều này sẽ giới hạn các tài liệu phụ "nhãn hiệu" trong kết quả là tài liệu phụ đầu tiên trong mỗi tài liệu danh mục nơi tìm thấy mặt hàng phù hợp. Mặc dù phương pháp chiếu này hạn chế dữ liệu quay lại từ máy chủ, nhưng nó có thể dẫn đến kết quả không mong muốn. Ví dụ: trong trường hợp này, chỉ những mặt hàng trong nhãn hiệu đầu tiên (trong mỗi tài liệu danh mục) có mặt hàng phù hợp mới được trả lại. Điều gì sẽ xảy ra nếu có các mặt hàng phù hợp ở các thương hiệu khác?

Vì vậy, làm cách nào để đảm bảo rằng truy vấn MongoDB của bạn cung cấp cho bạn dữ liệu chính xác mà bạn đang tìm kiếm trong các tài liệu con lồng nhau và không có gì khác?

Tổng hợp để giải cứu

May mắn thay, MongoDB đi kèm với một khung tổng hợp có thể được sử dụng để đạt được kết quả mong muốn là tìm nạp dữ liệu mảng con lồng nhau cụ thể từ cơ sở dữ liệu. Giả sử mục tiêu của chúng tôi là lấy danh sách tất cả các mặt hàng có nguồn gốc từ Ý và có giá từ 500 trở lên. Chúng tôi muốn đảm bảo rằng thao tác cơ sở dữ liệu chỉ trả về các mục phù hợp với tiêu chí và không có mục nào khác. Truy vấn tổng hợp sau hoàn thành mục tiêu này

db.getCollection('catalog').aggregate([
{ "$match": {
"brands": {
"$elemMatch": {
"items.origin": "Italy",
"items.price": { "$gte": 500 }
}
}
}},
{ "$project": { "_id":0, "brands":1 } },
{ "$addFields": {
"brands": {
"$filter": {
"input": {
"$map": {
"input": "$brands",
"as": "b",
"in": {
"items": {
"$filter": {
"input": "$$b.items",
"as": "i",
"cond": {
"$and": [
{ "$eq": [ "$$i.origin", "Italy" ] },
{ "$gte": [ "$$i.price", 500 ] }
]
}
}
}
}
}
},
"as": "b",
"cond":
{ "$gt": [ { "$size": "$$b.items" }, 0 ] }
}
}
}}
])

Khi truy vấn tổng hợp này được thực hiện trên bộ sưu tập mẫu (được điền bằng dữ liệu. json từ repo GitHub được tham chiếu sau), kết quả như sau


{
"brands" : [
{
"items" : [
{
"name" : "Olphia",
"origin" : "Italy",
"price" : 1200.0
},
{
"name" : "Mormont",
"origin" : "Italy",
"price" : 1300.0
}
]
}
]
}
{
"brands" : [
{
"items" : [
{
"name" : "Racer",
"origin" : "Italy",
"price" : 600.0
}
]
}
]
}

Như bạn có thể thấy, tập kết quả chỉ bao gồm dữ liệu cho các mục phù hợp với tiêu chí và không có gì khác. Truy vấn được sử dụng ở đây sử dụng một quy trình tổng hợp để tìm nạp và tinh chỉnh dần dữ liệu được trả về, sử dụng các giai đoạn quy trình tổng hợp $match, $project và $addFields. Nó cũng sử dụng toán tử tổng hợp $map để tinh chỉnh các mục có trong kết quả. Sau đây là phần giải thích cấp cao về quy trình, có thể đáng để xem chi tiết về các giai đoạn tổng hợp và toán tử trong tài liệu MongoDB

Tất cả điều này xảy ra trên máy chủ MongoDB bằng cách sử dụng các chỉ mục có sẵn, làm cho đây trở thành một cách tiếp cận hiệu quả tổng thể để truy vấn dữ liệu mảng con được lồng sâu. Cách tiếp cận này có thể được ngoại suy cho các mảng con được lồng sâu hơn vào cấu trúc tài liệu

Thực hiện giải pháp trong Golang

Bây giờ chúng ta đã xác định được một kỹ thuật MongoDB để cơ sở dữ liệu tìm nạp và chỉ trả về dữ liệu chúng ta cần, làm cách nào để triển khai điều này trong Golang? . Ở đây, 'c' là một thể hiện của loại Bộ sưu tập mgo. 'Origin' và 'minPrice' là các giá trị được tham số hóa cho tiêu chí truy vấn

pipe := c.Pipe([]bson.M{
{"$match": bson.M{
"brands": bson.M{
"$elemMatch": bson.M{
"items.origin": bson.M{"$eq": origin},
"items.price": bson.M{"$gte": minPrice},
},
},
}},
{"$project": bson.M{ "_id":0, "brands":1 } },
{"$addFields": bson.M{
"brands": bson.M{
"$filter": bson.M{
"input": bson.M{
"$map": bson.M{
"input": "$brands",
"as": "b",
"in": bson.M{
"items": bson.M{
"$filter": bson.M{
"input": "$$b.items",
"as": "i",
"cond": bson.M{
"$and": []interface{}{
bson.M{"$eq": []interface{}{"$$i.origin", origin}},
bson.M{"$gte": []interface{}{"$$i.price", minPrice}},
},
},
},
},
},
},
},
"as": "b",
"cond": bson.M{"$gt": []interface{}{bson.M{"$size": "$$b.items"}, 0}},
},
},
},
}})

Đường ống có thể được thực thi và phản hồi thu được như sau

var resp []bson.M
pipe.All(&resp)

'resp' được trả về bởi quá trình thực thi có cấu trúc bản đồ sau, khi nguồn gốc là "Ý" và giá tối thiểu là 500

________số 8_______

Lưu ý rằng điều này giống hệt với tập kết quả mà chúng ta đã thu được trước đó bằng cách thực thi trực tiếp đường dẫn tổng hợp bằng trình bao MongoDB. Tất cả những gì còn lại bây giờ là sắp xếp lại cái này thành một cấu trúc Go thích hợp

type Item struct {
Name string `bson:"name" json:"name"`
Origin string `bson:"brandName" json:"origin"`
Price float64 `bson:"price" json:"price"`
}
//traverse the bson Map returned by the aggregation and extract the //items
var itemsFound []Item
for _, catalogMap := range resp {
brands := catalogMap["brands"].([]interface{})
for _, b := range brands {
brandsMap := b.(bson.M)
items := brandsMap["items"].([]interface{})
for _, b := range items {
itemsMap := b.(bson.M)
data, _ := json.Marshal(itemsMap)
var item Item
if err := json.Unmarshal(data, &item); err != nil {
return nil, err
}
itemsFound = append(itemsFound, item)
}
}
}

Và đó là nó. Mảng 'itemsFound' hiện chứa dữ liệu mục mà chúng tôi đã tìm kiếm để truy vấn và truy xuất từ ​​bộ sưu tập MongoDB. Với cách tiếp cận này, chúng tôi đã tránh đưa toàn bộ tài liệu danh mục từ cơ sở dữ liệu MongoDB vào chương trình Go của mình và duyệt qua các tài liệu ở phía máy khách để trích xuất dữ liệu mặt hàng mà chúng tôi quan tâm. Công việc nặng nhọc để sàng lọc dữ liệu hiện đang diễn ra trên cơ sở dữ liệu

Mã nguồn

Mã nguồn cho ví dụ minh họa ở trên có sẵn trong repo Github sau. https. //github. com/psinghal04/mgonestedarrays