MongoDB에서 자동 증가 시퀀스를 만드는 방법


이번에는 MongoDB에서 자동 증가 시퀀스를 만드는 방법에 대해 설명하겠다.

MongoDB는 기본적으로 시퀀스가 존재하지 않기 때문에 따로 코드를 생성을 해야 한다. 여기에서는 간단한 예제로 두 가지 방법을 소개한다.

개요

기본적으로 MongoDB는 다수의 도큐먼트를 다루기 때문에 임의의 필드에 인크리먼트(increment)가 발생하지 않는다. 기본적으로 _idObjectId의 고유 키가 생성되므로 이를 사용한다.

그렇다고는 해도 시스템상 독특한 일련번호가 필요한 경우도 있다고 생각된다. 이번에는 독특한 번호를 발번하는 방법을 두 가지 소개한다.

어느 쪽의 방법에 대해서도 아래와 같이 도큐먼트에 userid에 시퀀스를 발행하는 방법을 예제 코드를 만들어 보기로 하겠다.

  • database : test
  • collection : users
  • document : {userid, name}

카운터 컬렉션을 사용하는 방법

{key: string, seq: number} 포맷으로 counters 컬렉션을 만들어 시퀀스가 필요한 도큐먼트를 등록할 때에 이 counters 도큐먼트로부터 최대치를 조회한 후에 갱신하는 것으로 자동 인크리먼트를 만드는 방법이다.

전제 counters 컬렉션을 생성한다. 다음 예제는 mongo 명령에서 실행한다.

> db.counters.createIndex({ key: 1 }, { unique: true })
> db.counters.insertOne({ key: "userid", seq: 0 })

작성 결과는 다음과 같다.

> db.counters.find({}, { _id: 0 })
{ "key" : "userid", "seq" : 0 }

소스 (index.js)

var MongoClient = require("mongodb").MongoClient;
 
const CONNECTION_STRING = "mongodb://localhost:27017/test";
 
// 시퀀스용 코렉션을 이용하여 시퀀스 발행
var insertUser = function (userinfo, callback) {
  MongoClient.connect(CONNECTION_STRING).then((db) => {

    // 카운트 콜렉션으로 부터 시퀀스 조회
    db.collection("counters").findOneAndUpdate(
      { key: "userid" },
      { $inc: { seq: 1 } },
      { upsert: true, returnOriginal: true },
      (err, doc) => {
        // 시퀀스 변호 설정
        userinfo.userid = doc.value.seq;
 
        // 도큐먼트 등록
        db.collection("users").insertOne(userinfo, (err, res) => {
          callback && callback(err, res);
        });
      });
  }).catch((err) => {
    console.log(err.message);
  });
};
 
// 실제 등록하기
insertUser({
  name: "kimkc"
});
 
insertUser({
  name: "devkuma"
});

실행

> node ./index.js

결과

> db.users.find({},{_id:0})
{ "name" : "kimkc", "userid" : 0 }
{ "name" : "devkuma", "userid" : 1 }

루프 처리를 이용하는 방법

시퀀스용 콜렉션을 생성하지 않고, 현재 등록된 데이터에서 최대값을 가져와 다음 번호를 가져온다.

소스 (index.js)

var MongoClient = require("mongodb").MongoClient;
 
const CONNECTION_STRING = "mongodb://localhost:27017/test";
 
// 반복 처리를 이용한 시퀀스 발행
var insertUser = function (userinfo, callback) {
  MongoClient.connect(CONNECTION_STRING).then((db) => {
 
    // 반복 처리
    var loop = function () {
      db.collection("users")
        .find({}, { _id: 0, userid: 1 })
        .sort({ userid: -1 })
        .limit(1)
        .toArray((err, docs) => {
          if (err) {
            callback && callback(err, null);
            return;
          }
 
          // 시퀀스 생성
          var seq = docs.length > 0 ? docs[0].userid + 1 : 0;
 
          // 시퀀스 설정
          userinfo.userid = seq;
 
          // 도큐먼트 등록
          db.collection("users").insertOne(userinfo, (err, res) => {
            // 시퀀스 번호가 사용되는 경우 재귀 실행
            if (err && err.code === 11000) {
              loop();
 
              return;
            }
            callback && callback(err, res);
          });
        });
    };
 
    // 반복 처리의 최초 실행
    loop();
  });
};
 
// 실제로 등록하기
insertUser({
  name: "kimkc"
});
 
insertUser({
  name: "devkuma"
});

사용

> node ./index.js

결과

> db.users.find({}, { _id: 0 })
{ "name" : "kimkc", "userid" : 0 }
{ "name" : "devkuma", "userid" : 1 }

참고 문서