การ query array
field ของ document เก็บ array ได้ และการ query มีเซอร์ไพรส์ที่เป็นมิตรฝังอยู่: เมื่อคุณกรอง field ที่เป็น array ด้วยค่าธรรมดา MongoDB ตรวจ ทุก element และตรงถ้า element ใดก็ตาม ผ่านเงื่อนไข กฎข้อเดียวนี้ทำให้ query array ง่าย ๆ อ่านเหมือน scalar ทุกประการ รายละเอียดปลีกย่อยปรากฏเมื่อต้องการให้หลายเงื่อนไขเป็นจริงบน element เดียวกัน ซึ่งเป็นจุดที่ $elemMatch มีค่า
ในบทนี้สมาชิกแต่ละคนมี array borrowed ของชื่อหนังสือ array scores ของตัวเลข และบางคนมี array tags ของป้ายความสนใจ ทุก output ด้านล่างจับมาจาก mongo 8.3.11, mongosh 2.11.1 และ Node driver 7.7.0 (แท็บ Python, Go และ Rust รันหนึ่งครั้งในโปรแกรม scratch บน server เดียวกัน)
ข้อมูลที่เรา query
หัวข้อที่มีชื่อว่า “ข้อมูลที่เรา query”ข้อมูลถูกเลือกให้ filter ต่างกันให้คำตอบ ต่างกัน — นั่นคือวิธีพิสูจน์ว่า operator ทำตามที่คุณคิด:
db.members.insertMany([ { name: "Ada", borrowed: ["Compilers"], scores: [70, 95], tags: ["js", "backend"] }, { name: "Grace", borrowed: ["Compilers", "Algorithms", "Databases"], scores: [70, 85, 95], tags: ["backend"] }, { name: "Linus", borrowed: [], scores: [], tags: [] }, { name: "Margaret", borrowed: ["Algorithms", "Compilers"], scores: [88], tags: ["js"] }, { name: "Edsger", borrowed: ["Databases"], scores: [60, 75] }, // no tags field { name: "Barbara", borrowed: "Compilers", scores: 85 }, // scalars, not arrays]);globalThis.names = (f) => db.members.find(f, { _id: 0, name: 1 }).sort({ name: 1 }).toArray().map((d) => d.name).join(", ");assert.strictEqual(db.members.countDocuments(), 6);await db.collection("members").insertMany([ { name: "Ada", borrowed: ["Compilers"], scores: [70, 95], tags: ["js", "backend"] }, { name: "Grace", borrowed: ["Compilers", "Algorithms", "Databases"], scores: [70, 85, 95], tags: ["backend"] }, { name: "Linus", borrowed: [], scores: [], tags: [] }, { name: "Margaret", borrowed: ["Algorithms", "Compilers"], scores: [88], tags: ["js"] }, { name: "Edsger", borrowed: ["Databases"], scores: [60, 75] }, { name: "Barbara", borrowed: "Compilers", scores: 85 },]);if ((await db.collection("members").countDocuments()) !== 6) throw new Error("expected 6");การจับคู่ element เดียว
หัวข้อที่มีชื่อว่า “การจับคู่ element เดียว”เพื่อถามว่า “ใครกำลังยืม Compilers อยู่?” ให้กรอง field array ด้วยค่าเหมือนเป็น scalar MongoDB มองเข้าไปใน array ให้:
console.log("borrowed 'Compilers' :", names({ borrowed: "Compilers" }));console.log("borrowed ['Compilers'] (whole array):", names({ borrowed: ["Compilers"] }));console.log("borrowed ['Algorithms','Compilers'] :", names({ borrowed: ["Algorithms", "Compilers"] }));console.log("borrowed ['Compilers','Algorithms'] :", names({ borrowed: ["Compilers", "Algorithms"] }));assert.strictEqual(names({ borrowed: "Compilers" }), "Ada, Barbara, Grace, Margaret");const docs = await db.collection("members") .find({ borrowed: "Compilers" }) .toArray();console.log(docs.map((d) => d.name).sort());if (docs.length !== 4) throw new Error("expected 4");docs = list(db.members.find({"borrowed": "Compilers"}))cur, err := coll.Find(ctx, bson.M{"borrowed": "Compilers"})if err != nil { return err}var docs []bson.Mif err := cur.All(ctx, &docs); err != nil { return err}let mut cursor = coll .find(doc! { "borrowed": "Compilers" }) .await?;สมาชิกที่ array มีชื่อนั้นตรงกัน ไม่ว่าอยู่ตำแหน่งใดในลิสต์ — และสังเกต Barbara ด้วย ที่ borrowed เป็น string "Compilers" ธรรมดา (scalar ที่เท่ากับค่าก็ตรงเช่นกัน) การส่ง array ทั้งก้อนให้ filter แทนหมายถึง array เท่ากันทุกประการ รวมลำดับ:
borrowed 'Compilers' : Ada, Barbara, Grace, Margaretborrowed ['Compilers'] (whole array): Adaborrowed ['Algorithms','Compilers'] : Margaretborrowed ['Compilers','Algorithms'] :["Compilers"] ตรงกับ Ada เท่านั้น ซึ่ง array เป็นแบบนั้นพอดี; ["Compilers","Algorithms"] ไม่ตรงใครเพราะ array ของ Margaret เก็บในลำดับตรงข้าม
หลายเงื่อนไขบน element เดียวกัน: $elemMatch
หัวข้อที่มีชื่อว่า “หลายเงื่อนไขบน element เดียวกัน: $elemMatch”นี่คือกับดัก สมมติ scores เก็บ array ของตัวเลขและคุณต้องการ document ที่มี element มากกว่า 80 และ น้อยกว่า 90 การเขียน { scores: { $gt: 80, $lt: 90 } } ไม่ได้ หมายถึงอย่างนั้น — มันตรงถ้า element บางตัว เกิน 80 และ element บางตัว (ที่อาจเป็นคนละตัว) ต่ำกว่า 90 เพื่อตรึงทั้งสองเงื่อนไขกับ element เดียว ใช้ $elemMatch seed มีสมาชิกสองคนที่ array แค่ ดูคล้าย กัน Ada [70, 95] และ Grace [70, 85, 95]:
console.log("plain {$gt: 80, $lt: 90} :", names({ scores: { $gt: 80, $lt: 90 } }));console.log("$elemMatch {$gt: 80, $lt: 90} :", names({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } }));assert.strictEqual(names({ scores: { $gt: 80, $lt: 90 } }).includes("Ada"), true); // 95 is >80 and 70 is <90: two DIFFERENT elementsassert.strictEqual(names({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } }), "Grace, Margaret");const docs = await db.collection("members").find({ scores: { $elemMatch: { $gt: 80, $lt: 90 } }}).toArray();console.log(docs.map((d) => d.name).sort());if (docs.length !== 2) throw new Error("expected Grace and Margaret");docs = list(db.members.find({ "scores": {"$elemMatch": {"$gt": 80, "$lt": 90}}}))cur, err := coll.Find(ctx, bson.M{ "scores": bson.M{"$elemMatch": bson.M{"$gt": 80, "$lt": 90}},})if err != nil { return err}var docs []bson.Mif err := cur.All(ctx, &docs); err != nil { return err}let mut cursor = coll .find(doc! { "scores": { "$elemMatch": { "$gt": 80, "$lt": 90 } } }) .await?;สองรูปแบบคืนคนละคน:
plain {$gt: 80, $lt: 90} : Ada, Barbara, Grace, Margaret$elemMatch {$gt: 80, $lt: 90} : Grace, Margaretมีเพียง Grace (85) และ Margaret (88) ที่มี element หนึ่งตัว อยู่ในช่วง 81 ถึง 89 รูปแบบธรรมดายังคืน Ada ซึ่ง array [70, 95] ไม่มี element ในช่วง — 95 ผ่าน > 80 และแยกกัน 70 ผ่าน < 90 — และ Barbara ซึ่ง scores เป็น scalar 85 (scalar ถูกทดสอบกับทั้งสองเงื่อนไขโดยตรง)
กฎเดียวกันใช้กับ array ของ sub-document กับ order ที่ items เก็บ { price, qty } “สินค้าที่ราคาเกิน 1000 และจำนวนอย่างน้อย 2”:
db.orders.insertMany([ { _id: 1, items: [{ sku: "a", price: 1500, qty: 1 }, { sku: "b", price: 20, qty: 5 }] }, { _id: 2, items: [{ sku: "c", price: 1500, qty: 3 }] },]);const ids = (f) => db.orders.find(f, { _id: 1 }).toArray().map((d) => d._id);console.log("plain dotted paths :", ids({ "items.price": { $gt: 1000 }, "items.qty": { $gte: 2 } }));console.log("$elemMatch :", ids({ items: { $elemMatch: { price: { $gt: 1000 }, qty: { $gte: 2 } } } }));console.log("$all of 2 $elemMatch:", ids({ items: { $all: [{ $elemMatch: { price: { $gt: 1000 } } }, { $elemMatch: { qty: { $gte: 2 } } }] } }));assert.strictEqual(ids({ items: { $elemMatch: { price: { $gt: 1000 }, qty: { $gte: 2 } } } }).join(), "2");plain dotted paths : [ 1, 2 ]$elemMatch : [ 2 ]$all of 2 $elemMatch: [ 1, 2 ]order 1 มีสินค้าแพง (qty 1) และสินค้า อีกชิ้น ที่ถูกแต่ qty 5: รูปแบบธรรมดาตรงผิด; $elemMatch คืนเฉพาะ order 2 อย่างถูกต้อง และ $all ที่ห่อสอง $elemMatch แยกกัน ไม่ใช่ ทางแก้ — มันคือ “element บางตัวสำหรับแต่ละเงื่อนไข” อีกครั้ง order 1 จึงตรง
การบังคับให้มีหลายค่า: $all
หัวข้อที่มีชื่อว่า “การบังคับให้มีหลายค่า: $all”$all ตรงเมื่อ array มี ทุก ค่าที่ระบุ ในลำดับใดก็ได้ มันคือ AND ของการเป็นสมาชิก “ใครยืมทั้ง Algorithms และ Compilers พร้อมกัน?” คือสิ่งนี้:
console.log("borrowed $all [Algorithms, Compilers]:", names({ borrowed: { $all: ["Algorithms", "Compilers"] } }));console.log("tags $all [js, backend] :", names({ tags: { $all: ["js", "backend"] } }));console.log("tags $in [js, backend] :", names({ tags: { $in: ["js", "backend"] } }));console.log("borrowed $all [] :", names({ borrowed: { $all: [] } }));assert.strictEqual(names({ borrowed: { $all: ["Algorithms", "Compilers"] } }), "Grace, Margaret");const docs = await db.collection("members").find({ borrowed: { $all: ["Algorithms", "Compilers"] }}).toArray();console.log(docs.map((d) => d.name).sort());if (docs.length !== 2) throw new Error("expected 2");docs = list(db.members.find({ "borrowed": {"$all": ["Algorithms", "Compilers"]}}))cur, err := coll.Find(ctx, bson.M{ "borrowed": bson.M{"$all": bson.A{"Algorithms", "Compilers"}},})if err != nil { return err}var docs []bson.Mif err := cur.All(ctx, &docs); err != nil { return err}let mut cursor = coll .find(doc! { "borrowed": { "$all": ["Algorithms", "Compilers"] } }) .await?;เฉพาะสมาชิกที่ถือทั้งสองชื่อถูกคืน; มีเพียงอันเดียวไม่พอ Grace ตรงด้วย — $all เมิน element เกินและลำดับ — และ $all คือ AND ที่ $in คือ OR:
borrowed $all [Algorithms, Compilers]: Grace, Margarettags $all [js, backend] : Adatags $in [js, backend] : Ada, Grace, Margaretborrowed $all [] :$all: [] ว่างตรง ไม่มีอะไรเลย (ไม่ใช่ “ทุกอย่าง”) กับดักเล็ก ๆ เมื่อสร้างลิสต์แบบ dynamic
การกรองตามความยาว: $size
หัวข้อที่มีชื่อว่า “การกรองตามความยาว: $size”$size ตรงกับ array ที่มีความยาวพอดี “ใครมีหนังสือยืมอยู่สองเล่มพอดี?” เป็น query ที่ใช้ operator เดียว:
console.log("borrowed $size 2 :", names({ borrowed: { $size: 2 } }));console.log("borrowed $size 0 :", names({ borrowed: { $size: 0 } }));console.log("tags $size 0 :", names({ tags: { $size: 0 } }), "| tags missing:", names({ tags: { $exists: false } }));console.log("more than one book :", names({ "borrowed.1": { $exists: true } }));let err;try { db.members.find({ borrowed: { $size: { $gt: 1 } } }).toArray(); } catch (e) { err = e; }console.log(err.name, err.code, err.message);assert.strictEqual(err.code, 2);const docs = await db.collection("members") .find({ borrowed: { $size: 2 } }) .toArray();console.log(docs.map((d) => d.name));if (docs.length !== 1) throw new Error("expected Margaret only");docs = list(db.members.find({"borrowed": {"$size": 2}}))cur, err := coll.Find(ctx, bson.M{"borrowed": bson.M{"$size": 2}})if err != nil { return err}var docs []bson.Mif err := cur.All(ctx, &docs); err != nil { return err}let mut cursor = coll .find(doc! { "borrowed": { "$size": 2 } }) .await?;สมาชิกที่ array borrowed มี element สองตัวพอดีตรงกัน $size รับ ตัวเลขตรง ๆ เท่านั้น — ช่วงคือ error จริง — และทางอ้อมสำหรับ “มากกว่าหนึ่ง” คือทดสอบว่า element ที่ index 1 มีอยู่:
borrowed $size 2 : Margaretborrowed $size 0 : Linustags $size 0 : Linus | tags missing: Barbara, Edsgermore than one book : Grace, MargaretMongoServerError 2 Failed to parse $size. Expected a number in: $size: { $gt: 1 }สังเกตความต่างระหว่างว่างกับขาด: $size: 0 พบ Linus (array ว่าง) ไม่ใช่สมาชิกที่ไม่มี field tags
การเข้าถึง element ด้วย index
หัวข้อที่มีชื่อว่า “การเข้าถึง element ด้วย index”ตำแหน่งใน array เข้าถึงได้ด้วย path ตัวเลขคั่นจุด: borrowed.0 คือ element แรก borrowed.1 ตัวที่สอง เป็นต้น ใช้สิ่งนี้ query element ที่ตำแหน่งที่รู้ — “ใครที่หนังสือ เล่มแรก ที่ยืมคือ Algorithms?”:
console.log("borrowed.0 = Algorithms:", names({ "borrowed.0": "Algorithms" }));assert.strictEqual(names({ "borrowed.0": "Algorithms" }), "Margaret");borrowed.0 = Algorithms: Margaretdot path เดียวกันใช้ได้เมื่อ array เก็บ sub-document — reviews.0.rating เข้าไปถึง field rating ของรีวิวแรก รูปแบบที่บทถัดไปสำรวจอย่างลึก
ดึงเฉพาะ element ที่ตรงกลับมา
หัวข้อที่มีชื่อว่า “ดึงเฉพาะ element ที่ตรงกลับมา”filter ตัดสิน document ที่กลับมา ไม่ใช่ element ของ array การตัด array ใช้ projection: positional $ (element แรกที่ filter ตรง), $elemMatch ใน projection หรือ $slice seed มี Grace ที่มีสามคะแนน:
const sorted = (c) => c.sort({ name: 1 }).toArray();console.log("scores.$ :", sorted(db.members.find({ scores: { $gt: 80 } }, { _id: 0, name: 1, "scores.$": 1 })));console.log("$elemMatch proj :", db.members.find({ name: "Grace" }, { _id: 0, name: 1, scores: { $elemMatch: { $gt: 80 } } }).toArray());console.log("$slice 2 :", db.members.find({ name: "Grace" }, { _id: 0, name: 1, borrowed: { $slice: 2 } }).toArray());assert.deepStrictEqual(Array.from(db.members.findOne({ name: "Grace", scores: { $gt: 80 } }, { "scores.$": 1 }).scores), [85]);scores.$ : [ { name: 'Ada', scores: [ 95 ] }, { name: 'Barbara', scores: 85 }, { name: 'Grace', scores: [ 85 ] }, { name: 'Margaret', scores: [ 88 ] }]$elemMatch proj : [ { name: 'Grace', scores: [ 85 ] } ]$slice 2 : [ { name: 'Grace', borrowed: [ 'Compilers', 'Algorithms' ] } ]positional $ คืน element แรก ที่ผ่าน filter (95 สำหรับ Ada คะแนนแรกที่เกิน 80; 85 สำหรับ Grace แม้ 95 ก็ผ่านเช่นกัน); $elemMatch และ $slice ใน projection อิสระจาก filter positional $ ต้องการให้ array ปรากฏใน query
ใน Compass: filter ของ array ใส่ในช่อง Filter เหมือน scalar เช่น { borrowed: { $size: 2 } } (เอกสาร Compass, mongodb.com/docs/compass/current/query/filter, มีตัวอย่าง $elemMatch ด้วย); ขยาย array ที่ตรงในมุมมอง document เพื่อดูแต่ละ element
array ใช้ index ได้ไหม?
หัวข้อที่มีชื่อว่า “array ใช้ index ได้ไหม?”ได้ — index บน field array กลายเป็น multikey index มีหนึ่งรายการต่อ element สร้าง { borrowed: 1 } แล้ว explain query แบบ element บนหกเอกสารของเรา:
db.members.createIndex({ borrowed: 1 });const e = db.members.find({ borrowed: "Compilers" }).explain("executionStats");const ix = e.queryPlanner.winningPlan.inputStage;console.log("stage:", ix.stage, "| isMultiKey:", ix.isMultiKey, "| keys", e.executionStats.totalKeysExamined, "| docs", e.executionStats.totalDocsExamined);assert.strictEqual(ix.isMultiKey, true);stage: IXSCAN | isMultiKey: true | keys 4 | docs 4โมดูล indexes ครอบคลุมกฎ multikey (รวม “cannot index parallel arrays”)
เคล็ดลับและข้อควรระวัง
หัวข้อที่มีชื่อว่า “เคล็ดลับและข้อควรระวัง”- ทำไม
$elemMatchสำคัญ: ถ้าไม่มี หลายเงื่อนไขบน field array ถูกตรวจ ข้ามทุก element{ scores: { $gt: 80, $lt: 90 } }จึงตรงกับ[70, 95](Ada)$elemMatchบังคับทุกเงื่อนไขกับ element เดียว ซึ่งเกือบทุกครั้งคือสิ่งที่คุณตั้งใจ - ค่า array ทั้งก้อนหมายถึงความเท่ากัน ไม่ใช่การบรรจุ
{ borrowed: ["Compilers"] }ตรงเฉพาะ array ที่เป็นแบบนั้นพอดีในลำดับนั้น $allไม่สนลำดับ และเมิน element เกิน;$all: []ไม่ตรงอะไร$sizeในทางตรงข้าม กำหนดความยาวพอดี$sizeรับตัวเลขตรง ๆ เท่านั้น (Failed to parse $size. Expected a number) สำหรับ “มากกว่าหนึ่ง element” ทดสอบ"field.1": { $exists: true }- filter ค่าเดียวบน array ตรงแบบบรรจุ — และตรง scalar ที่เท่ากับค่านั้นด้วย (Barbara)
- filter ไม่ตัด array ใช้ positional
$,$elemMatchใน projection หรือ$slice
อ่านโค้ดที่ AI สร้าง
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI สร้าง”ผู้ช่วย AI ที่ถูกขอ “สมาชิกที่มีคะแนนแบนด์ B ระหว่าง 80 ถึง 90” จะเขียน filter scalar ที่เป็นธรรมชาติ:
🐛 หา bug ในโค้ดนี้
// AI comment: "students with a score between 80 and 90"const bBand = await db.collection("members").find({ scores: { $gt: 80, $lt: 90 } }).toArray();await sendCongratulations(bBand);เฉลย
บน field array สองเงื่อนไขถูกทำให้เป็นจริงแยกกันโดยคนละ element บนข้อมูลของเรามันคืน สี่ สมาชิก — Ada [70, 95], Barbara 85, Grace [70, 85, 95] และ Margaret [88] — ทั้งที่มีเพียง Grace และ Margaret ที่มีคะแนนในช่วง Ada จะได้รับคำแสดงความยินดีสำหรับคะแนน 95 และ 70 แต่ไม่มี B ทางแก้คือตรึงทั้งสองขอบกับ element เดียว:
const bug = names({ scores: { $gt: 80, $lt: 90 } });const fixed = names({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } });console.log("plain :", bug);console.log("$elemMatch :", fixed);assert.strictEqual(bug, "Ada, Barbara, Grace, Margaret");assert.strictEqual(fixed, "Grace, Margaret");plain : Ada, Barbara, Grace, Margaret$elemMatch : Grace, Margaret(scalar 85 ของ Barbara หลุดจาก $elemMatch เพียงเพราะไม่ใช่ element ของ array; ถ้ามี scalar ปนกับ array ใน field เดียว ให้ทำ field ให้เป็นปกติก่อน — schema validation ในโมดูลถัดไปป้องกันได้)
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”field array คือ เซ็ตของค่าอิสระที่ filter เห็นพร้อมกันหมด แต่ละเงื่อนไขถาม “element ใด ผ่านฉันไหม?” และเงื่อนไขต่างกันถูกทำให้เป็นจริงโดยคนละ element ได้ $elemMatch รวมเงื่อนไขเป็นคำถามเดียวเกี่ยวกับ element เดียว; $all AND คำถามการเป็นสมาชิก; $size และ index path ถามเรื่องรูปร่าง query เลือก document; projection ($, $elemMatch, $slice) เลือก element
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
$elemMatch สำหรับหลายเงื่อนไข | บังคับทุกเงื่อนไขเป็นจริงบน element เดียว ตรงเจตนาจริงเกือบทุกครั้ง | เขียนมากขึ้นเล็กน้อย; ต้องคิดเรื่อง “element เดียวกัน” ตั้งแต่แรก |
operator แยกบน field เดียวกัน (ไม่มี $elemMatch) | เขียนสั้นกว่า อ่านเหมือน query scalar ธรรมดา | แต่ละเงื่อนไขผ่านด้วยคนละ element ได้ ทำให้ตรงโดยไม่ตั้งใจ (Ada ด้านบน) — bug เงียบที่พบบ่อย |
$all | แสดง “มีครบทุกค่าเหล่านี้” | เมิน element เกินและลำดับ; ลิสต์ว่างไม่ตรงอะไร |
query ตำแหน่ง/ความยาว ($size, "field.0") | เจาะตำแหน่งหรือความยาวโดยตรงในการเรียกเดียว | เปราะเมื่อ array เรียงลำดับใหม่ และ $size รับแค่ค่าตรง ๆ ไม่ใช่ช่วง |
| multikey index บน array | query แบบ element ใช้ IXSCAN (4 key สำหรับ 4 ผลลัพธ์) | ขนาด index และต้นทุนการเขียนโตตามความยาว array; ข้อจำกัดของ compound multikey index |
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “filter ตัด array ที่คืนให้เหลือ element ที่ตรง” — มันแค่ตัดสินว่าคืนทั้ง document หรือไม่ ใช้
scores.$,$elemMatchใน projection หรือ$slice - “
borrowed.0มีความหมายทางธุรกิจคงที่” — ลำดับ array เปลี่ยนทุก$push/$pull; query ตามค่าทนทานกว่า - “
$allกับหลาย$elemMatchตรึง element เดียว” — แต่ละ clause ถูกทำให้เป็นจริงโดยคนละ element ได้ (order 1 ในการรันด้านบนตรง) - “
{ field: ['a'] }แปลว่ามี ‘a’” — แปลว่า array เท่ากับ['a'] - “
$sizeรับการเปรียบเทียบได้” — ไม่ได้:Failed to parse $size. Expected a number - “
$size: 0พบ document ที่ไม่มี field” — พบเฉพาะ array ว่าง;$exists: falseพบอันที่ไม่มี
ตัวอย่างจากของจริง
บทช่วยสอน array ของคู่มือ MongoDB อธิบายทั้งสองพฤติกรรมที่เราวัด: compound filter ตรงกับ array ที่ “elements … in some combination satisfy the query conditions. One element can satisfy the greater than
15condition and another element can satisfy the less than20condition, or a single element can satisfy both”; ส่วน$elemMatchคือ “so that at least one array element satisfies all the specified criteria” (Query an Array, เรียกอ่านเมื่อ 2026-09-30)
แท็บ Python/Go/Rust สะท้อนพฤติกรรมที่พิสูจน์แล้วของ shell/Node; แต่ละแท็บถูกรันหนึ่งครั้งในโปรแกรม scratch กับ server เดียวกันและคืนสมาชิกชุดเดียวกัน (borrowed: "Compilers", $elemMatch, $all, $size) แต่ไม่ถูกรันโดยตัวตรวจอัตโนมัติของคอร์ส