mongoose 공부하기 [6] - Queries 파헤치기

김진성·2021년 10월 19일
2

MongoDB

목록 보기
6/7

지금까지 Document들에 대해서 공부를 했는데 이제는 Document를 찾는 방법인 Query를 다루는 방법에 대해서 공부를 하려고 한다.

Queries

Executing (실행)

우리가 Callback 함수가 있는 쿼리를 실행할 때 JSON document 처럼 쿼리문을 구체적으로 작성하면 된다.

const Person = mongoose.model('Person', yourSchema);

Person.findOne({'name.last': 'Ghost}, 'name occupation', function (err, person) {
	if (err) return handleError(err);
    console.log('$s $s is a %s.', person.name.first, person.name.last, person.occupation);
});

Mongoose는 쿼리를 실행 한 후 결과를 callback으로 전달을 하는데 Mongoose에서는 주로 callback(error, result) 라는 형식을 사용한다.

  • 오류 발생 시 : error에 오류 내용 전달/result는 null 형식
  • 성공 시 : error는 null 형식/result는 쿼리를 실행한 결과를 가져옴

예시) callback이 전달되지 않았을 때

const query = Person.findOne({'name.last': 'Ghost'});

query.select('name occupation');

query.exec(function (err, person) {
	if (err) return handleError(err);
    console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation);
});

예시) JSON doc과 query builder의 구조

// JSON doc일 경우
Person.
  find({
    occupation: /host/,
    'name.last': 'Ghost',
    age: { $gt: 17, $lt: 66 },
    likes: { $in: ['vaporizing', 'talking'] }
  }).
  limit(10).
  sort({ occupation: -1 }).
  select({ name: 1, occupation: 1 }).
  exec(callback);

// query builder일 경우
Person.
  find({ occupation: /host/ }).
  where('name.last').equals('Ghost').
  where('age').gt(17).lt(66).
  where('likes').in(['vaporizing', 'talking']).
  limit(10).
  sort('-occupation').
  select('name occupation').
  exec(callback);

Queries != Promises

mongoose 쿼리는 promises가 아니다. 쿼리들은 .then 함수, async/await 함수들을 가지고 있지만 promises 함수와 다르게 .then 함수를 여러번 실행할 수 있다.

const q = MyModel.updateMany({}, {idDeleted: true}, function {
	console.log('Update 1');
});

q.then(() => console.log('Update 2'));
q.then(() => console.log('Update 3'));

쿼리에서 callback 및 promise를 혼합해서 사용하면 안된다. 왜냐하면 callback을 넘기고 .then() 이 또다시 실행될 수 있기에 중복해서 작동이 된다. 아래 예시는 await와 callback 함수를 동시에 실행한 경우로 중복해서 작동이 된다.

const BlogPost = mongoose.model('BlogPose', new Schema({
	title: String,
    tags: [String]
}));

const update = {$push: {tags: ['javascript']}};
await Blogpost.updateOne({title: 'Introduction to Promises'}, update, (err, res) => {
	console.log(res);
});

Streaming

쿼리 실행 결과를 스트림하게 가져올 때에는 Query#cursor() 함수를 사용해 QueryCursor의 인스턴스를 가져올 필요가 있다.

const cursor = Person.find({occupation: /host/}).cursor();

for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
	console.log(doc);
}

or

for await (const doc of Person.find()) {
	console.log(doc);
}

Cursor는 시간이 지나면 자동적으로 실행이 꺼지게 된다. 그래서 noCursorTimeout 옵션을 사용해 시간이 지나서도 계속 실행시킬 수 있다.

const cursor = Person.find().cursor().addCursorFlag('noCursorTimeout', true);

vs Aggregation

Aggregation은 쿼리와 똑같은 역할을 할 수 있다. 예를 들어, 아래처럼 aggregate()를 사용해 docs를 찾을 수 있다.

const docs = await Person.aggregate([{$match: {'name.last': 'Ghost'}}]);

그러나, aggregate를 사용할 수 있다는 것이 무조건 사용하라는 것은 아니다. 일반적으로, 가능하다면 쿼리문을 쓰는게 낫고 필요한 경우에만 aggregate를 사용하는 것이다.

쿼리문 결과와는 다르게, Aggregation은 POJOs의 형태로 결과가 나오고 Mongoose는 hydrate() 한 결과를 나타내지 못하고 있다.

const docs = await Person.aggregate([{$match: {'name.last': 'Ghost'}}]);

docs[0] instanceof mongoose.Document; // false

또한, 쿼리문 필터와 다르게 올바른 타입으로 값들이 전달하는 파이프라인을 Mongoose에서 지원해주지 않는다. 아래 예시처럼 쿼리문은 Person이라는 객체를 찾을 수 있지만 Aggregate는 파이프라인 지원이 없어서 Person을 찾을 수가 없다.

const doc = await Person.findOne();
const idString = doc._id.toString();
const queryRes = await Person.findOne({_id: idString});
const aggRes = await Person.aggregate([{$match: {_id: idString}}])
profile
https://medium.com/@jinsung1048 미디엄으로 이전하였습니다.

0개의 댓글