2014-12-12 1 views
6

배열의 요소를 일치 : ""있는하여 MongoDB의 집계 프레임 워크 : 프로젝트는 내가 같은 "클래스"문서가

{ 
className: "AAA", 
students: [ 
    {name:"An", age:"13"}, 
    {name:"Hao", age:"13"}, 
    {name:"John", age:"14"}, 
    {name:"Hung", age:"12"} 
    ] 
} 

을 그리고 내가 이름을 가진 학생을 얻으려면, 단지에서 요소를 일치 수 배열 "학생". 나는 같은 기능 발견()로이 작업을 수행 할 수 있습니다

>db.class.find({"students.name":"An"}, {"students.$":true}) 
{ 
"_id" : ObjectId("548b01815a06570735b946c1"), 
"students" : [ 
    { 
     "name" : "An", 
     "age" : "13" 
    } 
]} 

그것은 괜찮지 만 다음과 같은 난 집계와 동일한 작업을 수행 할 때, 그것은 오류 얻을 :

db.class.aggregate([ 
    {$match:{"students.name":'An'}}, 
    {$project:{"students.$":true}} 
]) 

오류는 다음과 같습니다

uncaught exception: aggregate failed: { 
    "errmsg" : "exception: FieldPath field names may not start with '$'.", 
    "code" : 16410, 
    "ok" : 0 
} 

왜? aggregate()의 $ 프로젝트 연산자에서 배열에 "$"를 사용할 수 없지만 find()의 프로젝트 연산자에서이 연산자를 사용할 수 있습니다.

답변

1

파이프 라인에 언 와인드 연산자를 사용해보십시오 : http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/#pipe._S_unwind

귀하의 집계가 docs에서

db.class.aggregate([ 
    { $match: { "students.name": "An" }, 
    { $unwind: "$students" }, 
    { $project: { "students": 1 } } 
]) 
+0

나는 그것을 시도했지만, 나는이 해결책이 좋지 않다고 생각한다. $ unwind를 사용하면 문서가 너무 많은 시간 (배열의 요소 수만큼) 복제되어 성능에 영향을 미칠 수 있습니다. – Deka

+0

그건 의미가 있습니다. 그렇게 할 수있는 방법은 파이프 라인에서 풀림과 일치 순서를 전환하여 문서를 먼저 필터링 한 다음 푸는 것이 쉽도록 푸는 것입니다. 업데이트 된 답변을 참조하십시오. – atyagi

+0

그것은 수용할만한 해결책이 될 것입니다, 나는 단지 "왜"나는 $ ("$"를 집계()의 $ 프로젝트 연산자로 사용할 수있는 반면, find()의 프로젝트 연산자에서는 이것을 사용할 수는 없다 "라고 말합니다. – Deka

3

과 같습니다

위치 적 연산자 $는 사용할 수 없습니다

Use $ in the projection document of the find() method or the findOne() method when you only need one particular array element in selected documents.

집계 파이프 라인 투영 단계. 거기에서 인식되지 않습니다.

찾기 쿼리와 함께 프로젝션을 실행하면 쿼리의 투영 부분에 대한 입력이 쿼리와 일치하는 단일 문서이기 때문에 이는 의미가 있습니다. 일치하는 컨텍스트는 투영 중에도 알려져 있습니다 . 따라서 쿼리와 일치하는 각 문서의 경우 투영 연산자가 적용되어 다음 일치 항목이 검색됩니다. 의 경우

:

db.class.aggregate([ 
    {$match:{"students.name":'An'}}, 
    {$project:{"students.$":true}} 
]) 

aggregation 파이프 라인 단계의 집합입니다. 각 단계는 이전 단계 또는 다음 단계와 완전히 독립적이며 독립적입니다. 일련의 문서는 파이프 라인의 다음 단계로 전달되기 전에 스테이지를 완전히 통과합니다. 이 경우의 첫 번째 단계는 $match 단계이며, 모든 문서는 일치 조건에 따라 필터링됩니다. 이제 프로젝션 스테이지에 대한 입력이 세트의 문서가 일치 스테이지의 일부로 필터링되었습니다.

그래서 현재 단계에서 어떤 기준으로 필드가 필터링되었는지 알 수 없기 때문에 영사 단계의 위치 연산자는 이해가되지 않습니다. 따라서 $ 연산자는 필드 경로의 일부로 사용할 수 없습니다.

왜 아래가 작동합니까?

db.class.aggregate([ 
    { $match: { "students.name": "An" }, 
    { $unwind: "$students" }, 
    { $project: { "students": 1 } } 
]) 

여러분도 보셨 듯이 프로젝션 스테이지는 일련의 문서를 입력으로 가져 와서 필요한 필드를 투영합니다. 그것은 이전 단계와 다음 단계에서 독립적입니다.

+0

감사합니다! "왜 우리는 aggregate()의 $ 프로젝트 연산자에서 배열에"$ "를 사용할 수 있지만 find()의 프로젝트 연산자에서 이것을 사용할 수 있는지 이해할 수있었습니다. 이 솔루션을 프로젝트에 사용 하겠지만 배열을 풀지 않는 또 다른 방법을 찾습니다. 배열에 요소가 너무 많으면 성능이 떨어집니다. 어쨌든 ... 다시 한번 감사드립니다! – Deka

+1

네, 맞습니다. 일치하는 하위 문서가 하나 뿐이라는 것을 알고 있으면 질문과 마찬가지로 find() 쿼리를 사용하여 맹목적으로 진행해야합니다. 그러나 불행히도, 집합에서, 당신은 단지 풀지 않고서는 안됩니다. – BatScream

관련 문제