MongoDB - Collation - 콜레이션 - 정렬 - locale

Share

Last Updated on 2월 5, 2023 by Jade(정현호)

안녕하세요   
이번 글에서는 MongoDB의 Collation(콜레이션) 에 대해서 내용을 확인 해보려고 합니다.     

Collation(콜레이션)

Collation(콜레이션) 이란, 문자열을 비교하고 정렬하는 방법을 정의하는 규칙 세트(set of rule)입니다. 데이터 정렬(collation,콜레이션)은 하나의 문자 집합에 속합니다.

모든 문자 집합(chracter set)에는 적어도 하나 이상의 콜레이션이 있으며 대부분은 두 개 이상의 데이터 정렬이 있습니다.

서로 다른 가중치를 가지는 문자들을 비교하여 문자가 가지는 크고 작음에 따라서 출력시 순서를 결정하게 됩니다.

콜레이션에 따라서 동일한 문자에 대해서 다른 가중치를 부여할 수 있으며, 그래서 콜레이션에 따라서 대소문자를 구분하는 경우도 있지만, 동일한 가중치로 비교해서 대소문자를 구분하지 않은(같은 가중치) 콜레이션도 있습니다.


Character Set(또는 charset) 은 정보를 표현하기 위한 글자나 기호들의 집합을 정의한 것입니다.

이러한 문자나 기호의 집합을 컴퓨터에 저장할 때에 정해진 규칙에 따라서 문자 인코딩(부호화) 하고, 인코딩 된 문자 부호를 다시 디코딩(복호화) 하여 본래의 문자와 기호를 표현 할 수 있습니다.

인코딩 종류에 따라서 다룰 수 있는 문자의 종류가 차이가 있을 수 있으며, 저장하는 byte 역시도 차이가 나게 됩니다.

국가별로 언어가 다르듯이 각 언어를 하나의 규칙으로 표준화시켜 국가 또는 개인의 요구에 따라 사용할 수 있도록 문자 집합을 만들게 됩니다.

이러한 문자 집합(Character Set)은 표현해야 할 문자와 특수문자를 정의하고 순서를 지정해 놓은 것을 의미합니다.

이러한 문자 집합은 다양하게 있으며, 요즘 대표적으로 많이 사용하는 문자 집합으로는 전 세계의 모든 문자를 동일하게 표현하기 위한 국제 표준 코드로 유니코드를 많이 사용 합니다.

유니코드 인코딩(encoding) 하는 방식에 따라서 utf-8 , utf-16 등이 있으며, utf-8(Unicode Transformation Format - 8bit)은 가변 인코딩 방식으로 8bit(1byte)를 기준으로 인코딩을 합니다.

MongoDB 는 문자열 저장시 사용 되는 문자 집합으로 utf-8 을 사용 하고 있으며 다른 Character Set은 지원하지 않습니다.
                     

MongoDB의 Collation

MongoDB는 기본적으로 쿼리의 검색시, 그리고 데이터베이스나 콜레이션, 필드명 등에서도 대소문자를 구분하게 됩니다.

간단하게 확인해보기 위해서 아래와 같이 데이터를 먼저 입력해 보도록 하겠습니다.

db.char_test.insertMany( [
    {col:"A1"},{col:"Á1"},{col:"á1"},
    {col:"C1"},{col:"Ć1"},{col:"ć1"},
    {col:"a1"},{col:"B1"},{col:"b1"}
])


그리고 sort 를 사용하여 정렬하여 결과를 출력 해보겠습니다.

> db.char_test.find({},{_id:0}).sort({col:1})
{ "col" : "A1" }
{ "col" : "B1" }
{ "col" : "C1" }
{ "col" : "a1" }
{ "col" : "b1" }
{ "col" : "Á1" }
{ "col" : "á1" }
{ "col" : "Ć1" }
{ "col" : "ć1" }


조회 결과와 같이 대소문자 구분 및 악센트 문자의 대소문자를 구분을 하게 됩니다.

이러한 대소문자 구분은 정렬의 기준에도 사용 되지만, 조회시에도 대소문자를 구분해서 검색을 하게 됩니다.

