0Pricing
MongoDB Academy · บทเรียน

ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด

ผู้เรียนจะคิวรีเพื่อตรวจสอบว่าฟิลด์มีอยู่หรือเป็นชนิดใด โดยใช้ $exists และ $type เพื่อจัดการข้อมูลที่เป็นตัวเลือกหรือมีหลายชนิด

ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

Optional Fields in Flexible Schemas

MongoDB's flexible schema means some documents in a collection may have fields that others lack. A user document might have an optional phoneNumber field—some users provided it, others did not. In SQL, you would handle this with a NULL value in every row. In MongoDB, the field simply does not exist in some documents.

This difference has important implications for querying. MongoDB's element operators—primarily $exists and $type—let you query based on the presence or type of a field, rather than its value.

$exists: Checking Field Presence

$exists: true matches documents that have the specified field (regardless of its value, even if the value is null). $exists: false matches documents where the field is completely absent.

This distinction is subtle but important: a document with { phone: null } does match { phone: { $exists: true } } because the field exists—it is just null. Only a document with no phone field at all matches { phone: { $exists: false } }.

// Find users who have provided a phone number (field exists)
db.users.find({ phone: { $exists: true } });

// Find users who never provided a phone number (field absent)
db.users.find({ phone: { $exists: false } });

// Key distinction:
// { phone: null }    => $exists: true (field exists, value is null)
// { name: 'Alice' }  => $exists: false (no phone field at all)

// Combined: field exists AND is not null
db.users.find({ phone: { $exists: true, $ne: null } });

null vs Missing Field Queries

MongoDB's equality filter with null has a dual behavior that often surprises beginners: { phone: null } matches documents where phone is explicitly null AND documents where phone does not exist at all. This is because MongoDB considers an absent field to be implicitly null for equality comparisons.

If you want to distinguish between 'field is null' and 'field is missing', combine $exists with $eq: null. For most practical purposes, both cases mean 'no phone number provided' and you can treat them the same way.

// Matches BOTH null and missing field:
db.users.find({ phone: null });
// Returns: { name: 'Alice', phone: null }
//      AND: { name: 'Bob' }  (no phone field)

// Only where field is explicitly null:
db.users.find({ phone: { $eq: null, $exists: true } });
// Returns: { name: 'Alice', phone: null }
// NOT: { name: 'Bob' } (missing field)

// Only where field is missing:
db.users.find({ phone: { $exists: false } });

$type: Querying by BSON Type

MongoDB is schema-flexible, which means a field like price might be a Number in most documents but accidentally stored as a String in some legacy records. The $type operator lets you filter documents based on the BSON type of a field's value.

You can specify types by name (e.g., 'string', 'int', 'date') or by BSON type number (e.g., 2 for String, 16 for Int32). Using type names is more readable and preferred.

// Find documents where price is a string (data quality issue)
db.products.find({ price: { $type: 'string' } });
// These need to be fixed - price should be a number

// Find documents where price is a number (any numeric type)
db.products.find({ price: { $type: ['double', 'int', 'long', 'decimal'] } });
// $type accepts an array - matches any of the listed types

// Check for boolean
db.settings.find({ enabled: { $type: 'bool' } });

Common BSON Type Names

The most commonly used BSON type names for $type queries are:

  • 'double' — 64-bit floating point number (JS default number)
  • 'string' — UTF-8 string
  • 'object' — embedded document (sub-object)
  • 'array' — array
  • 'binData' — binary data
  • 'objectId' — ObjectId
  • 'bool' — boolean
  • 'date' — Date
  • 'null' — null value
  • 'int' — 32-bit integer
  • 'long' — 64-bit integer
  • 'decimal' — Decimal128
// Real-world use: find documents where tags is an array
db.products.find({ tags: { $type: 'array' } });

// Find docs where createdAt is a date (not a string)
db.events.find({ createdAt: { $type: 'date' } });

// Audit: find any document where _id is not an ObjectId
db.users.find({ _id: { $not: { $type: 'objectId' } } });
// These might be docs with custom string _ids

The 'number' Alias

A useful shortcut is the 'number' type alias, which matches any numeric BSON type: double, int, long, and decimal. This is equivalent to writing an array of all four numeric type names but much cleaner.

Use 'number' when you want to find all documents where a field is any kind of number, regardless of the specific precision. This is particularly useful for data quality checks where you want to identify fields that should be numeric but were accidentally stored as strings.

// 'number' alias matches double, int, long, decimal
db.products.find({ price: { $type: 'number' } });
// Same as: { price: { $type: ['double', 'int', 'long', 'decimal'] } }

// Data quality check: find price fields that are strings
// These should be numbers:
db.products.find({ price: { $type: 'string' } });

// Fix those docs:
db.products.find({ price: { $type: 'string' } }).forEach(doc => {
  db.products.updateOne(
    { _id: doc._id },
    { $set: { price: parseFloat(doc.price) } }
  );
});

Using $exists in Schema Migrations

Schema migrations in MongoDB often involve adding new required fields to existing documents. $exists helps you find the documents that still need to be updated—or that have already been migrated.

