MongoDB의 복합 인덱스로 구성된 shard key 범위를 나타내는 min/max로 그 범위 내에서 query하는게 의외로 쉽지 않음.
그냥 일반적인 나열로는 안되고, shard key 순서대로 조건을 중첩해야 가능함.
예를 들어 아래와 같은 shard key가 있다고 가정.
shard_key = {
x: 1,
y: 1,
z: 1
};
그리고 chunk의 min/max가 아래와 같다고 가정.
min = {
x: 1,
y: 'zbc',
z: 99
};
max = {
x: 9,
y: 'aaa',
z: 1
};
단순하게 생각해서 저 범위내로 query 한다고 아래처럼 하면 y부터 말이 안됨.
db.col.find({
x: {$gte:1, $lte: 9},
y: {$gte:'zbc', $lte:'aaa'},
z: {$gte:99, $lte:1}
})
그래서 아래처럼 각 단계별로 조건을 중첩하는 로직이 필요함.
db.col.aggregate([
// 일단 첫번째 key로 필터링
{
$match: {
x: {
$gte: 1,
$lte: 9
}
}
},
// min 필터링 (중첩 조건)
{
$match: {
$or: [
{
x: {$gt: 1}
},
{
$and: [
{x: 1},
{
y: {$gte: 'zbc'}
},
{
$or: [
{y: {$gt: 'zbc'}},
{
y: 'zbc',
z: {$gte: 99}
}
]
}
]
}
]
}
},
// max 필터링 (중첩 조건)
{
$match: {
$or: [
{
x: {$lt: 9}
},
{
$and: [
{x: 9},
{
y: {$lte: 'aaa'}
},
{
$or: [
{y: {$lt: 'aaa'}},
{
y: 'aaa',
// chunk의 min은 '포함'이고, max는 '비포함'이므로 $lt
z: {$lt: 1}
}
]
}
]
}
]
}
}
])
shard key의 depth가 늘어날수록 훨씬 복잡해지기 때문에 min, max를 넣으면 범위를 지정할 수 있는 pipeline을 반환해주는 함수 제작.
function getPipeline(min, max)
{
const keys = Object.keys(min);
const pipeline = [{
$match: {
[keys[0]]: {
$gte: min[keys[0]],
$lte: max[keys[0]]
}
}
}];
function createMatchConditions(q, isMin)
{
const keys = Object.keys(q);
const operator = isMin ? '$gt' : '$lt';
let eqOperator = isMin ? '$gte' : '$lte';
const conditions = {
$and: [
{[keys[0]]: q[keys[0]]},
{[keys[1]]: {[eqOperator]: q[keys[1]]}}
]
};
function getNextCondition(idx)
{
if (idx >= keys.length) {
return [];
}
const key = keys[idx];
const nextCondition = {
$or: [
{[key]: {[operator]: q[key]}}
]
};
const nextIdx = idx + 1;
if (nextIdx >= keys.length - 1) {
if (!isMin) eqOperator = '$lt';
nextCondition.$or.push({
[key]: q[keys[idx]],
[keys[nextIdx]]: {[eqOperator]: q[keys[nextIdx]]}
});
}
else {
nextCondition.$or.push({
[key]: q[keys[idx]],
...getNextCondition(nextIdx)
});
}
return nextCondition;
}
const nextCondition = getNextCondition(1);
conditions.$and.push(nextCondition);
return conditions;
}
pipeline.push({
$match: {
$or: [{
[keys[0]]: {
$gt: min[keys[0]]
}
},
createMatchConditions(min, true)]
}
});
pipeline.push({
$match: {
$or: [{
[keys[0]]: {
$lt: max[keys[0]]
}
},
createMatchConditions(max, false)]
}
});
return pipeline;
}
// 상단에 x,y,z 로 구성된 min, max를 넣고 호출하면 필요한 pipeline이 나옴
const pipeline = getPipeline(min, max);
// document count
pipeline.push({ $count: 'document_count' });
db.col.aggregate(pipeline);
// 결과값
[ { document_count: 36037 } ]
// chunk 내에서 N번째 document를 확인하려면 이런식으로
const pipeline = getPipeline(min, max);
pipeline.push({$sort:{x:1, y:1, z:1}});
pipeline.push({$skip: N});
pipeline.push({$limit: 1});
'DataBase' 카테고리의 다른 글
[MongoDB] collection별 balancer enable/disable (0) | 2024.08.26 |
---|---|
[MongoDB] 지정한 사이즈 미만 크기의 chunk를 옮긴 후 merge (0) | 2024.08.21 |
[MongoDB] AutoMerger (0) | 2024.08.07 |
[MongoDB] mergeable chunk list 확인 (0) | 2024.08.07 |
[MongoDB] Range deletion (0) | 2024.07.23 |