MongoDB 3.4 이전 버전까지는 Collation 기능이 지원되지 않았기 때문에 대소문자 구분 및 비교하여 사용 하였으며, Collation 기능이 추가된 3.4 버전에서도 별도의 Collation 을 지정하지 않으면 이전과 동일하게 문자를 encoding 된 바이너리 스트림 값으로 문자를 비교 하게 됩니다.

즉 a 와 A 는 코드가 다르기 때문에 다른 문자로 인식 되며, 즉 대소문자를 구분하게 됩니다.

MongoDB 3.4 버전 부터 Collation 기능이 추가 되었으며, 조회시에 Collation 을 지정하여 조회하거나, 컬렉션이나 뷰, 인덱스 또는 데이터 정렬을 지원하는 특정 작업에 대한 데이터 정렬(Collation)을 지정할 수 있습니다.


아래는 collation document 필드 종류 내역 입니다.

{
   locale: <string>,
   caseLevel: <boolean>,
   caseFirst: <string>,
   strength: <int>,
   numericOrdering: <boolean>,
   alternate: <string>,
   maxVariable: <string>,
   backwards: <boolean>,
   normalization : <boolean>
}


locale: <string>
데이터 정렬을 지정할 때 locale 필드는 필수입니다. 다른 모든 데이터 정렬 필드는 선택 사항입니다.
기본 collation 파라미터 값은 지정하는 로케일에 따라 다릅니다.

기본 collation 파라미터 및 연관된 로케일의 전체 목록은 Collation Default Parameters(데이터 정렬 기본 파라미터) 를 참조하십시오.

단순히 binary 비교를 지정하려면 "simple" 값을 지정합니다.
MongoDB는 기본적으로 binary collation 을 사용하여 문자열을 정렬합니다

지원되는 ICU 로케일 목록은 Supported Languages and Locales 을 참조하시면 됩니다.



caseLevel: <boolean>
대소문자와 발음 기호(악센트 문자 등)의 비교를 포함할 것인지 추가로 설정하는 필드인데, 이 옵션은 strength 가 1 또는 2일 때에만 적용할 수 있다.

caseLevel true 인 경우 대소문자 비교를 하게 되고 true 을 설정 할 경우, strength 의 값이 1 또는 2에 따라서 대소문자 비교 및 다른 비교등을 포함할지 여부를 결정하는 플래그입니다.

strength:1과 함께 사용하면 데이터 정렬에서 기본 문자와 대소문자를 비교합니다.
strength:2와 함께 사용할 경우 데이터 정렬은 기본 문자, 발음 구별 기호(또는 가능한 기타 보조 차이점) 및 대소문자를 비교합니다.

false인 경우 수준 1 또는 2에서 대/소문자 비교를 포함하지 않습니다. 기본값은 false입니다.


caseFirst: <string>
정렬시 대문자와 소문자중에서 어떤 문자를 먼저 앞으로 출력(정렬) 할 지를 결정하는 옵션으로 선택 가능한 옵션은 upper, lower , off 세가지가 있습니다.

upper 는 대문자를 먼저 출력, lower 는 소문자를 먼저 출력, off 는 기본값으로 lower 와 흡사(비슷하게) 동작합니다.


strength: <int>
문자열을 비교를 어떠한 강도로 비교 할지를 결정하는 필드 항목으로 1~5 를 명시적으로 설정 합니다. strength 수치가 높을 수록 비교하는 규칙의 수준이 높아지게 됩니다.

1: 데이터 정렬은 기본 문자만 비교하고 분음 부호(diacritic) 및 대소문자와 같은 다른 차이점은 무시합니다. 즉 같은 문자로 비교 수행 합니다.

2: 데이터 정렬은 분음 부호(diacritic)와 같은 2차 차이까지 비교를 수행합니다. 즉, 데이터 정렬은 기본 문자(1차 차이점)와 분음 부호(2차 차이점)의 비교를 수행합니다. 즉 A 와 a 는 같지만 a 와 á 는 다르게 비교 합니다.


