limitκ³Ό offsetμ 쿼리 κ²°κ³Όμ κ°μλ₯Ό μ ννκ³ νμ΄μ§λ€μ΄μ
μ ꡬννλ λ©μλμ
λλ€.
limit
μ‘°νν λ μ½λ μλ₯Ό μ νν©λλ€.κΈ°λ³Έ μ¬μ©λ²
// μμ 10κ°λ§
const users = await puri
.table("users")
.select({ id: "users.id", name: "users.name" })
.orderBy("users.created_at", "desc")
.limit(10);
// LIMIT 10
μ΅μ Nκ°
// μ΅μ κ²μλ¬Ό 20κ°
const recentPosts = await puri
.table("posts")
.select({
id: "posts.id",
title: "posts.title",
created_at: "posts.created_at",
})
.orderBy("posts.created_at", "desc")
.limit(20);
offset
건λλΈ λ μ½λ μλ₯Ό μ§μ ν©λλ€.κΈ°λ³Έ μ¬μ©λ²
// μ²μ 10κ°λ₯Ό 건λλ°κ³ κ·Έ λ€μ 10κ°
const users = await puri
.table("users")
.select({ id: "users.id", name: "users.name" })
.orderBy("users.id", "asc")
.limit(10)
.offset(10); // 11~20λ²μ§Έ λ μ½λ
// LIMIT 10 OFFSET 10
νμ΄μ§λ€μ΄μ
limitκ³Ό offsetμ μ‘°ν©νμ¬ νμ΄μ§λ€μ΄μ
μ ꡬνν©λλ€.
κΈ°λ³Έ νμ΄μ§λ€μ΄μ
function getPage(page: number, pageSize: number = 20) {
return puri
.table("posts")
.select({
id: "posts.id",
title: "posts.title",
created_at: "posts.created_at",
})
.orderBy("posts.created_at", "desc")
.limit(pageSize)
.offset((page - 1) * pageSize);
}
// 1νμ΄μ§ (1~20)
const page1 = await getPage(1); // offset: 0, limit: 20
// 2νμ΄μ§ (21~40)
const page2 = await getPage(2); // offset: 20, limit: 20
// 3νμ΄μ§ (41~60)
const page3 = await getPage(3); // offset: 40, limit: 20
μ΄ κ°μμ ν¨κ»
async function getPageWithTotal(page: number, pageSize: number = 20) {
// μ΄ κ°μ
const [{ total }] = await puri
.table("posts")
.select({ total: Puri.count() })
.where("posts.published", true);
// νμ΄μ§ λ°μ΄ν°
const posts = await puri
.table("posts")
.select({
id: "posts.id",
title: "posts.title",
})
.where("posts.published", true)
.orderBy("posts.created_at", "desc")
.limit(pageSize)
.offset((page - 1) * pageSize);
return {
data: posts,
pagination: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize),
},
};
}
μ€μ μμ
- 무ν μ€ν¬λ‘€
- λ°μ΄ν° ν μ΄λΈ
- Top N 쿼리
- λ°°μΉ μ²λ¦¬
async function getInfiniteScroll(cursor: number = 0, limit: number = 20) {
const posts = await puri.table("posts")
.select({
id: "posts.id",
title: "posts.title",
created_at: "posts.created_at"
})
.where("posts.published", true)
.where("posts.id", ">", cursor) // 컀μ μ΄ν
.orderBy("posts.id", "asc")
.limit(limit);
return {
data: posts,
nextCursor: posts.length > 0 ? posts[posts.length - 1].id : null,
hasMore: posts.length === limit
};
}
// μ¬μ© μμ
const result1 = await getInfiniteScroll(0, 20); // 첫 νμ΄μ§
const result2 = await getInfiniteScroll(result1.nextCursor, 20); // λ€μ νμ΄μ§
interface TableParams {
page: number;
pageSize: number;
sortBy?: string;
sortOrder?: "asc" | "desc";
filters?: Record<string, any>;
}
async function getTableData(params: TableParams) {
const { page, pageSize, sortBy = "id", sortOrder = "desc", filters = {} } = params;
// 기본 쿼리
let query = puri.table("users")
.select({
id: "users.id",
name: "users.name",
email: "users.email",
status: "users.status",
created_at: "users.created_at"
});
// νν° μ μ©
if (filters.status) {
query = query.where("users.status", filters.status);
}
if (filters.search) {
query = query.where("users.name", "like", `%${filters.search}%`);
}
// μ΄ κ°μ
const countQuery = query.clone();
const [{ total }] = await countQuery
.clear("select")
.select({ total: Puri.count() });
// μ λ ¬ λ° νμ΄μ§λ€μ΄μ
const data = await query
.orderBy(`users.${sortBy}` as any, sortOrder)
.limit(pageSize)
.offset((page - 1) * pageSize);
return {
data,
pagination: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize)
}
};
}
// μμ 10κ° μν
async function getTopProducts(limit: number = 10) {
return puri.table("products")
.select({
id: "products.id",
name: "products.name",
sales: "products.sales",
rating: "products.rating"
})
.where("products.published", true)
.orderBy("products.sales", "desc")
.limit(limit);
}
// μ΅κ·Ό νλ μ¬μ©μ
async function getActiveUsers(days: number = 7, limit: number = 20) {
const since = new Date();
since.setDate(since.getDate() - days);
return puri.table("users")
.select({
id: "users.id",
name: "users.name",
last_login: "users.last_login"
})
.where("users.last_login", ">=", since)
.orderBy("users.last_login", "desc")
.limit(limit);
}
async function processBatch<T>(
query: Puri<any, any, T>,
batchSize: number = 100,
callback: (batch: T[]) => Promise<void>
) {
let offset = 0;
let hasMore = true;
while (hasMore) {
// λ°°μΉ μ‘°ν
const batch = await query
.clone()
.limit(batchSize)
.offset(offset);
if (batch.length === 0) {
hasMore = false;
break;
}
// λ°°μΉ μ²λ¦¬
await callback(batch);
offset += batchSize;
hasMore = batch.length === batchSize;
}
}
// μ¬μ© μμ
await processBatch(
puri.table("users").select({ id: "users.id", email: "users.email" }),
500,
async (users) => {
// 500κ°μ© μ²λ¦¬
await sendEmailBatch(users);
}
);
limit(0)μ μλ―Έ
limit(0)μ κ²°κ³Όλ₯Ό λ°ννμ§ μμ΅λλ€.
// 0κ° λ°ν (λΉ λ°°μ΄)
const users = await puri.table("users").limit(0);
// []
μΌλΆ λ°μ΄ν°λ² μ΄μ€μμ
limit(0)μ ꡬ쑰 νμΈμ©μΌλ‘ μ¬μ©λμ§λ§, Puriμμλ λΉ λ°°μ΄μ λ°νν©λλ€.μ±λ₯ μ΅μ ν
1. μΈλ±μ€ νμ©
// β
μ’μ: μ λ ¬ 컬λΌμ μΈλ±μ€
await puri
.table("posts")
.orderBy("posts.created_at", "desc") // created_at μΈλ±μ€ νμ
.limit(20);
// β λμ¨: μΈλ±μ€ μλ μ»¬λΌ μ λ ¬
await puri
.table("posts")
.orderBy("posts.content", "desc") // μΈλ±μ€ μμ
.limit(20);
2. Offset ν¬κΈ°
// β οΈ μ£Όμ: ν° offsetμ λλ¦Ό
await puri.table("posts").limit(20).offset(100000); // 10λ§ κ°λ₯Ό 건λλ°κ³ 20κ° μ‘°ν (λλ¦Ό)
// β
μ’μ: 컀μ κΈ°λ° νμ΄μ§λ€μ΄μ
await puri.table("posts").where("posts.id", ">", lastId).limit(20);
3. COUNT(*) μ΅μ ν
// β λμ¨: λ§€λ² μ 체 μΉ΄μ΄νΈ
const [{ total }] = await puri.table("posts").select({ total: Puri.count() });
// β
μ’μ: μΊμ±
const cachedTotal = await cache.get("posts:total");
if (!cachedTotal) {
const [{ total }] = await puri.table("posts").select({ total: Puri.count() });
await cache.set("posts:total", total, 300); // 5λΆ μΊμ
}
컀μ κΈ°λ° νμ΄μ§λ€μ΄μ
λμ©λ λ°μ΄ν°μλ offset λμ 컀μλ₯Ό μ¬μ©νμΈμ.Offset κΈ°λ° (λλ¦Ό)
// β ν° offsetμ μ±λ₯ μ ν
async function getPage(page: number, pageSize: number = 20) {
return puri
.table("posts")
.select({ id: "posts.id", title: "posts.title" })
.orderBy("posts.id", "desc")
.limit(pageSize)
.offset((page - 1) * pageSize); // νμ΄μ§κ° 컀μ§μλ‘ λλ €μ§
}
await getPage(1000, 20); // offset: 19980 (λ§€μ° λλ¦Ό)
컀μ κΈ°λ° (λΉ λ¦)
// β
컀μ μ¬μ© (νμ λΉ λ¦)
async function getNextPage(cursor: number | null, pageSize: number = 20) {
let query = puri
.table("posts")
.select({ id: "posts.id", title: "posts.title" })
.orderBy("posts.id", "desc")
.limit(pageSize);
if (cursor !== null) {
query = query.where("posts.id", "<", cursor);
}
const posts = await query;
return {
data: posts,
nextCursor: posts.length > 0 ? posts[posts.length - 1].id : null,
hasMore: posts.length === pageSize,
};
}
// νμ λΉ λ¦ (μΈλ±μ€ μ¬μ©)
const page1 = await getNextPage(null, 20);
const page2 = await getNextPage(page1.nextCursor, 20);
const page100 = await getNextPage(page99.nextCursor, 20); // μ¬μ ν λΉ λ¦
μ£Όμμ¬ν
1. ORDER BY νμ
// β μν: μ λ ¬ μμ΄ limit (μμ λΆνμ€)
await puri.table("users").limit(10);
// β
μ¬λ°λ¦: μ λ ¬ ν limit
await puri.table("users").orderBy("users.id", "asc").limit(10);
2. offsetμ 0λΆν°
// β
μ¬λ°λ¦
.offset(0) // 첫 λ²μ§Έ λ μ½λλΆν°
.offset(10) // 11λ²μ§Έ λ μ½λλΆν°
// β μλ¬: μμ λΆκ°
.offset(-1) // Error: Invalid offset: must be >= 0
3. limitλ 0 μ΄μ
// β
μ¬λ°λ¦
.limit(0) // λΉ λ°°μ΄
.limit(10) // 10κ°
// β μλ¬: μμ λΆκ°
.limit(-1) // Error: Invalid limit: must be >= 0
4. μΌκ΄λ μ λ ¬
// β οΈ μ£Όμ: μ λ ¬μ΄ μΌκ΄λμ§ μμΌλ©΄ μ€λ³΅/λλ½ κ°λ₯
await puri
.table("posts")
.orderBy("posts.created_at", "desc") // κ°μ μκ° μ¬λ¬ κ° μμ μ μμ
.limit(20)
.offset(20); // μ€λ³΅ λλ λλ½ κ°λ₯
// β
μ’μ: κ³ μ μ»¬λΌ μΆκ°
await puri
.table("posts")
.orderBy("posts.created_at", "desc")
.orderBy("posts.id", "desc") // λμΌ μκ°μ IDλ‘ κ΅¬λΆ
.limit(20)
.offset(20);
νμ μμ μ±
// β
μ¬λ°λ₯Έ μ¬μ©
await puri.table("users").limit(10).offset(0);
// β νμ
μλ¬: μμ
await puri.table("users").limit(-10); // Error: Invalid limit
// β νμ
μλ¬: μμ
await puri.table("users").offset(-5); // Error: Invalid offset
λ€μ λ¨κ³
order-by
κ²°κ³Ό μ λ ¬νκΈ°
where
쑰건 νν°λ§νκΈ°
advanced-methods
κ³ κΈ μΏΌλ¦¬ λ©μλ
Subset
μλΈμ
μΌλ‘ κ΄κ³ λ°μ΄ν° λ‘λ©