Tài liệu

Tìm bản ghi

Tìm bản ghi Truy vấn bản ghi từ bảng với lọc, sắp xếp, phân trang và chọn trường. Cách dùng cơ bản // Find all records (up to default limit of 10) const result = await $ctx.$repos.products.find({}); // Access the data const products = result.data; // Array of product records Tham

Tìm bản ghi

Truy vấn bản ghi từ bảng với lọc, sắp xếp, phân trang và chọn trường.

Cách dùng cơ bản

// Find all records (up to default limit of 10)
const result = await $ctx.$repos.products.find({});

// Access the data
const products = result.data; // Array of product records

Tham số

await $ctx.$repos.tableName.find({
  filter: { ... },       // Filter conditions (optional)
  fields: '...',         // Fields to return (optional)
  limit: 10,             // Max records to return (optional, default: 10)
  sort: '...',           // Sort order (optional, default: primary key field)
  meta: 'totalCount'     // Request metadata (optional)
})

Tìm bản ghi

Get all records (no limit)

const result = await $ctx.$repos.products.find({
  limit: 0  // 0 = no limit, fetch all records
});
const allProducts = result.data;

Get limited records

const result = await $ctx.$repos.products.find({
  limit: 20  // Return max 20 records
});

Filter records

const result = await $ctx.$repos.products.find({
  filter: {
    category: { _eq: 'electronics' },
    price: { _gte: 100 }
  }
});

Lọc với nhiều điều kiện

const result = await $ctx.$repos.products.find({
  filter: {
    _and: [
      { category: { _eq: 'electronics' } },
      { price: { _between: [100, 500] } },
      { isActive: { _eq: true } }
    ]
  }
});

Chọn trường

Trả về các trường xác định

const result = await $ctx.$repos.products.find({
  fields: 'id,name,price'  // Comma-separated field names
});

Trả về mọi trường (mặc định)

const result = await $ctx.$repos.products.find({
  // No fields parameter = return all fields
});

Gồm trường của bảng liên quan

const result = await $ctx.$repos.products.find({
  fields: 'id,name,category.name,category.description'
});
// Returns: [{id: 1, name: "Phone", category: {name: "Electronics", description: "..."}}]

Gồm mọi trường từ một quan hệ

const result = await $ctx.$repos.products.find({
  fields: 'id,name,category.*'  // category.* = all category fields
});

Loại trừ trường

const result = await $ctx.$repos.enfyra_route_handler.find({
  fields: '-compiledCode'
});

Trường có tiền tố - sẽ chuyển phạm vi fields sang chế độ loại trừ. Ở chế độ này, tên trường dương bị bỏ qua; vì vậy fields: 'id,-compiledCode' trả về mọi trường có thể đọc trừ compiledCode.

Loại trừ lồng nhau dùng ký pháp dấu chấm:

const result = await $ctx.$repos.posts.find({
  fields: '-author.avatar'
});

Truy vấn deep (quan hệ lồng nhau)

Với truy vấn lồng nhau phức tạp qua nhiều cấp, hãy dùng tham số deep:

// Fetch products with category, and category's parent category
const result = await $ctx.$repos.products.find({
  fields: 'id,name',
  deep: {
    category: {
      fields: 'id,name',
      deep: {
        parent: {
          fields: 'id,name'
        }
      }
    }
  }
});

// With filter, sort, and pagination
const result = await $ctx.$repos.products.find({
  fields: 'id,name',
  deep: {
    category: {
      fields: 'id,name',
      filter: { isActive: { _eq: true } },
      sort: 'name',
      limit: 10
    }
  }
});

Trường deep cũng hỗ trợ chế độ loại trừ ở từng phạm vi lồng nhau:

const result = await $ctx.$repos.posts.find({
  fields: 'id,title',
  deep: {
    comments: {
      fields: '-compiledCode,-author.avatar',
      limit: 10,
      deep: {
        author: {
          fields: '-avatar'
        }
      }
    }
  }
});

Để xem hướng dẫn đầy đủ về deep query, hãy đọc Hướng dẫn Deep Queries, bao gồm: - Ví dụ lồng relation nhiều cấp - Lọc relation lồng nhau - Sắp xếp và phân trang theo từng cấp - Thực hành tốt về hiệu năng - URL query examples

Aggregate kết quả tính toán

Dùng aggregate() khi cần các giá trị đã tính toán thay vì raw record. Đây là phương thức riêng, không phải option của find(). Kết quả nằm trong data, không trả raw record và không dùng meta.aggregate.

Summary đơn giản

Bỏ qua dimensions để nhận một summary row cho tập dữ liệu sau filter:

const result = await $ctx.$repos.orders.aggregate({
  filter: {
    status: { _eq: 'paid' }
  },
  measures: {
    orders: { count: 'id' },
    revenue: { sum: 'amount' },
    averageOrderValue: { avg: 'amount' }
  }
});

// result.data[0] = { orders: 42, revenue: 18400, averageOrderValue: 438.1 }