3: strength의 기본값으로(MongoDB 기본값) 데이터 정렬은 기본 문자(1차 차이점), 분음 부호(2차 차이점), 대소문자 및 변형(3차 차이점)의 비교를 수행합니다. 즉 A 와 a 가 다르게 비교 합니다.

기본 수준입니다.

4: 구두점이 있는 단어와 없는 단어를 구분이 가능하며(예: "ab" < "a-b" < "aB"), 가타카나와 히라가나를 구별할 수 있습니다.


5: 비교하려는 대상이 모든 레벨에서 동일한 경우 타이브레이커로 사용됩니다.

타이브레이커는 스포츠나 게임에서 여럿이 동률일 때 그중에서 승자나 앞선 순위를 가진 사람을 결정하는 제도를 의미 합니다.
각 문자열의 NFD형식(유니코드 정규화 양식 D)의 유니코드 코드 포인트 값을 통해서 비교를 수행합니다.


numericOrdering: <boolean>
숫자 문자열을 숫자로 비교할지 문자열로 비교할지 결정하는 플래그 입니다.
true 숫자로 비교합니다. 예를 들어, 10은 2보다 큽니다.
false: 문자열로 비교합니다. 예를 들어, "10"은 "2"보다 작습니다.


alternate: <string>
데이터 정렬에서 비교를 위해 공백과 구두점을 기본 문자로 고려해야 하는지 여부를 결정하는 필드입니다.
기본값은  "non-ignorable" 입니다.

non-ignorable : 공백과 구두점은 기본 문자로 간주됩니다.
shifted : 공백과 구두점은 기본 문자로 간주되지 않으며 strength의 레벨이 3보다 클 때만 구별 됩니다.


maxVariable: <string>
alternate : shifted 로 설정하였을 경우 비교에서 어떤 것을 제외할 지를 결정하는 필드 입니다.

punct : 공백과 구두점은 모두 무시할 수 없으며 기본 문자로 간주되지 않습니다.
space : 공백은 무시할 수 없으며 기본 문자로 간주되지 않습니다.


backwards: <boolean>
일부 프랑스어 사전 순서와 같이 구분 기호가 있는 문자열이 문자열 뒤에서 정렬 되는지 여부를 결정하는 플래그입니다.

true : 뒤에서 앞으로 비교합니다.
false : 앞에서 뒤를 비교 합니다.


normalization : <boolean>

텍스트에 정규화가 필요한지 여부를 확인하고 정규화를 수행할지 여부를 결정하는 플래그입니다. 일반적으로 대부분의 텍스트에는 이러한 정규화 처리가 필요하지 않습니다.

true: 완전히 정규화되었는지 확인하고 정규화를 수행하여 텍스트를 비교합니다.
false : 확인하지 않습니다.

기본값은 false 입니다.


앞서 설명한 내용과 같이 MongoDB 3.4버전 부터는 Collation 기능을 지원함에 따라 컬렉션이나 뷰, 인덱스 또는 데이터 정렬을 지원하는 특정 작업에 대한 데이터 정렬(Collation)을 지정할 수 있으며, 쿼리시에도 Collation 을 지정하여 사용할 수 있습니다.


이전에 생성한 Collection 을 삭제 후 collation 을 지정하여 다시 생성 후에 Collection 정보를 확인 해보도록 하겠습니다.

> db.char_test.drop()
> db.createCollection("char_test",{collation:{locale:"ko"}})

> db.getCollectionInfos({name :'char_test'})
[
    {
        "name" : "char_test",
        "type" : "collection",
        "options" : {
            "collation" : {
                "locale" : "ko", <!!--
                "caseLevel" : false,
                "caseFirst" : "off",
                "strength" : 3,
                "numericOrdering" : false,
                "alternate" : "non-ignorable",
                "maxVariable" : "punct",
                "normalization" : false,
                "backwards" : false,
                "version" : "57.1"
            }
        },
        "info" : {
            "readOnly" : false,
            "uuid" : UUID("171b8760-e246-4637-8ed1-1be7fc3a29b8")
        },
        "idIndex" : {
            "v" : 2,
            "key" : {
                "_id" : 1
            },
            "name" : "_id_",
            "collation" : {
                "locale" : "ko",
                "caseLevel" : false,
                "caseFirst" : "off",
                "strength" : 3,
                "numericOrdering" : false,
                "alternate" : "non-ignorable",
                "maxVariable" : "punct",
                "normalization" : false,
                "backwards" : false,
                "version" : "57.1"
            }
        }
    }
]

