ใช้ $match กรองเอกสารที่ไม่ใช่ Null ก่อน แล้วค่อย $group พร้อม $first เพื่อเอาค่าแรกที่ไม่ใช่ Null (Conditional First)
สร้างคอลเลกชันและข้อมูลตัวอย่าง (มี Null ระหว่างกลาง)
> db.conditionalFirstDemo.insertOne({_id: 100, "StudentName": "Chris", "StudentSubject": null});
{ "acknowledged": true, "insertedId": 100 }
> db.conditionalFirstDemo.insertOne({_id: 101, "StudentName": "Chris", "StudentSubject": null});
{ "acknowledged": true, "insertedId": 101 }
> db.conditionalFirstDemo.insertOne({_id: 102, "StudentName": "Chris", "StudentSubject": "MongoDB"});
{ "acknowledged": true, "insertedId": 102 }
> db.conditionalFirstDemo.insertOne({_id: 103, "StudentName": "Chris", "StudentSubject": "MongoDB"});
{ "acknowledged": true, "insertedId": 103 }
> db.conditionalFirstDemo.insertOne({_id: 104, "StudentName": "Chris", "StudentSubject": null});
{ "acknowledged": true, "insertedId": 104 }
แสดงข้อมูลทั้งหมด
> db.conditionalFirstDemo.find();
ผลลัพธ์: 5 เอกสาร สลับ Null และ MongoDB
Aggregation: ข้าม Null แล้วเอา $first
> db.conditionalFirstDemo.aggregate([
{$match: {"StudentSubject": {$ne: null}}},
{$group: {_id: "$StudentName", "StudentSubject": {$first: "$StudentSubject"}}}
]);
ผลลัพธ์: ได้ MongoDB (ค่าแรกที่ไม่ใช่ Null ตามลำดับ _id)
{"_id": "Chris", "StudentSubject": "MongoDB"}
การทำงาน
$match: {StudentSubject: {$ne: null}}กรองเอาเฉพาะเอกสารที่ StudentSubject ไม่เป็น Null (เหลือ _id 102, 103)$groupจัดกลุ่มตาม StudentName (Chris)$firstเอาค่าแรกในกลุ่ม (ตามลำดับ Natural/Order) = MongoDB
เทคนิคเพิ่มเติม
- เรียงลำดับก่อน Group (สำคัญ! $first ขึ้นกับ Sort Order):
db.coll.aggregate([ {$match: {field: {$ne: null}}}, {$sort: {date: 1}}, // เรียงเก่าไปใหม่ หรือใหม่ไปเก่า {$group: {_id: "$key", firstValue: {$first: "$field"}}} ]); - ใช้ $last แทน $first หากต้องการค่าสุดท้าย
- $firstN / $lastN (MongoDB 5.2+): เอา N ค่าแรก/สุดท้าย
กรณีซับซ้อน: First Non-null ใน Array
db.coll.aggregate([
{$unwind: "$arrayField"},
{$match: {"arrayField.value": {$ne: null}}},
{$group: {_id: "$_id", firstNonNull: {$first: "$arrayField.value"}}}
]);
หมายเหตุ
- โดย Default $first จะเอาค่าแรกตามลำดับ Document ใน Shard/Collection (ไม่ Guarantee Order)
- ต้องใช้
$sortก่อน $group เสมอหากต้องการ Order เฉพาะ $matchวางก่อน $sort เพื่อประสิทธิภาพ (Reduce Documents to Sort)