Các operation được hỗ trợ là count, countDistinct, sum, avg, minmax. Các operation tính trên số yêu cầu field dạng số. Dùng countDistinct để đếm giá trị duy nhất, chẳng hạn số khách hàng.

Grouped analytics

Thêm dimensions khi cần một row cho từng giá trị hoặc time bucket:

const result = await $ctx.$repos.ai_usage.aggregate({
  filter: {
    createdAt: {
      _gte: '2026-08-01T00:00:00+07:00',
      _lte: '2026-08-31T23:59:59.999+07:00'
    }
  },
  dimensions: [
    {
      field: 'createdAt',
      bucket: 'day',
      timezone: 'Asia/Ho_Chi_Minh'
    }
  ],
  measures: {
    requests: { count: 'id' },
    totalTokens: { sum: 'totalTokens' },
    creditChargedMilli: { sum: 'creditChargedMilli' }
  },
  sort: [{ field: 'createdAt', direction: 'asc' }],
  limit: 100
});

Dimension có thể là scalar field hoặc date bucket với hour, day, week, month hay year. Field trong sort phải trỏ tới output key của dimension hoặc tên measure. Dùng pagelimit để phân trang các grouped row.

Các field dùng trong aggregate vẫn chịu kiểm tra authorization và giới hạn với field được mã hóa như các repository query khác. Khi query đi qua ranh giới bảo mật của user, hãy dùng repository có enforcement quyền trường như $ctx.$repos.main hoặc $ctx.$repos.secure.<tableName>.

Không đặt aggregate bên trong find(); dạng cũ này không còn được hỗ trợ.

Sắp xếp

Sort ascending

const result = await $ctx.$repos.products.find({
  sort: 'name'  // Sort by name ascending
});

Sort descending

const result = await $ctx.$repos.products.find({
  sort: '-price'  // Prefix with "-" for descending
});

Multi-field sorting

const result = await $ctx.$repos.products.find({
  sort: 'category,-price'  // Sort by category ASC, then price DESC
});

Toán tử lọc

Tham số filter hỗ trợ các toán tử tương tự MongoDB:

Toán tử so sánh

filter: {
  price: { _eq: 100 },        // Equal to
  price: { _neq: 100 },       // Not equal to
  price: { _gt: 100 },        // Greater than
  price: { _gte: 100 },       // Greater than or equal
  price: { _lt: 500 },        // Less than
  price: { _lte: 500 },       // Less than or equal
  price: { _between: [100, 500] }  // Between (inclusive)
}

Array Operators

filter: {
  category: { _in: ['electronics', 'gadgets'] },      // In array
  category: { _not_in: ['discontinued', 'old'] }     // Not in array
}

Text Search Operators

filter: {
  name: { _contains: 'phone' },        // Contains text (case-insensitive)
  name: { _starts_with: 'Apple' },     // Starts with
  name: { _ends_with: 'Pro' }          // Ends with
}

Null Checks

filter: {
  description: { _is_null: true },        // Field is null
  description: { _is_not_null: true }     // Field is not null
}

Logical Operators

filter: {
  _and: [                                    // All conditions must match
    { category: { _eq: 'electronics' } },
    { price: { _gte: 100 } }
  ],
  _or: [                                     // At least one condition must match
    { status: { _eq: 'active' } },
    { status: { _eq: 'pending' } }
  ],
  _not: {                                    // Negate condition
    category: { _eq: 'discontinued' }
  }
}

Complex Combinations

filter: {
  _and: [
    { category: { _in: ['electronics', 'gadgets'] } },
    {
      _or: [
        { price: { _lt: 100 } },
        { isOnSale: { _eq: true } }
      ]
    },
    { description: { _is_not_null: true } }
  ]
}

Lọc theo relation

Lọc bản ghi theo dữ liệu của bảng liên quan:

// Find products where category name is 'Electronics'
const result = await $ctx.$repos.products.find({
  filter: {
    category: {
      name: { _eq: 'Electronics' }
    }
  }
});

Metadata của truy vấn

Yêu cầu metadata cho truy vấn:

const result = await $ctx.$repos.products.find({
  filter: { category: { _eq: 'electronics' } },
  meta: 'totalCount'  // Get total count of all records (before filter)
});

console.log(result.meta.totalCount);  // Total records in table
console.log(result.data.length);      // Records matching filter

Các tùy chọn metadata có sẵn: - 'totalCount' - Total number of records in table (ignores filter) - 'filterCount' - Number of records matching the filter - ['totalCount', 'filterCount'] - Both counts

Ví dụ hoàn chỉnh

const result = await $ctx.$repos.products.find({
  filter: {
    _and: [
      { category: { _in: ['electronics', 'gadgets'] } },
      { price: { _between: [100, 500] } },
      { isActive: { _eq: true } },
      { description: { _is_not_null: true } }
    ]
  },
  fields: 'id,name,price,category.name',
  sort: '-price',
  limit: 20,
  meta: ['totalCount', 'filterCount']
});

const products = result.data;
const totalCount = result.meta.totalCount;
const filteredCount = result.meta.filterCount;

Tiếp theo