query
질의를 위해서는 먼저 BaseModel.query를 이용해 질의 객체를 만드세요. 그리고 메소드 연쇄를 통해 질의를 만들어 나간 후, Query#exec나 Query#count, Query#update, Query#delete를 사용해 질의를 실행합니다.
단순한 질의를 쉽게 실행하기 위해서 BaseModel 클래스는 Query에서 일부 메소드를 빌려와 가지고 있습니다.
const users = await User.query().where(\{ age: 27 }).exec();
// 또는
const users = await User.where(\{ age: 27 }).exec();
필터
Query#where나 Query#find를 사용해 레코드를 선택할 수 있습니다.
where
의 조건문은 MongoDB의 문법과 비슷합니다.
where
를 두개 이상 사용할 경우 논리곱(모두 참)을 의미합니다.
설명 | CORMO | SQL | MongoDB |
---|---|---|---|
같음 | User.where({ age: 27 }) | SELECT * FROM users WHERE age=27 | db.users.find({ age: 27 }) |
논리곱(모두 참) | { name: 'John Doe', age: 27 } | name='John Doe' AND age=27 | { name: 'John Doe', age: 27 } |
.where({name: 'John Doe'}).where({age: 27}) | |||
{ $and: [ { name: 'John Doe' }, { age: 27 } ] } | |||
[ { name: 'John Doe' }, { age: 27 } ] | |||
논리합(하나 이상 참) | { $or: [ { name: 'John Doe' }, { age: 27 } ] } | name='John Doe' OR age=27 | { $or: [ { name: 'John Doe' }, { age: 27 } ] } |
비교 ($lt, $gt, $lte, $gte) | [ { age: { $gt: 30 } }, { age: { $lte: 45 } } ] | age>30 AND age<=45 | { $and: [ { age: { $gt: 30 } }, { age: { $lte: 45 } } ] } |
대소문자 구분 없이 텍스트를 포함 | { name: { $contains: 'smi' } } | name LIKE '%smi%' | { name: /smi/i } |
{ name: { $contains: ['smi', 'doe'] } } | name LIKE '%smi%' OR name LIKE '%doe%' | { name: { $in: [/smi/i, /doe/i] } } | |
대소문자 구분 없이 텍스트로 시작 | { name: { $startswith: 'smi' } } | name LIKE 'smi%' | { name: /^smi/i } |
대소문자 구분 없이 텍스트로 끝남 | { name: { $endswith: 'smi' } } | name LIKE '%smi' | { name: /smi$/i } |
정규표현식 | { name: /smi/ } | name REGEXP 'smi' | { name: /smi/i } |
{ name: /smi|doe/ } | name REGEXP 'smi|doe' | { name: /smi|doe/i } | |
배열의 값 중 하나라도 일치 | { age: { $in: [ 10, 20, 30 ] } } | age IN (10,20,30) | { age: { $in: [ 10, 20, 30 ] } } |
{ age: [ 10, 20, 30 ] } | |||
부정 | { age: { $not: 27 } } | NOT (age=27) OR age IS NULL | { age: { $ne: 27 } } |
{ age: { $not: { $lt: 27 } } } | NOT (age<27) OR age IS NULL | { age: { $not: { $lt: 27 } } } | |
{ name: { $not: { $contains: 'smi' } } } | NOT (name LIKE '%smi%') OR name IS NULL | { name: { $not: /smi/i } } | |
{ name: { $not: { $contains: ['smi', 'doe'] } } } | NOT (name LIKE '%smi%' OR name LIKE '%doe%') OR name IS NULL | { name: { $nin: [/smi/i, /doe/i] } } | |
{ age: { $not: { $in: [ 10, 20, 30 ] } } } | NOT (age IN (10,20,30)) OR age IS NULL | { age: { $nin: [10,20,30] } } | |
{ age: { $not: [ 10, 20, 30 ] } } | |||
{ name: { $not: null } } | NOT name IS NULL | { age: { $ne: null } } |
식별자에 기반해 레코드를 찾으려면 하나의 ID나 ID의 배열을 인자로 하는 Query#find를 사용합니다.
논리적으로는 .where(\{ id: <주어진 ID나 ID의 배열> })
과 동일하지만, 해당 레코드가 없을 경우 find
는 예외를 발생시키는데 반해, where
는 빈 결과를 반환합니다.
조건부 활성화
하나의 질의 연쇄에서 다른 조건을 적용하고 싶은 경우, Query#if와 Query#endif를 사용할 수 있습니다. 이를 이용해 질의 문장을 단순화할 수 있습니다.
async function getOldUsers(options: \{ limit?: number; columns?: string[] }) \{
const query = User.query();
query.where(\{ age: \{ $gt: 30 } });
if (options.limit) \{
query.limit(options.limit);
}
if (options.columns) \{
query.select(options.columns as any);
}
return await query.exec();
}
// 위 코드를 다음과 같이 작성할 수 있습니다.
async function getOldUsers(options: \{ limit?: number; columns?: string[] }) \{
return await User.query()
.where(\{ age: \{ $gt: 30 } })
.if(options.limit != null)
.limit(options.limit)
.endif()
.if(options.columns != null)
.select(options.columns as any)
.endif()
.exec();
}
레코드 가져오기
Query#exec는 레코드를 가져옵니다.
보통은 Model 인스턴스의 배열을 반환합니다. 하지만 하나의 ID를 가지고 Query#find를 호출한 경우에는 하나의 Model 인스턴스를 반환합니다.
const user = await User.find(1).exec();
const users = await User.find([1, 2, 3]).exec();
Query는 내부적으로 exec
를 호출하는 then
메소드를 가지고 있습니다(즉 thenable). 따라서 exec
호출을 생략하고 단순히 await
만 붙여줘도 됩니다.
const users = await User.where(\{ age: 30 });
찾지 못하는 ID가 있는 경우 Query#find는 에러를 던집니다.
find
는 주어진 순서를 보정하지 않습니다. 순서를 보장하고 싶은 경우 대신 Query#findPreserve를 사용하십시오.
const users = await User.findPreserve([2, 1, 2, 3]).exec();
// users[0].id는 2, users[1].id는 1, users[2].id는 2, users[3].id는 3입니다.
가져오기시 몇가지 옵션을 줄 수 있습니다.
설명 | CORMO | SQL | MongoDB |
---|---|---|---|
컬럼 선택 | User.select(['id', 'name', 'age']) | SELECT id,name,age FROM users | db.users.find({}, { name: 1, age: 1 }) |
정렬 | User.order('age -name') | SELECT * FROM users ORDER BY age ASC, name DESC | db.users.find().sort({ age: 1, name: -1 }) |
제한 | User.query().limit(3) | SELECT * FROM users LIMIT 3 | db.users.find().limit(3) |
건너뛰기 | User.query().skip(3) | SELECT * FROM users LIMIT 2147483647 OFFSET 3 | db.users.find().skip(3) |
하나의 레코드만 요청하기
하나의 결과만 있는 것을 알고 있다면 (예를 들어 유니크 컬럼으로 질의), Query#one가 유용할 겁니다. 인스턴스 배열을 반환하는 대신 하나의 인스턴스 (또는 null)을 반환하도록 만듭니다.
const user = await User.where(\{ age: 27 }).one();
하나의 컬럼만 선택하기
하나의 컬럼에만 관심이 있다면 Query#selectSingle를 사용할 수 있습니다. 그러면 질의 객체는 Model 인스턴스 대신 하나의 값이나 값의 배열을 반환합니다.
const user_ids = await User.where(\{ age: 27 }).selectSingle('id');
const user_name = await User.find(1).selectSingle('name');
결과를 스트림 시키기
결과가 아주 많은 레코드를 가지고 있는 경우, 메모리 사용량을 줄이기 위해 Node.js의 stream API를 사용할 수 있습니다.
let count = 0;
await new Promise((resolve, reject) => \{
const stream = User.where(\{ age: 27 }).stream();
stream.on('data', function (user) \{
count++;
});
stream.on('end', function () \{
resolve();
});
});
레코드 수 새기
Query#count는 레코드의 수를 반환합니다.
CORMO | SQL | MongoDB |
---|---|---|
User.count() | SELECT COUNT(*) FROM users | db.users.count() |
User.count({age: 27}) | SELECT COUNT(*) FROM users WHERE age=27 | db.users.find({age: 27}).count() |
User.where({age: 27}).count() |
레코드 갱신하기
레코드를 갱신하기 위해 BaseModel#save와 Query#update를 제공하고 있습니다.
BaseModel#save는 가져온 하나의 레코드를 갱신하기 위해 사용합니다.
const user = await User.find(1);
user.age = 30;
await user.save();
한편, Query#update는 필터된 레코드를 갱신합니다.
CORMO | SQL | MongoDB |
---|---|---|
User.update({ age: 10 }, { age: 27 }) | UPDATE users SET age=10 WHERE age=27 | db.users.update({age: 27}, {$set: {age: 10}}, {multi: true}) |
User.where({ age: 27 }).update({ age:10 }) | ||
User.find(1).update({ age: 10 }) | UPDATE users SET age=10 WHERE id=1 | db.users.update({_id: 1}, {$set: {age: 10}}, {multi: true}) |
User.find(2).update({ age: { $inc: 3 } }) | UPDATE users SET age=age+3 WHERE id=2 | db.users.update({_id: 2}, {$inc: {age: 3}}, {multi: true}) |
Query#update는 갱신 명량만 데이터베이스 시스템에 전송하기 때문에 보통 더 빠릅니다. 대신 수정된 객체를 얻을 수 없고, 갱신 콜백도 호출되지 않습니다.
필요에 따라서 적절한 것을 사용하세요.
In CORMO, Active Record 패턴(즉 BaseModel#save
)은 운영환경에서 잘 검증되지 않았습니다. 그러므로 주의해서 사용해주세요.
레코드 삭제하기
Query#delete나 BaseModel#destroy는 레코드를 삭제합니다.
BaseModel#destroy는 BaseModel#save
와 비슷하게 ID를 통해 하나의 레코드를 삭제합니다. 그리고 Query#delete 는 Query#update
와 비슷하게 필터된 레코드를 삭제합니다.
CORMO | SQL | MongoDB |
---|---|---|
User.delete({age: 27}) | DELETE FROM users WHERE age=27 | db.users.remove({age: 27}) |
User.where({age: 27}).delete() | ||
User.delete() | DELETE FROM users | db.users.remove() |