file-json-db

Queries & Operators

The query system is one of the most important parts of file-json-db. It lets you express filters in a MongoDB-like language. This page explains every operator in detail, including when to use it and real-world examples.

How a query works

When you call collection.find(query) or findOne(query), the library walks every document (or uses an index when possible) and asks:

“Does this document match the given query object?”

The matching is performed by the internal function matchQuery(doc, query) (you can see it in the source). It supports:

Simple equality

The simplest query is a plain object whose keys are field names and whose values are the exact values you want:

// Find the user whose email is exactly this string
users.findOne({ email: 'alice@acme.dev' });

// Find all issues that are currently open
issues.find({ status: 'open' }).toArray();

// Empty query matches every document
const all = users.find({}).toArray();

Under the hood this is equivalent to { email: { $eq: 'alice@acme.dev' } }.

Nested fields (dot notation)

You can reach into nested objects with a dot:

// Document shape:
// { profile: { address: { city: 'Lagos', country: 'NG' } } }

users.find({ 'profile.address.city': 'Lagos' }).toArray();
users.findOne({ 'profile.address.country': 'NG' });

The helper getNestedValue(doc, 'profile.address.city') walks the path safely and returns undefined if any intermediate key is missing.

Comparison operators

$eq – equal

Matches documents where the field is strictly equal to the given value (using JavaScript ===).

users.find({ age: { $eq: 25 } }).toArray();
// same as
users.find({ age: 25 }).toArray();

$ne – not equal

Matches documents where the field is not equal to the value.

// Everyone except the admin
users.find({ role: { $ne: 'admin' } }).toArray();

$gt / $gte – greater than / greater-or-equal

// Users older than 30
users.find({ age: { $gt: 30 } }).toArray();

// Orders with total >= 100
orders.find({ total: { $gte: 100 } }).toArray();

$lt / $lte – less than / less-or-equal

// Products cheaper than $20
products.find({ price: { $lt: 20 } }).toArray();

// Tasks due on or before today
tasks.find({ dueDate: { $lte: '2026-10-12' } }).toArray();

Combining comparisons on the same field

You can put several operators on one field:

// Age between 18 and 65 (inclusive)
users.find({
  age: { $gte: 18, $lte: 65 }
}).toArray();

Array membership operators

$in – value is one of several

Matches if the field’s value appears in the provided array. Extremely useful for “status is open or in-progress”.

// Issues that are still active
issues.find({
  status: { $in: ['open', 'in-progress', 'review'] }
}).toArray();

// Users with one of these roles
users.find({
  role: { $in: ['admin', 'moderator'] }
}).toArray();

$nin – value is not in the list

// Everything except closed or cancelled
issues.find({
  status: { $nin: ['closed', 'cancelled'] }
}).toArray();

Existence and type

$exists

Checks whether a field is present on the document (i.e. not undefined). Note that a field set to null is still considered to exist.

// Users who have a phone number recorded
users.find({ phone: { $exists: true } }).toArray();

// Users who never set a phone number
users.find({ phone: { $exists: false } }).toArray();

$type

Matches the JavaScript-style type of the value. Possible values: 'string', 'number', 'boolean', 'object', 'array', 'null'.

// Documents where age is stored as a number (not a string)
users.find({ age: { $type: 'number' } }).toArray();

// Tags field is an array
posts.find({ tags: { $type: 'array' } }).toArray();

Regular expressions – $regex

Matches a string field against a regular expression. You can pass either a string or a real RegExp object. Optional flags are supplied via $options.

// Usernames that start with "admin"
users.find({
  username: { $regex: '^admin' }
}).toArray();

// Case-insensitive search for "error" in the title
issues.find({
  title: { $regex: 'error', $options: 'i' }
}).toArray();

// Using a real RegExp
users.find({
  email: { $regex: /@acme\.dev$/i }
}).toArray();

Array-specific operators

$size

Matches arrays that have exactly the given length.

// Posts that have exactly 3 tags
posts.find({ tags: { $size: 3 } }).toArray();

$elemMatch

Matches documents where at least one element of an array satisfies the given sub-query. Useful when the array contains objects.

// Orders that contain at least one item with quantity > 5
orders.find({
  items: {
    $elemMatch: { quantity: { $gt: 5 } }
  }
}).toArray();

// Users who have a tag that is exactly "admin"
users.find({
  tags: { $elemMatch: { $eq: 'admin' } }
}).toArray();

Logical operators

$and

All of the sub-queries must match. You rarely need to write $and explicitly because top-level keys are already ANDed together, but it is useful when you need multiple conditions on the same field or nested logic.

users.find({
  $and: [
    { age: { $gte: 18 } },
    { age: { $lte: 65 } },
    { active: true }
  ]
}).toArray();

$or

At least one of the sub-queries must match.

// High-priority issues OR issues assigned to the current user
issues.find({
  $or: [
    { priority: 'high' },
    { assigneeId: currentUserId }
  ]
}).toArray();

$nor

None of the sub-queries may match (logical NOR).

// Issues that are neither closed nor cancelled
issues.find({
  $nor: [
    { status: 'closed' },
    { status: 'cancelled' }
  ]
}).toArray();

Real-world example: filtering a product catalog

const products = db.collection({
  name: 'products',
  autoId: true,
  indexes: ['category', 'sku', 'brand']
});

// Complex storefront filter:
// - category is "electronics" or "computers"
// - price between 50 and 500
// - still in stock
// - brand is not "Generic"
// - name contains "wireless" (case-insensitive)

const results = products.find({
  category: { $in: ['electronics', 'computers'] },
  price: { $gte: 50, $lte: 500 },
  stock: { $gt: 0 },
  brand: { $ne: 'Generic' },
  name: { $regex: 'wireless', $options: 'i' }
}).sort({ price: 1 }).limit(20).toArray();

Performance notes

See also Query Builder (limit, skip, sort, project, count) · Indexes