DB - MongoDB FindAndModify 란?

2019. 9. 19. 13:41Database/MongoDB

 

MongoDB에서는 여러 명령을 하나의 트랜잭션으로 묶어서 사용할 수 없다. 그 이유는 몽고디비는 단일 문서 단위의 트랜잭션만 지원되기 때문이다. 이때문에 변경 직전이나 직후의 문서 데이터를 확인하기란 쉽지 않다. 사실 일반적으로 응용 프로그램에서 변경 직후의 데이터는 자신이 직접 변경한 데이터이므로 크게 필요없을 수 있지만, 변경 직전의 데이터를 확인하는 기능은 필요할 수 있다. 이러한 기능을 제공하기 위해서 몽고디비는 FindAndModify라는 명령을 제공한다. 해당 명령은 검색 조건에 일치하는 문서를 검색하고, 그 문서를 변경하거나 삭제하는 후속 오퍼레이션을 설정할 수 있다.

 

> db.collection.findAndModify({

        query:<document>,

        sort:<document>,

        remove:<boolean>,

        update:<document>,

        new:<boolean>,

        fields:<document>,

        upsert:<boolean>,

        bypassDocumentValidation:<boolean>,

        writeConcern:<document>,

        collation:<document>

});

 

위는 findAndModify 사용법이다. 주의해야할 점은 FindAndModify 명령의 조건에 일치하는 문서는 여러개 일 수 있다. 하지만 해당 명령은 반드시 하나의 문서만 변경 혹은 삭제하고, 변경된 도큐먼트를 반환한다. 만약 해당 명령의 조건에 일치하는 도큐먼트 중에서 특정 도큐먼트를 변경하거나 삭제하고 싶다면 sort옵션을 사용하면 된다.

 

옵션 설명
query 변경하고자 하는 도큐먼트를 검색할 조건을 명시한다. 주어진 조건에 일치하는 도큐먼트가 여러 개라 하더라도 그 중에서 첫 번째로 검색된 문서에 대해서만 변경 또는 삭제 작업을 수행한다.
sort 검색된 문서가 여러 개일 때, 실제 몽고디비서버가 어떤 문서를 변경했는지 명확히 판단이 힘들다. 그래서 검색 조건에 일치하는 문서가 여러 개일 것이라고 예상될 때, sort 옵션을 이용해서 변경 또는 삭제할 문서를 조정할 수 있다.
remove 검색된 문서 결과를 삭제한다. 기본 값은 false이며, 이 값을 true로 설정하면 몽고디비 서버는 검색 결과를 삭제한다. 만약 upsert 옵션을 설정하면 해당 옵션은 설정이 되지 않거나 false로 설정해야 한다. 하나의 findAndModify 명령으로 update,delete를 동시에 할 수 없다.
update 검색된 문서를 어떻게 변경할 지 설정한다. update 쿼리의 두번째 인자처럼 사용하면 된다.
new findAndModify 명령은 검색된 문서를 변경하거나 삭제하고, 변경하거나 삭제된 문서를 반환한다. 이때 삭제 또는 변경되기 직전의 문서를 반환할지 아니면 변경 또는 삭제된 이후의 문서를 반환할 것인지 new 옵션으로 결정한다. 기본값은 false이며, true로 설정하면 변경 직후의 문서가 반환된다.
fields 결과로 반환되는 문서의 Projection을 설정한다. 즉, 결과로 반환될 문서의 노출 필드를 지정하는 것이다.
upsert 검색결과가 있으면 update, 없다면 insert를 수행한다.
bypassDocumentValidation 몽고디비는 삽입,수정 시점에 필드에 대한 유효성 체크 지정이 가능한데, 해당 옵션을 통해 문서 유효성 검사를 건너 뛰게 설정한다.
writeConcern 변경 또는 삭제 작업의 writeConcern을 조정한다.
maxTimeMS findAndModify 명령이 실행될 최대 시간을 밀리초 단위로 설정한다.
collation 문서 검색시 사용할 콜레이션을 지정한다.

 

upsert와 new 옵션 값에 대한 결과값이다.

 

  new = false new = true
upsert = false 변경되기 전 문서 반환, 검색 결과가 없으면 Null 검색된 결과가 있으면 변경된 직후의 문서 반환, 검색 결과가 없으면 Null
upsert = true 변경되기 전 문서 반환, 검색 결과가 없으면 Null 검색된 결과가 있으면 변경된 직후의 문서 반환, 검색된 결과가 없으면 Insert된 문서 반환

 

upsert 옵션이 true인 findAndModify 명령과 update 명령과 아주 유사해보인다. 이 둘의 차이점은 아래와 같다.

 

  • 디폴트 옵션에서는 두 명령보두 하나의 문서만 변경할 수 있다. 하지만 update 명령은 multi 옵션을 true로 설정해서 여러 문서를 한번에 변경할 수 있다.
  • update 명령은 실제 어떤 문서를 변경할지 알 수없다.(매칭된 문서가 여러개일때) 하지만 findAndModify는 sort 옵션을 통해 특정 문서를 정렬해 첫 번째 문서만 변경할 수 있다.
  • update명령은 처리 결과를 반환하지만, findAndModify는 변경전 혹은 직후의 문서를 결과로 반환한다.

 

> db.users.insertMany([{name:"abc",age:22},{name:"yeoseong",age:28},{name:"sora",age:30},{name:"mija",age:50}])

{

"acknowledged" : true,

"insertedIds" : [

ObjectId("5d83050d04a68b589a9c1aa5"),

ObjectId("5d83050d04a68b589a9c1aa6"),

ObjectId("5d83050d04a68b589a9c1aa7"),

ObjectId("5d83050d04a68b589a9c1aa8")

]

}

> db.collection.findAndModify({

... query:{name:"abc"},

... sort:{name:1},

... remove:false,

... update:{$set:{name:"cba"}},

... new:true,

... fields:{_id:0,name:1,age:1},

... upsert:false,

... bypassDocumentValidation:false})

 

위와 같이 문서를 삽입하고 findAndModify 명령을 수행했다. 결과 값으로는 아래와 같다.

 

 

new를 true로 적용했고, 프로젝션은 이름과 나이로 적용했으므로 변경된 직후의 문서가 결과로 반환된다. 반대로 적용해보자.

 

> db.collection.findAndModify({

... query:{name:"cba"},

... sort:{name:1},

... remove:false,

... update:{$set:{name:"abc"}},

... new:false,

... fields:{_id:0,name:1,age:1},

... upsert:false,

... bypassDocumentValidation:false})

 

 

 

결과로 변경되기 전의 문서가 반환되었다. 간단한 예제이지만, 다양하게 옵션을 적용해보며 사용해보면 추후에 유용하게 사용될 명령일 듯하다. 그리고 잘만 사용한다면 insert,update,delete를 해당 명령으로만 사용가능하지 않을까? 물론 문서하나씩만 수행되는 명령이지만..또한 성능이 어쩔지는 모르지만..