where๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํํฐ๋งํ๋ ์กฐ๊ฑด์ ์ง์ ํ๋ ๋ฉ์๋์
๋๋ค. ๋ค์ํ ์ฐ์ฐ์์ ์กฐ๊ฑด ์กฐํฉ์ ์ง์ํ๋ฉฐ, ํ์
์์ ํ๊ฒ ํํฐ๋งํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๋จ์ ๋ฑํธ ์กฐ๊ฑด
const users = await puri . table ( "users" )
. where ( "users.status" , "active" )
. select ({ id: "users.id" , name: "users.name" });
// WHERE users.status = 'active'
๋น๊ต ์ฐ์ฐ์
// ๋ณด๋ค ํฐ
await puri . table ( "users" )
. where ( "users.age" , ">" , 18 );
// ๋ณด๋ค ์๊ฑฐ๋ ๊ฐ์
await puri . table ( "orders" )
. where ( "orders.amount" , "<=" , 1000 );
// ๊ฐ์ง ์์
await puri . table ( "posts" )
. where ( "posts.status" , "!=" , "deleted" );
์ง์ ์ฐ์ฐ์:
= - ๊ฐ์ (๊ธฐ๋ณธ๊ฐ)
!= - ๊ฐ์ง ์์
> - ๋ณด๋ค ํผ
>= - ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์
< - ๋ณด๋ค ์์
<= - ๋ณด๋ค ์๊ฑฐ๋ ๊ฐ์
like - ํจํด ๋งค์นญ
not like - ํจํด ๋ถ์ผ์น
๊ฐ์ฒด ํํ ์กฐ๊ฑด
์ฌ๋ฌ AND ์กฐ๊ฑด์ ๊ฐ์ฒด๋ก ์ง์ ํ ์ ์์ต๋๋ค.
const users = await puri . table ( "users" )
. where ({
"users.status" : "active" ,
"users.role" : "admin" ,
"users.verified" : true
})
. select ({ id: "users.id" , name: "users.name" });
// WHERE users.status = 'active'
// AND users.role = 'admin'
// AND users.verified = true
NULL ์ฒ๋ฆฌ
IS NULL
// ๋ช
์์ null ์ฒดํฌ
await puri . table ( "users" )
. where ( "users.deleted_at" , null );
// WHERE users.deleted_at IS NULL
// ์ฐ์ฐ์ ์ฌ์ฉ
await puri . table ( "users" )
. where ( "users.deleted_at" , "=" , null );
// WHERE users.deleted_at IS NULL
IS NOT NULL
await puri . table ( "users" )
. where ( "users.email" , "!=" , null );
// WHERE users.email IS NOT NULL
ํจํด ๋งค์นญ (LIKE)
๋ถ๋ถ ์ผ์น
// ํฌํจ
await puri . table ( "users" )
. where ( "users.email" , "like" , "%@gmail.com" );
// ์์
await puri . table ( "users" )
. where ( "users.name" , "like" , "John%" );
// ๋
await puri . table ( "users" )
. where ( "users.phone" , "like" , "%1234" );
// ์ด๋๋ ํฌํจ
await puri . table ( "posts" )
. where ( "posts.title" , "like" , "%typescript%" );
NOT LIKE
await puri . table ( "users" )
. where ( "users.email" , "not like" , "%spam%" );
// WHERE users.email NOT LIKE '%spam%'
whereIn / whereNotIn
๊ฐ ๋ชฉ๋ก ์ค ํ๋
// IN
const userIds = [ 1 , 2 , 3 , 4 , 5 ];
await puri . table ( "users" )
. whereIn ( "users.id" , userIds );
// WHERE users.id IN (1, 2, 3, 4, 5)
// NOT IN
await puri . table ( "users" )
. whereNotIn ( "users.status" , [ "banned" , "suspended" ]);
// WHERE users.status NOT IN ('banned', 'suspended')
๋น ๋ฐฐ์ด ์ฒ๋ฆฌ
// ๋น ๋ฐฐ์ด์ ํญ์ false
await puri . table ( "users" )
. whereIn ( "users.id" , []);
// WHERE 1 = 0 (์๋ฌด๊ฒ๋ ๋ฐํํ์ง ์์)
๋ณต์กํ ์กฐ๊ฑด ๊ทธ๋ฃน
whereGroup (AND ๊ทธ๋ฃน)
๊ดํธ๋ก ๋ฌถ์ธ ์กฐ๊ฑด๋ค์ ๋ง๋ญ๋๋ค.
await puri . table ( "users" )
. where ( "users.role" , "admin" )
. whereGroup (( group ) => {
group . where ( "users.status" , "active" )
. orWhere ( "users.status" , "pending" );
});
// WHERE users.role = 'admin'
// AND (users.status = 'active' OR users.status = 'pending')
orWhereGroup (OR ๊ทธ๋ฃน)
await puri . table ( "products" )
. where ( "products.published" , true )
. orWhereGroup (( group ) => {
group . where ( "products.featured" , true )
. where ( "products.stock" , ">" , 0 );
});
// WHERE products.published = true
// OR (products.featured = true AND products.stock > 0)
์ค์ฒฉ ๊ทธ๋ฃน
await puri . table ( "orders" )
. whereGroup (( outer ) => {
outer . where ( "orders.status" , "completed" )
. whereGroup (( inner ) => {
inner . where ( "orders.amount" , ">" , 1000 )
. orWhere ( "orders.vip" , true );
});
});
// WHERE (
// orders.status = 'completed'
// AND (orders.amount > 1000 OR orders.vip = true)
// )
Full-Text Search
whereMatch (MySQL)
MySQL์ FULLTEXT ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
await puri . table ( "posts" )
. whereMatch ( "posts.content" , "typescript programming" );
// WHERE MATCH (posts.content) AGAINST ('typescript programming')
whereMatch๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ํด๋น ์ปฌ๋ผ์ FULLTEXT ์ธ๋ฑ์ค๊ฐ ์์ด์ผ ํฉ๋๋ค.
whereTsSearch (PostgreSQL)
PostgreSQL์ tsvector๋ฅผ ์ฌ์ฉํฉ๋๋ค.
await puri . table ( "posts" )
. whereTsSearch ( "posts.content_tsv" , "typescript programming" );
// WHERE posts.content_tsv @@ websearch_to_tsquery('simple', 'typescript programming')
์ต์
:
// ํ์ ์ง์
await puri . table ( "posts" )
. whereTsSearch ( "posts.content_tsv" , "typescript" , {
parser: "plainto_tsquery" , // ๋๋ "to_tsquery", "phraseto_tsquery"
config: "english" // ์ธ์ด ์ค์
});
// ๊ฐ๋จํ config๋ง ์ง์
await puri . table ( "posts" )
. whereTsSearch ( "posts.content_tsv" , "typescript" , "english" );
whereSearch (PGroonga)
PGroonga ํ์ฅ์ ์ฌ์ฉํ ์ ๋ฌธ ๊ฒ์์
๋๋ค.
// ๋จ์ผ ์ปฌ๋ผ ๊ฒ์
await puri . table ( "posts" )
. whereSearch ( "posts.title" , "ํ์
์คํฌ๋ฆฝํธ" );
// WHERE posts.title &@~ pgroonga_condition('ํ์
์คํฌ๋ฆฝํธ')
// ์ฌ๋ฌ ์ปฌ๋ผ ๊ฒ์ (๋ฐฐ์ด)
await puri . table ( "posts" )
. whereSearch (
[ "posts.title" , "posts.content" ],
"ํ์
์คํฌ๋ฆฝํธ"
);
// WHERE ARRAY[posts.title::text, posts.content::text]
// &@~ pgroonga_condition('ํ์
์คํฌ๋ฆฝํธ')
// ๊ฐ์ค์น ์ง์
await puri . table ( "posts" )
. whereSearch (
[ "posts.title" , "posts.content" ],
"ํ์
์คํฌ๋ฆฝํธ" ,
{ weights: [ 10 , 1 ] } // title์ด content๋ณด๋ค 10๋ฐฐ ์ค์
);
PGroonga ๊ฒ์์ ์ฌ์ฉํ ์ธ๋ฑ์ค์ ๋์ผํ ์ปฌ๋ผ ๊ตฌ์ฑ ์ผ๋ก ๊ฒ์ํด์ผ ์ธ๋ฑ์ค๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
Raw SQL ์กฐ๊ฑด
whereRaw
๋ณต์กํ SQL ์กฐ๊ฑด์ ์ง์ ์์ฑํ ์ ์์ต๋๋ค.
// ํ๋ผ๋ฏธํฐ ์์
await puri . table ( "users" )
. whereRaw ( "YEAR(users.created_at) = YEAR(CURRENT_DATE)" );
// ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
await puri . table ( "users" )
. whereRaw ( "users.age BETWEEN ? AND ?" , [ 18 , 65 ]);
// ์ฌ๋ฌ ์กฐ๊ฑด
await puri . table ( "orders" )
. whereRaw ( "DATE(orders.created_at) = ?" , [ "2024-01-01" ])
. whereRaw ( "orders.amount > ?" , [ 1000 ]);
SQL ์ธ์ ์
์ ๋ฐฉ์งํ๊ธฐ ์ํด ํญ์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ์ธ์.
์ค์ ์์
์ฌ์ฉ์ ํํฐ๋ง
์ฃผ๋ฌธ ๊ฒ์
๋ณต์กํ ์กฐ๊ฑด
์ ๋ฌธ ๊ฒ์
๋ ์ง ๋ฒ์
async function getActiveAdmins () {
return puri . table ( "users" )
. where ({
"users.role" : "admin" ,
"users.status" : "active" ,
"users.email_verified" : true
})
. where ( "users.last_login" , ">" , new Date ( Date . now () - 30 * 24 * 60 * 60 * 1000 ))
. select ({
id: "users.id" ,
email: "users.email" ,
name: "users.name" ,
last_login: "users.last_login"
})
. orderBy ( "users.last_login" , "desc" );
}
async function searchOrders ( filters : {
status ?: string ;
minAmount ?: number ;
userIds ?: number [];
dateFrom ?: Date ;
dateTo ?: Date ;
}) {
let query = puri . table ( "orders" ). selectAll ();
// ์ํ ํํฐ
if ( filters . status ) {
query = query . where ( "orders.status" , filters . status );
}
// ๊ธ์ก ๋ฒ์
if ( filters . minAmount ) {
query = query . where ( "orders.amount" , ">=" , filters . minAmount );
}
// ์ฌ์ฉ์ ๋ชฉ๋ก
if ( filters . userIds && filters . userIds . length > 0 ) {
query = query . whereIn ( "orders.user_id" , filters . userIds );
}
// ๋ ์ง ๋ฒ์
if ( filters . dateFrom ) {
query = query . where ( "orders.created_at" , ">=" , filters . dateFrom );
}
if ( filters . dateTo ) {
query = query . where ( "orders.created_at" , "<=" , filters . dateTo );
}
return query . orderBy ( "orders.created_at" , "desc" );
}
async function getEligibleProducts () {
return puri . table ( "products" )
// ๊ธฐ๋ณธ ์กฐ๊ฑด
. where ( "products.published" , true )
. where ( "products.deleted_at" , null )
// (์ฌ๊ณ ์์ OR ์ฌ์ ์ฃผ๋ฌธ ๊ฐ๋ฅ) AND ๊ฐ๊ฒฉ ์กฐ๊ฑด
. whereGroup (( group1 ) => {
group1 . whereGroup (( group2 ) => {
group2 . where ( "products.stock" , ">" , 0 )
. orWhere ( "products.preorder_available" , true );
}). whereGroup (( group3 ) => {
group3 . where ( "products.price" , ">=" , 1000 )
. where ( "products.price" , "<=" , 100000 );
});
})
// ์ ์ธ ์กฐ๊ฑด
. whereNotIn ( "products.category" , [ "adult" , "restricted" ])
. select ({
id: "products.id" ,
name: "products.name" ,
price: "products.price" ,
stock: "products.stock"
})
. orderBy ( "products.featured" , "desc" )
. orderBy ( "products.created_at" , "desc" );
}
async function searchPosts ( keyword : string ) {
return puri . table ( "posts" )
// PGroonga ์ ๋ฌธ ๊ฒ์
. whereSearch (
[ "posts.title" , "posts.content" ],
keyword ,
{ weights: [ 10 , 1 ] } // ์ ๋ชฉ ๊ฐ์ค์น ๋์
)
// ์ถ๊ฐ ํํฐ
. where ( "posts.published" , true )
. where ( "posts.deleted_at" , null )
. select ({
id: "posts.id" ,
// ํ์ด๋ผ์ดํ
๋ ์ ๋ชฉ/๋ด์ฉ
title: Puri . highlight ( "posts.title" , keyword ),
content: Puri . highlight ( "posts.content" , keyword ),
// ๊ฒ์ ์ ์
score: Puri . score (),
created_at: "posts.created_at"
})
// ์ ์ ์ ์ ๋ ฌ
. orderBy ( "score" , "desc" )
. limit ( 20 );
}
async function getRecentActivity ( days : number = 7 ) {
const startDate = new Date ();
startDate . setDate ( startDate . getDate () - days );
return puri . table ( "activities" )
// ๋ ์ง ๋ฒ์
. where ( "activities.created_at" , ">=" , startDate )
. where ( "activities.created_at" , "<=" , new Date ())
// ํ์ฑ ์ฌ์ฉ์๋ง
. whereGroup (( group ) => {
group . where ( "activities.user_status" , "active" )
. orWhere ( "activities.user_status" , "premium" );
})
// ํน์ ํ์
์ ์ธ
. whereNotIn ( "activities.type" , [ "system" , "automated" ])
. select ({
id: "activities.id" ,
type: "activities.type" ,
user_id: "activities.user_id" ,
created_at: "activities.created_at"
})
. orderBy ( "activities.created_at" , "desc" );
}
์กฐ๊ฑด ์ฒด์ด๋
๋ชจ๋ where ๋ฉ์๋๋ ์ฒด์ด๋ ๊ฐ๋ฅํ๋ฉฐ, ๊ธฐ๋ณธ์ ์ผ๋ก AND ๋ก ์ฐ๊ฒฐ๋ฉ๋๋ค.
const result = await puri . table ( "users" )
. where ( "users.role" , "admin" ) // AND
. where ( "users.status" , "active" ) // AND
. where ( "users.age" , ">" , 18 ) // AND
. whereIn ( "users.country" , [ "KR" , "US" ]) // AND
. where ( "users.verified" , true ); // AND
OR ์กฐ๊ฑด
WhereGroup ๋ด์์ orWhere
await puri . table ( "users" )
. whereGroup (( group ) => {
group . where ( "users.role" , "admin" )
. orWhere ( "users.role" , "moderator" )
. orWhere ( "users.role" , "editor" );
});
// WHERE (
// users.role = 'admin'
// OR users.role = 'moderator'
// OR users.role = 'editor'
// )
์ต์์ ๋ ๋ฒจ์๋ orWhere๊ฐ ์์ต๋๋ค. OR ์กฐ๊ฑด์ whereGroup ๋๋ orWhereGroup ๋ด์์ ์ฌ์ฉํ์ธ์.
ํ์
์์ ์ฑ
WHERE ์กฐ๊ฑด์ ํ์
์์ ํ๊ฒ ๊ฒ์ฆ๋ฉ๋๋ค.
// โ
์ฌ๋ฐ๋ฅธ ์ปฌ๋ผ
await puri . table ( "users" )
. where ( "users.email" , "like" , "%@gmail.com" );
// โ ํ์
์๋ฌ: ์กด์ฌํ์ง ์๋ ์ปฌ๋ผ
await puri . table ( "users" )
. where ( "users.unknown_field" , "value" );
// โ
์ฌ๋ฐ๋ฅธ ํ์
await puri . table ( "users" )
. where ( "users.age" , 25 ); // number
// โ ํ์
์๋ฌ: ์๋ชป๋ ํ์
await puri . table ( "users" )
. where ( "users.age" , "25" ); // string (age๋ number)
์ฑ๋ฅ ์ต์ ํ
1. ์ธ๋ฑ์ค ํ์ฉ
// โ
์ข์: ์ธ๋ฑ์ค ์ปฌ๋ผ ์ฌ์ฉ
await puri . table ( "users" )
. where ( "users.email" , "[email protected] " ); // email์ ์ธ๋ฑ์ค ํ์
// โ ๋์จ: ํจ์ ์ฌ์ฉ (์ธ๋ฑ์ค ๋ฌดํจํ)
await puri . table ( "users" )
. whereRaw ( "LOWER(users.email) = ?" , [ "[email protected] " ]);
2. LIKE ํจํด
// โ
์ข์: ์ ๊ณ ์ (์ธ๋ฑ์ค ์ฌ์ฉ ๊ฐ๋ฅ)
. where ( "users.name" , "like" , "John%" )
// โ ๋์จ: ์ ์์ผ๋์นด๋ (์ธ๋ฑ์ค ์ฌ์ฉ ๋ถ๊ฐ)
. where ( "users.name" , "like" , "%John" )
3. IN vs ์ฌ๋ฌ OR
// โ
์ข์: IN ์ฌ์ฉ
. whereIn ( "users.id" , [ 1 , 2 , 3 , 4 , 5 ])
// โ ๋์จ: ์ฌ๋ฌ OR (๋นํจ์จ์ )
. whereGroup (( g ) => {
g . where ( "users.id" , 1 )
. orWhere ( "users.id" , 2 )
. orWhere ( "users.id" , 3 )
. orWhere ( "users.id" , 4 )
. orWhere ( "users.id" , 5 )
})
์ฃผ์์ฌํญ
1. NULL ์ฒ๋ฆฌ
// โ
์ฌ๋ฐ๋ฆ: NULL ์ฒดํฌ
. where ( "users.deleted_at" , null ) // IS NULL
. where ( "users.deleted_at" , "=" , null ) // IS NULL
. where ( "users.deleted_at" , "!=" , null ) // IS NOT NULL
// โ ์๋ชป๋จ: ์ผ๋ฐ ๋น๊ต (๋์ํ์ง ์์)
. where ( "users.deleted_at" , "=" , undefined )
2. ๋น ๋ฐฐ์ด
// whereIn์ ๋น ๋ฐฐ์ด์ ์ ๋ฌํ๋ฉด ๊ฒฐ๊ณผ ์์
const ids = [];
await puri . table ( "users" )
. whereIn ( "users.id" , ids ); // ์๋ฌด๊ฒ๋ ๋ฐํํ์ง ์์
// ์กฐ๊ฑด๋ถ๋ก ์ฒ๋ฆฌ
if ( ids . length > 0 ) {
query = query . whereIn ( "users.id" , ids );
}
3. Raw SQL ์ฃผ์
// โ ์ํ: SQL ์ธ์ ์
๊ฐ๋ฅ
const userInput = "'; DROP TABLE users; --" ;
. whereRaw ( `users.name = ' ${ userInput } '` )
// โ
์์ : ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
. whereRaw ( "users.name = ?" , [ userInput ])
4. ์กฐ๊ฑด ์์
// WHERE ์์๋ ์ฑ๋ฅ์ ์ํฅ์ ์ค ์ ์์
// โ
์ข์: ์ ํ๋ ๋์ ์กฐ๊ฑด ๋จผ์
. where ( "users.id" , 123 ) // ๋งค์ฐ ์ ํ์
. where ( "users.status" , "active" ) // ๋ ์ ํ์
// โ ๋นํจ์จ: ์ ํ๋ ๋ฎ์ ์กฐ๊ฑด ๋จผ์
. where ( "users.status" , "active" ) // ๋ง์ ํ
. where ( "users.id" , 123 ) // ์์์ ํ
๋ค์ ๋จ๊ณ