Skip to content

Nimbu.Query

Nimbu.Query allows you to retrieve objects from Nimbu by specifying search criteria. It provides a powerful and flexible way to filter, sort, and paginate your data.

Creating Queries

Basic Query

js
// Query a channel
const query = new Nimbu.Query('blog');

// Query using a subclass
const Product = Nimbu.Object.extend('products');
const query = new Nimbu.Query(Product);

Executing Queries

Get All Results

js
const query = new Nimbu.Query('products');
const products = await query.collection().fetch();

console.log(`Found ${products.length} products`);

Get First Result

js
const query = new Nimbu.Query('customers');
query.equalTo('email', '[email protected]');

const customer = await query.first();

if (customer) {
  console.log('Found customer:', customer.get('name'));
} else {
  console.log('Customer not found');
}

Get by ID

js
const query = new Nimbu.Query('products');
const product = await query.get('product_id_here');

console.log('Product:', product.get('name'));

Count Results

js
const query = new Nimbu.Query('customers');
query.equalTo('newsletter_subscribed', true);

const count = await query.count();
console.log(`${count} subscribers`);

Basic Constraints

Equal To

js
query.equalTo('status', 'active');
query.equalTo('category', 'electronics');
query.equalTo('featured', true);

Not Equal To

js
query.notEqualTo('status', 'deleted');
query.notEqualTo('stock', 0);

Greater Than / Less Than

js
// Greater than
query.greaterThan('price', 50);
query.greaterThan('stock', 0);

// Greater than or equal to
query.greaterThanOrEqualTo('age', 18);

// Less than
query.lessThan('price', 100);

// Less than or equal to
query.lessThanOrEqualTo('stock', 10);

Contained In (Array)

js
// Match any of these values
query.containedIn('status', ['active', 'pending', 'processing']);
query.containedIn('category', ['books', 'magazines']);

Not Contained In

js
query.notContainedIn('status', ['deleted', 'archived']);

Contains All

js
// Object must have all these tags
query.containsAll('tags', ['featured', 'sale']);

Exists

js
// Field must exist and not be null/undefined
query.exists('email');
query.exists('shipping_address');

// Field must not exist or be null
query.doesNotExist('deleted_at');

Text Matching

Starts With

js
query.startsWith('name', 'John');
query.startsWith('email', 'admin@');

Ends With

js
query.endsWith('email', '@example.com');
query.endsWith('title', '?');

Contains (Substring)

js
query.contains('description', 'sale');
query.contains('title', 'guide');

Matches (Regex)

js
// Case-insensitive search
query.matches('name', 'john', 'i');

// Email pattern
query.matches('email', '^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$');

Sorting

Ascending Order

js
query.ascending('created_at');
query.ascending('name');
query.ascending('price');

Descending Order

js
query.descending('created_at');
query.descending('price');
query.descending('popularity');

Multiple Sort Orders

js
// Sort by category ascending, then price descending
query.ascending('category');
query.descending('price');

Limiting and Pagination

Limit Results

js
// Get only first 10 results
query.limit(10);

// Get first 50 results
query.limit(50);

Skip Results

js
// Skip first 10 results (for pagination)
query.skip(10);

// Page 2 with 20 items per page
const page = 2;
const perPage = 20;
query.limit(perPage);
query.skip((page - 1) * perPage);

Pagination Example

js
async function getPage(pageNumber, pageSize = 20) {
  const query = new Nimbu.Query('products');
  query.equalTo('active', true);
  query.limit(pageSize);
  query.skip((pageNumber - 1) * pageSize);
  query.descending('created_at');

  const products = await query.collection().fetch();
  const total = await query.count();

  return {
    products,
    page: pageNumber,
    pageSize,
    totalPages: Math.ceil(total / pageSize),
    total
  };
}

// Usage
const page1 = await getPage(1);
console.log(`Page 1: ${page1.products.length} of ${page1.total} products`);

Working with Relations

js
// Fetch books with their authors in one query
const query = new Nimbu.Query('books');
query.include('author');

const books = await query.collection().fetch();

books.forEach(book => {
  const author = book.get('author');
  console.log(`${book.get('title')} by ${author.get('name')}`);
});

Include Multiple Relations

js
query.include('author');
query.include('publisher');
query.include('category');

// Or include nested relations
query.include('author.country');
js
// Find books by a specific author
const author = await new Nimbu.Query('authors').get(authorId);

const query = new Nimbu.Query('books');
query.equalTo('author', author);

const booksByAuthor = await query.collection().fetch();

Query a Relation

js
// Get a customer's favorite products
const customer = await new Nimbu.Query('customers').get(customerId);
const favorites = customer.relation('favorite_products');

const favoriteProducts = await favorites.query()
  .descending('created_at')
  .collection()
  .fetch();

Compound Queries

AND Conditions (Default)

js
// All conditions must match (AND)
query.equalTo('status', 'active');
query.greaterThan('price', 10);
query.lessThan('price', 100);

// Results: active AND price > 10 AND price < 100

OR Queries

js
const query1 = new Nimbu.Query('products');
query1.equalTo('category', 'books');

const query2 = new Nimbu.Query('products');
query2.equalTo('category', 'magazines');