위의 조회 결과에서 확인 할 수 있듯이 options.collation 항목을 확인 할 수 있습니다.

이제 데이터를 입력 후에 정렬을 하여 조회해보도록 하겠습니다.

> db.char_test.insertMany( [
    {col:"A1"},{col:"Á1"},{col:"á1"},
    {col:"C1"},{col:"가"},{col:"Ć1"},
    {col:"ć1"},{col:"차"},{col:"아"},
    {col:"고"},{col:"누"},{col:"처"},
    {col:"a1"},{col:"B1"},{col:"b1"}
])


> db.char_test.find({},{_id:0}).sort({col:1})
{ "col" : "가" }
{ "col" : "고" }
{ "col" : "누" }
{ "col" : "아" }
{ "col" : "차" }
{ "col" : "처" }
{ "col" : "a1" }
{ "col" : "A1" }
{ "col" : "á1" }
{ "col" : "Á1" }
{ "col" : "b1" }
{ "col" : "B1" }
{ "col" : "C1" }
{ "col" : "ć1" }
{ "col" : "Ć1" }

locale 을 한글인 ko 로 지정한 collection 에서 정렬을 사용하여 조회한 결과는 위와 같이 한글이 먼저 출력 되는 것을 확인 할 수 있습니다.


인덱스를 생성시에도 collation 옵션을 사용할 수 있으며, 별도로 옵션을 사용하지 않는다면 collection 의 설정을 따라가게 됩니다.
인덱스 생성 후 인덱스를 확인 해보도록 하겠습니다.

> db.char_test.createIndex({col:1})


> db.char_test.getIndexes({col:1})

[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "collation" : {
            "locale" : "ko",<!!---- 컬렉션
            "caseLevel" : false,
            "caseFirst" : "off",
            "strength" : 3,
            "numericOrdering" : false,
            "alternate" : "non-ignorable",
            "maxVariable" : "punct",
            "normalization" : false,
            "backwards" : false,
            "version" : "57.1"
        }
    },
    {
        "v" : 2,
        "key" : {
            "col" : 1
        },
        "name" : "col_1",
        "collation" : {
            "locale" : "ko", <!!--- 인덱스
            "caseLevel" : false,
            "caseFirst" : "off",
            "strength" : 3,
            "numericOrdering" : false,
            "alternate" : "non-ignorable",
            "maxVariable" : "punct",
            "normalization" : false,
            "backwards" : false,
            "version" : "57.1"
        }
    }
]

collection 과 동일하게 인덱스의 collation.locale 이 ko 인 것을 확인 할 수 있습니다.

인덱스 생성시 collation 옵션을 지정하여 생성할 수 있습니다. 삭제 후에 다시 생성 해보도록 하겠습니다.

> db.char_test.dropIndex({col:1})

> db.char_test.createIndex({col:1},{collation:{locale:"de"}})

> db.char_test.getIndexes({col:1})

[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "collation" : {
            "locale" : "ko", <!!-- collection의 속성
            "caseLevel" : false,
            "caseFirst" : "off",
            "strength" : 3,
            "numericOrdering" : false,
            "alternate" : "non-ignorable",
            "maxVariable" : "punct",
            "normalization" : false,
            "backwards" : false,
            "version" : "57.1"
        }
    },
    {
        "v" : 2,
        "key" : {
            "col" : 1
        },
        "name" : "col_1",
        "collation" : {
            "locale" : "de", <!!--- index의 속성
            "caseLevel" : false,
            "caseFirst" : "off",
            "strength" : 3,
            "numericOrdering" : false,
            "alternate" : "non-ignorable",
            "maxVariable" : "punct",
            "normalization" : false,
            "backwards" : false,
            "version" : "57.1"
        }
    }
]

