$lookup 후 집계 필터
$lookup 후 필터를 추가하려면 어떻게 해야 하나요, 아니면 다른 방법이 있나요?
내 데이터 수집 테스트는 다음과 같습니다.
{ "_id" : ObjectId("570557d4094a4514fc1291d6"), "id" : 100, "value" : "0", "contain" : [ ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d7"), "id" : 110, "value" : "1", "contain" : [ 100 ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d8"), "id" : 120, "value" : "1", "contain" : [ 100 ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d9"), "id" : 121, "value" : "2", "contain" : [ 100, 120 ] }
id 100을 선택하고 하위 항목을 집계합니다.
db.test.aggregate([ {
$match : {
id: 100
}
}, {
$lookup : {
from : "test",
localField : "id",
foreignField : "contain",
as : "childs"
}
}]);
다시 왔어요.
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d9"),
"id":121,
"value":"2",
"contain":[ 100, 120 ]
}
]
}
그러나 "값: 1"과 일치하는 하위 항목만 원합니다.
마지막에는 다음과 같은 결과가 예상됩니다.
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
}
]
}
여기서의 질문은 사실 뭔가 다른 것에 관한 것이고 전혀 필요하지 않습니다.하지만 순수하게 "$lookup 후 필터링"이라는 제목으로 이곳에 온 모든 사람들에게 다음과 같은 기술이 적용됩니다.
MongoDB 3.6 - 하위 파이프라인
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
이전 - $lookup + $unwind + $match 병합
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$unwind": "$childs" },
{ "$match": { "childs.value": "1" } },
{ "$group": {
"_id": "$_id",
"id": { "$first": "$id" },
"value": { "$first": "$value" },
"contain": { "$first": "$contain" },
"childs": { "$push": "$childs" }
}}
])
어레이에서 사용하는 것과 반대되는 이유가 무엇인지 의문이 든다면, Aggregate $lookup 일치하는 파이프라인의 문서 총 크기가 일반적으로 필요하고 훨씬 더 최적인 이유에 대한 모든 세부 정보에 대해 최대 문서 크기를 초과합니다.
MongoDB 3.6 이상 버전의 경우, 일반적으로 더 표현적인 "하위 파이프라인"은 배열로 반환되기 전에 외부 컬렉션의 결과를 "필터링"하려는 것입니다.
질문에 "가입 안 함"이 전혀 필요하지 않은 이유를 실제로 설명하는 답변으로 돌아갑니다.
원래의
이렇게 사용하는 것은 여기서 원하는 일을 하는 가장 "효율적인" 방법이 아닙니다.하지만 이것에 대해서는 나중에 더.
기본 개념으로, 결과 어레이에 사용합니다.
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$project": {
"id": 1,
"value": 1,
"contain": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$eq": [ "$$child.value", "1" ] }
}
}
}}
]);
또는 대신 다음을 사용합니다.
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$value", "0" ] },
{ "$eq": [ "$value", "1" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
]);
둘 다 동일한 결과를 얻습니다.
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
}
]
}
중요한 것은 그 자체가 특정 데이터만 선택하기 위해 "아직" 쿼리할 수 없다는 것입니다.그래서 모든 "필터링"은 다음 이후에 발생해야 합니다.
그러나 실제로 이러한 유형의 "자체 조인"의 경우에는 전혀 사용하지 않고 추가 읽기 및 "해시 병합"의 오버헤드를 완전히 피하는 것이 좋습니다.관련 항목을 가져오고 대신 다음을 수행합니다.
db.test.aggregate([
{ "$match": {
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}},
{ "$group": {
"_id": {
"$cond": {
"if": { "$eq": [ "$value", "0" ] },
"then": "$id",
"else": { "$arrayElemAt": [ "$contain", 0 ] }
}
},
"value": { "$first": { "$literal": "0"} },
"childs": {
"$push": {
"$cond": {
"if": { "$ne": [ "$value", "0" ] },
"then": "$$ROOT",
"else": null
}
}
}
}},
{ "$project": {
"value": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$ne": [ "$$child", null ] }
}
}
}}
])
제가 의도적으로 외부 필드를 제거했기 때문에 조금 다를 뿐입니다.다음과 같이 하려면 직접 추가하십시오.
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
그래서 여기서 유일한 진짜 문제는 "필터링"입니다.null
배열의 결과, 현재 문서가 다음일 때 생성됩니다.parent
항목을 처리할 때
여기서 또한 누락된 것처럼 보이는 것은 찾고 있는 결과에 집계나 "하위 쿼리"가 전혀 필요하지 않다는 것입니다.결론을 내렸거나 다른 곳에서 찾을 수 있는 구조는 "노드"를 얻을 수 있도록 "설계"되어 있으며, 단일 쿼리 요청에서 모든 노드가 "하위"입니다.
즉, 실제로 필요한 것은 "쿼리"뿐이며, 데이터 수집(어떤 콘텐츠도 "축소"되지 않기 때문에 발생하는 모든 것)은 커서 결과를 반복하는 기능일 뿐입니다.
var result = {};
db.test.find({
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
if ( doc.id == 100 ) {
result = doc;
result.childs = []
} else {
result.childs.push(doc)
}
})
printjson(result);
이는 정확히 동일한 작업을 수행합니다.
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
그리고 여기서 정말로 해야 할 일은 부모와 자식을 모두 선택하는 "단일" 쿼리를 발행하는 것이라는 증거가 됩니다.반환된 데이터는 동일하며, 서버 또는 클라이언트에서 수행하는 작업은 수집된 다른 형식으로 "마사지"하는 것뿐입니다.
이는 "관계형" 데이터베이스에서 작업을 수행한 방식에 대해 "잡힐" 수 있지만 데이터 저장 방식이 "변경"되었기 때문에 더 이상 동일한 접근 방식을 사용할 필요가 없다는 것을 깨닫지 못하는 경우 중 하나입니다.
이것이 바로 하나의 쿼리 내에서 부모와 자식을 쉽게 선택할 수 있는 구조의 문서 예제 "하위 참조가 있는 모델 트리 구조"의 요점입니다.
언급URL : https://stackoverflow.com/questions/36459983/aggregation-filter-after-lookup
'programing' 카테고리의 다른 글
농담 유형 스크립트 - 모의 날짜 생성자 (0) | 2023.06.19 |
---|---|
완전히 실행 가능한 JAR을 실행하는 스프링 부트 및 -D 속성 지정 (0) | 2023.06.19 |
범위를 변경하지 않고 VLOOKUP 공식 작성 (0) | 2023.06.19 |
스프링 부팅에서 @EnableWebFluxSecurity를 사용할 때 오류 발생 (0) | 2023.06.19 |
Mongoose - RangeError: 최대 콜 스택 크기 초과 (0) | 2023.06.19 |