// Combine with OR
const mainQuery = Nimbu.Query.or(query1, query2);

// Results: category = books OR category = magazines
const results = await mainQuery.collection().fetch();

Complex Conditions

js
// (category = books AND price < 20) OR (category = magazines)
const affordableBooks = new Nimbu.Query('products');
affordableBooks.equalTo('category', 'books');
affordableBooks.lessThan('price', 20);

const magazines = new Nimbu.Query('products');
magazines.equalTo('category', 'magazines');

const query = Nimbu.Query.or(affordableBooks, magazines);

Common Query Patterns

Find Active Items

js
const query = new Nimbu.Query('products');
query.equalTo('active', true);
query.greaterThan('stock', 0);
query.descending('created_at');
query.limit(20);

const products = await query.collection().fetch();

Search by Email

js
const query = new Nimbu.Query('customers');
query.equalTo('email', email.toLowerCase());

const customer = await query.first();

Recent Items

js
const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);

const query = new Nimbu.Query('orders');
query.greaterThan('created_at', oneDayAgo);
query.descending('created_at');

const recentOrders = await query.collection().fetch();

Range Queries

js
// Products between $10 and $50
const query = new Nimbu.Query('products');
query.greaterThanOrEqualTo('price', 10);
query.lessThanOrEqualTo('price', 50);

const products = await query.collection().fetch();

Search with Filters

js
async function searchProducts(filters) {
  const query = new Nimbu.Query('products');
  query.equalTo('active', true);

  if (filters.category) {
    query.equalTo('category', filters.category);
  }

  if (filters.minPrice) {
    query.greaterThanOrEqualTo('price', filters.minPrice);
  }

  if (filters.maxPrice) {
    query.lessThanOrEqualTo('price', filters.maxPrice);
  }

  if (filters.search) {
    query.matches('name', filters.search, 'i');
  }

  query.limit(filters.limit || 20);
  query.skip(filters.skip || 0);

  return await query.collection().fetch();
}

// Usage
const results = await searchProducts({
  category: 'electronics',
  minPrice: 50,
  maxPrice: 500,
  search: 'wireless',
  limit: 10
});

Advanced Techniques

Select Specific Fields

js
// Only fetch specific fields (saves bandwidth)
query.select('name', 'price', 'stock');

const products = await query.collection().fetch();

// products will only have name, price, and stock fields

Exclude Fields

js
// Fetch all fields except these
query.exclude('large_description', 'internal_notes');

Find Distinct Values

js
const query = new Nimbu.Query('products');
query.distinct('category');

const categories = await query.collection().fetch();

Error Handling

Handle Query Errors

js
try {
  const query = new Nimbu.Query('products');
  const products = await query.collection().fetch();

  console.log(`Found ${products.length} products`);
} catch (error) {
  console.error('Query failed:', error.message);

  if (error.code === 101) {
    console.error('Object not found');
  }
}

Validate Before Querying

js
async function safeQuery(className, constraints) {
  if (!className) {
    throw new Error('className is required');
  }

  const query = new Nimbu.Query(className);

  // Apply constraints
  Object.entries(constraints).forEach(([key, value]) => {
    query.equalTo(key, value);
  });

  try {
    return await query.collection().fetch();
  } catch (error) {
    console.error(`Query failed for ${className}:`, error.message);
    return [];
  }
}

Performance Tips

1. Use Specific Queries

js
// Good: Specific query
query.equalTo('status', 'active');
query.equalTo('category', 'books');
const results = await query.collection().fetch();

// Avoid: Fetch all then filter
const all = await query.collection().fetch();
const filtered = all.filter(item => item.get('status') === 'active');

2. Limit Results

js
// Good: Limit to what you need
query.limit(20);

// Avoid: Fetching thousands of records
const all = await query.collection().fetch(); // Could be huge!

3. Use Count for Numbers

js
// Good: Just count
const count = await query.count();

// Avoid: Fetch all to count
const all = await query.collection().fetch();
const count = all.length;

4. Select Only Needed Fields

js
// Good: Select specific fields
query.select('name', 'price');

// Avoid: Fetch all fields when you only need a few

5. Use Includes for Relations

js
// Good: One query with includes
query.include('author');
const books = await query.collection().fetch();

// Avoid: N+1 queries
const books = await query.collection().fetch();
for (const book of books) {
  await book.get('author').fetch(); // Separate query each time!
}

Real-World Examples

Newsletter Subscribers

js
async function getNewsletterSubscribers() {
  const query = new Nimbu.Query('customers');
  query.equalTo('newsletter_subscribed', true);
  query.exists('email');
  query.select('email', 'name');

  return await query.collection().fetch();
}

Low Stock Products

js
async function getLowStockProducts(threshold = 10) {
  const query = new Nimbu.Query('products');
  query.equalTo('active', true);
  query.lessThanOrEqualTo('stock', threshold);
  query.greaterThan('stock', 0);
  query.ascending('stock');

  return await query.collection().fetch();
}

Pending Orders

js
async function getPendingOrders(limit = 50) {
  const query = new Nimbu.Query('orders');
  query.containedIn('status', ['pending', 'processing']);
  query.descending('created_at');
  query.limit(limit);
  query.include('customer');

  return await query.collection().fetch();
}

Next Steps