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