A typical migration flow: first use { newField: { $exists: false } } to find all legacy documents, then add the new field with a default value. Check progress by counting how many documents still have $exists: false. This rolling migration approach works without downtime because MongoDB's flexible schema allows old and new document shapes to coexist temporarily.

// Migration: add 'timezone' field with default value to legacy users
async function migrateTimezone() {
  const batchSize = 1000;
  let processed = 0;

  while (true) {
    const result = await db.collection('users').updateMany(
      { timezone: { $exists: false } },  // Legacy docs without field
      { $set: { timezone: 'UTC' } },     // Add default
      { limit: batchSize }               // Process in batches
    );
    processed += result.modifiedCount;
    if (result.modifiedCount === 0) break;
  }
  console.log('Migrated:', processed);
}

Combining $exists and $type

You can combine $exists and $type in one query to find documents where a field exists AND has a specific type. This is useful for data quality audits where you want to identify documents with the field set to the wrong type versus documents where the field is simply absent.

For example, in a collection where age should be a number, you might want to separately count: (1) documents missing the age field entirely, (2) documents where age is a string (data entry error), and (3) documents where age is a valid number.

// Audit 'age' field data quality
const total = await db.collection('users').countDocuments({});

const missingAge = await db.collection('users')
  .countDocuments({ age: { $exists: false } });

const ageIsString = await db.collection('users')
  .countDocuments({ age: { $type: 'string' } });

const ageIsNumber = await db.collection('users')
  .countDocuments({ age: { $type: 'number' } });

console.log(`Total: ${total}, Missing: ${missingAge}, String: ${ageIsString}, Number: ${ageIsNumber}`);

$expr: Cross-Field Comparisons

The $expr operator lets you use aggregation expressions inside a regular find() filter. This unlocks cross-field comparisons—something that is not possible with standard query operators.

For example: 'find orders where the discount is greater than 50% of the original price' requires comparing discount to a computed fraction of originalPrice—two fields in the same document. $expr enables these relationships using expression operators like $gt, $multiply, and $divide.

// Find products where salePrice < originalPrice * 0.7 (>30% off)
db.products.find({
  $expr: {
    $lt: [
      '$salePrice',
      { $multiply: ['$originalPrice', 0.7] }
    ]
  }
});

// Find orders where quantity ordered > quantity in stock
db.orders.find({
  $expr: { $gt: ['$quantityOrdered', '$quantityInStock'] }
});

Indexing for $exists Queries

Queries using $exists have mixed index support. An index on a field does not help with { field: { $exists: false } }—if the field is absent, it is not in the index—so MongoDB must scan every document. However, { field: { $exists: true } } can use a sparse index: a sparse index only stores entries for documents that have the indexed field, enabling fast lookups of existing documents.

Use db.collection.createIndex({ field: 1 }, { sparse: true }) to create a sparse index when you frequently query for documents that have an optional field.

// Sparse index: only indexes docs where 'proSubscriptionId' exists
db.users.createIndex(
  { proSubscriptionId: 1 },
  { sparse: true }
);

// This query now uses the sparse index efficiently:
db.users.find({ proSubscriptionId: { $exists: true } });
// Only pro subscribers are in the index

// Regular (non-sparse) index would include null entries for all users
// Sparse index is smaller and faster for optional-field queries

Data Quality Audit Workflow

A practical data quality audit workflow using and : first identify all distinct field patterns in your collection to spot inconsistencies, then categorize documents by their field types, and finally write targeted updateMany calls to normalize the data.

MongoDB's aggregation pipeline makes auditing easy: by of a field to count how many documents have each type. This gives you a clear picture of data quality issues before you start fixing them.

// Audit: how many docs have price as each type?
db.products.aggregate([
  {
    : {
      _id: { : '' },  // Group by BSON type of price
      count: { : 1 }
    }
  }
]);
// Results like:
// [{ _id: 'double', count: 4820 },
//  { _id: 'string', count: 3 },
//  { _id: 'missing', count: 12 }]
// Now you know exactly what to fix!

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: $exists queries for the presence or absence of a field—a missing field and a null field are different and can be distinguished by combining $exists: true with $ne: null, $type filters by BSON type name (string, number, date, array, etc.) and is essential for data quality audits in schema-flexible collections, and sparse indexes efficiently support $exists: true queries on optional fields by only indexing documents that have the field. Next up we explore regex queries and pattern matching for flexible text searches.

คำถามที่พบบ่อย

บทเรียน “ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด”

ผู้เรียนจะคิวรีเพื่อตรวจสอบว่าฟิลด์มีอยู่หรือเป็นชนิดใด โดยใช้ $exists และ $type เพื่อจัดการข้อมูลที่เป็นตัวเลือกหรือมีหลายชนิด คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม

ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ตัวดำเนินการเปรียบเทียบ: $eq, $gt, $lt, $in
  2. ตัวดำเนินการตรรกะ: $and, $or, $nor, $not
  3. ตัวดำเนินการองค์ประกอบและการตรวจสอบชนิด
  4. คิวรีนิพจน์ปกติและการจับคู่รูปแบบ
← กลับไปที่ MongoDB Academy