위의 조회 결과에서 처럼 index의 collation.locale 은 "de" 인 것을 확인 할 수 있습니다.

아래는 인덱스의 collation document 필드 종류 내역 입니다.

collation: {
   locale: <string>,
   caseLevel: <boolean>,
   caseFirst: <string>,
   strength: <int>,
   numericOrdering: <boolean>,
   alternate: <string>,
   maxVariable: <string>,
   backwards: <boolean>
}



컬렉션을 삭제하고 이번에는 기본값(또는 미지정) 으로 생성 해보도록 하겠습니다.
단순히 binary 비교를 지정하려면 "simple" 값을 지정하면 됩니다. 미지정(default) 와 simple 은 같습니다.

> db.char_test.drop()

> db.createCollection("char_test",{collation:{locale:"simple"}})

> db.getCollectionInfos({name :'char_test'})
[
    {
        "name" : "char_test",
        "type" : "collection",
        "options" : {

        },
        "info" : {
            "readOnly" : false,
            "uuid" : UUID("bf32c470-8186-4949-8449-3759457544f7")
        },
        "idIndex" : {
            "v" : 2,
            "key" : {
                "_id" : 1
            },
            "name" : "_id_"
        }
    }
]

options 에 collation 항목 정보가 별도로 없는 것을 확인할 수 있습니다.

데이터를 입력 후 sort 를 사용하여 조회해보도록 하겠습니다.

> db.char_test.insertMany( [
    {col:"A1"},{col:"Á1"},{col:"á1"},
    {col:"C1"},{col:"가"},{col:"Ć1"},
    {col:"ć1"},{col:"차"},{col:"아"},
    {col:"고"},{col:"누"},{col:"처"},
    {col:"a1"},{col:"B1"},{col:"b1"}
])

> db.char_test.find({},{_id:0}).sort({col:1})
{ "col" : "A1" }
{ "col" : "B1" }
{ "col" : "C1" }
{ "col" : "a1" }
{ "col" : "b1" }
{ "col" : "Á1" }
{ "col" : "á1" }
{ "col" : "Ć1" }
{ "col" : "ć1" }
{ "col" : "가" }
{ "col" : "고" }
{ "col" : "누" }
{ "col" : "아" }
{ "col" : "차" }
{ "col" : "처" }

이전의 locale : ko 를 지정하여 생성한 collection 조회와 다르게 이번에는 영문이 더 먼저 정렬되어 출력 되었습니다.


collation 은 이와 같이 collection 이나 index 생성시 지정할 수도 있고, 조회시(find 메서드 사용시) 에도 collation 을 사용할 수 있습니다.
이번에는 find 메서드를 통해 조회시 collation 을 사용해서 조회 해보도록 하겠습니다.

> db.char_test.find({},{_id:0}).sort({col:1}).collation( { locale: 'ko', strength: 3 } )

{ "col" : "가" }
{ "col" : "고" }
{ "col" : "누" }
{ "col" : "아" }
{ "col" : "차" }
{ "col" : "처" }
{ "col" : "a1" }
{ "col" : "A1" }
{ "col" : "á1" }
{ "col" : "Á1" }
{ "col" : "b1" }
{ "col" : "B1" }
{ "col" : "C1" }
{ "col" : "ć1" }
{ "col" : "Ć1" }

조회 결과는 이전에 locale:ko 로 생성한 컬렉션과 동일하게 출력되는 것을 확인 할 수 있습니다.


이 처럼 MongoDB 3.4 버전 부터 제공되는 collation 기능을 사용하여 정렬 순서 및 문자 및 기호의 비교를 달리 하여 사용할 수 있습니다.
                 

Reference

Reference URL
mongodb.com/collation
mongodb.com/collation-languages-locales
nuli.navercorp.com/article/1079940
terms.naver.com/categoryId=43667


관련된 다른 글

 

 

 

                

0
글에 대한 당신의 생각을 기다립니다. 댓글 의견 주세요!x