MongoDB의 $ in 절이 순서를 보장합니까?
MongoDB의 $in
절을 사용할 때 반환 된 문서의 순서가 항상 배열 인수의 순서와 일치합니까?
언급했듯이 $ in 절의 배열에서 인수 순서는 문서 검색 방법의 순서를 반영하지 않습니다. 물론 그것은 자연 순서이거나 표시된 것처럼 선택한 인덱스 순서입니다.
이 순서를 유지해야하는 경우 기본적으로 두 가지 옵션이 있습니다.
따라서 _id
문서 의의 값을 $in
as에 전달 될 배열과 일치한다고 가정 해 보겠습니다 [ 4, 2, 8 ]
.
Aggregate를 사용하여 접근
var list = [ 4, 2, 8 ];
db.collection.aggregate([
// Match the selected documents by "_id"
{ "$match": {
"_id": { "$in": [ 4, 2, 8 ] },
},
// Project a "weight" to each document
{ "$project": {
"weight": { "$cond": [
{ "$eq": [ "$_id", 4 ] },
1,
{ "$cond": [
{ "$eq": [ "$_id", 2 ] },
2,
3
]}
]}
}},
// Sort the results
{ "$sort": { "weight": 1 } }
])
그래서 그것은 확장 된 형태가 될 것입니다. 여기서 기본적으로 발생하는 것은 값 배열이 전달되는 것처럼 값 을 테스트하고 적절한 가중치를 할당하기 위해 $in
"중첩 된" $cond
문을 생성하는 것 입니다. "가중치"값은 배열의 요소 순서를 반영하므로 필요한 순서로 결과를 얻기 위해 해당 값을 정렬 단계로 전달할 수 있습니다.
물론 실제로 다음과 같이 코드에서 파이프 라인 문을 "빌드"합니다.
var list = [ 4, 2, 8 ];
var stack = [];
for (var i = list.length - 1; i > 0; i--) {
var rec = {
"$cond": [
{ "$eq": [ "$_id", list[i-1] ] },
i
]
};
if ( stack.length == 0 ) {
rec["$cond"].push( i+1 );
} else {
var lval = stack.pop();
rec["$cond"].push( lval );
}
stack.push( rec );
}
var pipeline = [
{ "$match": { "_id": { "$in": list } }},
{ "$project": { "weight": stack[0] }},
{ "$sort": { "weight": 1 } }
];
db.collection.aggregate( pipeline );
mapReduce를 사용한 접근
물론 그 모든 것이 당신의 감성에 무거워 보인다면 mapReduce를 사용하여 똑같은 일을 할 수 있습니다. 이는 더 간단 해 보이지만 다소 느리게 실행될 것입니다.
var list = [ 4, 2, 8 ];
db.collection.mapReduce(
function () {
var order = inputs.indexOf(this._id);
emit( order, { doc: this } );
},
function() {},
{
"out": { "inline": 1 },
"query": { "_id": { "$in": list } },
"scope": { "inputs": list } ,
"finalize": function (key, value) {
return value.doc;
}
}
)
그리고 이것은 기본적으로 출력 된 "키"값이 입력 배열에서 발생하는 방법의 "인덱스 순서"에있는 것에 의존합니다.
따라서 기본적으로 입력 목록의 순서를 $in
결정된 순서로 이미 목록이있는 조건 으로 유지하는 방법입니다 .
MongoDB verion> = 3.4 에만 적용되는 집계 쿼리를 사용하는 또 다른 방법 -
신용은이 멋진 블로그 게시물로 이동 합니다.
이 순서로 가져올 예제 문서-
var order = [ "David", "Charlie", "Tess" ];
쿼리-
var query = [
{$match: {name: {$in: order}}},
{$addFields: {"__order": {$indexOfArray: [order, "$name" ]}}},
{$sort: {"__order": 1}}
];
var result = db.users.aggregate(query);
사용 된 이러한 집계 연산자를 설명하는 게시물의 또 다른 인용문-
"$ addFields"단계는 3.4에서 새로 추가되었으며 다른 모든 기존 필드를 몰라도 기존 문서에 새 필드를 "$ project"할 수 있습니다. 새로운 "$ indexOfArray"표현식은 주어진 배열에서 특정 요소의 위치를 반환합니다.
기본적으로 addFields
연산자는 order
찾을 때 모든 문서에 새 필드를 추가 하고이 order
필드는 우리가 제공 한 배열의 원래 순서를 나타냅니다. 그런 다음이 필드를 기준으로 문서를 정렬합니다.
을 사용하지 않으려는 경우 aggregate
다른 해결책은 find
다음 을 사용 하여 클라이언트 측 문서 결과 를 사용 하고 정렬하는 것입니다 array#sort
.
$in
값이 숫자와 같은 기본 유형 인 경우 다음 과 같은 접근 방식을 사용할 수 있습니다.
var ids = [4, 2, 8, 1, 9, 3, 5, 6];
MyModel.find({ _id: { $in: ids } }).exec(function(err, docs) {
docs.sort(function(a, b) {
// Sort docs by the order of their _id values in ids.
return ids.indexOf(a._id) - ids.indexOf(b._id);
});
});
상기 중간 $in
값이 같은 비 프리미티브 종류 ObjectId
들과 같은 다른 접근 방법이 필요 indexOf
경우에 참고로 비교한다.
Node.js 4.x +를 사용 하는 경우 함수를 다음과 같이 변경하여이를 사용 Array#findIndex
하고 ObjectID#equals
처리 할 수 있습니다 sort
.
docs.sort((a, b) => ids.findIndex(id => a._id.equals(id)) -
ids.findIndex(id => b._id.equals(id)));
또는 밑줄 / lodash가있는 Node.js 버전 findIndex
:
docs.sort(function (a, b) {
return _.findIndex(ids, function (id) { return a._id.equals(id); }) -
_.findIndex(ids, function (id) { return b._id.equals(id); });
});
JonnyHK 의 솔루션과 유사하게 , EcmaScript 2015 find
의 map
및 Array.prototype.find
함수 조합을 사용하여 클라이언트 (클라이언트가 JavaScript에있는 경우)에서 반환 된 문서를 재정렬 할 수 있습니다 .
Collection.find({ _id: { $in: idArray } }).toArray(function(err, res) {
var orderedResults = idArray.map(function(id) {
return res.find(function(document) {
return document._id.equals(id);
});
});
});
몇 가지 참고 사항 :
- 위의 코드는 Mongoose가 아닌 Mongo Node 드라이버를 사용하고 있습니다.
- 의
idArray
배열입니다.ObjectId
- 이 메서드의 성능 대 정렬을 테스트하지는 않았지만 반환 된 각 항목 (매우 일반적 임)을 조작해야하는 경우
map
콜백 에서 수행 하여 코드를 단순화 할 수 있습니다.
An easy way to order the result after mongo returns the array is to make an object with id as keys and then map over the given _id's to return an array that is correctly ordered.
async function batchUsers(Users, keys) {
const unorderedUsers = await Users.find({_id: {$in: keys}}).toArray()
let obj = {}
unorderedUsers.forEach(x => obj[x._id]=x)
const ordered = keys.map(key => obj[key])
return ordered
}
Always? Never. The order is always the same: undefined (probably the physical order in which documents are stored). Unless you sort it.
I know this question is related to Mongoose JS framework, but the duplicated one is generic, so I hope posting a Python (PyMongo) solution is fine here.
things = list(db.things.find({'_id': {'$in': id_array}}))
things.sort(key=lambda thing: id_array.index(thing['_id']))
# things are now sorted according to id_array order
I know this is an old thread, but if you're just returning the value of the Id in the array, you may have to opt for this syntax. As I could not seem to get indexOf value to match with a mongo ObjectId format.
obj.map = function() {
for(var i = 0; i < inputs.length; i++){
if(this._id.equals(inputs[i])) {
var order = i;
}
}
emit(order, {doc: this});
};
How to convert mongo ObjectId .toString without including 'ObjectId()' wrapper -- just the Value?
You can guarantee order with $or clause.
So use $or: [ _ids.map(_id => ({_id}))]
instead.
This is a code solution after the results are retrieved from Mongo. Using a map to store index and then swapping values.
catDetails := make([]CategoryDetail, 0)
err = sess.DB(mdb).C("category").
Find(bson.M{
"_id": bson.M{"$in": path},
"is_active": 1,
"name": bson.M{"$ne": ""},
"url.path": bson.M{"$exists": true, "$ne": ""},
}).
Select(
bson.M{
"is_active": 1,
"name": 1,
"url.path": 1,
}).All(&catDetails)
if err != nil{
return
}
categoryOrderMap := make(map[int]int)
for index, v := range catDetails {
categoryOrderMap[v.Id] = index
}
counter := 0
for i := 0; counter < len(categoryOrderMap); i++ {
if catId := int(path[i].(float64)); catId > 0 {
fmt.Println("cat", catId)
if swapIndex, exists := categoryOrderMap[catId]; exists {
if counter != swapIndex {
catDetails[swapIndex], catDetails[counter] = catDetails[counter], catDetails[swapIndex]
categoryOrderMap[catId] = counter
categoryOrderMap[catDetails[swapIndex].Id] = swapIndex
}
counter++
}
}
}
참고URL : https://stackoverflow.com/questions/22797768/does-mongodbs-in-clause-guarantee-order
'Programing' 카테고리의 다른 글
문자열이 C에서 다른 문자열로 시작하는지 확인하는 방법은 무엇입니까? (0) | 2020.10.28 |
---|---|
슬롯에 인수 전달 (0) | 2020.10.28 |
Mockito-스파이 대 모의 (0) | 2020.10.27 |
Postgres 캐시 / 버퍼를보고 지우시겠습니까? (0) | 2020.10.27 |
inspect를 사용하여 Python에서 수신자로부터 호출자 정보를 얻는 방법은 무엇입니까? (0) | 2020.10.27 |