복붙노트

[MONGODB] 여러 필드 MongoDB에 의해 기 값

MONGODB

여러 필드 MongoDB에 의해 기 값

예를 들어, 나는이 문서가 :

{
  "addr": "address1",
  "book": "book1"
},
{
  "addr": "address2",
  "book": "book1"
},
{
  "addr": "address1",
  "book": "book5"
},
{
  "addr": "address3",
  "book": "book9"
},
{
  "addr": "address2",
  "book": "book5"
},
{
  "addr": "address2",
  "book": "book1"
},
{
  "addr": "address1",
  "book": "book1"
},
{
  "addr": "address15",
  "book": "book1"
},
{
  "addr": "address9",
  "book": "book99"
},
{
  "addr": "address90",
  "book": "book33"
},
{
  "addr": "address4",
  "book": "book3"
},
{
  "addr": "address5",
  "book": "book1"
},
{
  "addr": "address77",
  "book": "book11"
},
{
  "addr": "address1",
  "book": "book1"
}

그래서 내가 최고 N 주소와 주소 당 최고 M 책을 설명하는 요청을 할 수 on.How 예상 결과의 예 : 주소 1 | book_1 : 5 | book_2 : 10 | book_3 : 50 | 총 : 65 ______________________ 주소 2 | book_1 : 10 | book_2 : 10 | ... | book_M : 10 | 총 : M * 10 ... ______________________ addressN | book_1 : 20 | book_2 : 20 | ... | book_M : 20 | 총 : M * 20

해결법

  1. ==============================

    1.현대 MongoDB를 릴리스에서는 그냥 기본 집계 결과 오프 $ 슬라이스이 무력 할 수 있습니다. "큰"결과를 각각 그룹화하는 대신 병렬 쿼리를 실행 (목록 데모는 대답의 끝에), 또는 서버 9377 해결하려면 $ 푸시에 항목의 수에 "한계"를 허용 할 기다립니다 배열.

    현대 MongoDB를 릴리스에서는 그냥 기본 집계 결과 오프 $ 슬라이스이 무력 할 수 있습니다. "큰"결과를 각각 그룹화하는 대신 병렬 쿼리를 실행 (목록 데모는 대답의 끝에), 또는 서버 9377 해결하려면 $ 푸시에 항목의 수에 "한계"를 허용 할 기다립니다 배열.

    db.books.aggregate([
        { "$group": {
            "_id": {
                "addr": "$addr",
                "book": "$book"
            },
            "bookCount": { "$sum": 1 }
        }},
        { "$group": {
            "_id": "$_id.addr",
            "books": { 
                "$push": { 
                    "book": "$_id.book",
                    "count": "$bookCount"
                },
            },
            "count": { "$sum": "$bookCount" }
        }},
        { "$sort": { "count": -1 } },
        { "$limit": 2 },
        { "$project": {
            "books": { "$slice": [ "$books", 2 ] },
            "count": 1
        }}
    ])
    

    아직 SERVER-9377 해결하지만,이 릴리스 $ 조회에서 대신 "localFields"의 인수와 "foreignFields"옵션과 같은 '파이프 라인'표현 소요 새로운 "비 상관"옵션을 할 수 없습니다. 이 다음에 우리가 "상위 N"결과를 반환하기 위해 $ 제한을 적용 할 수있는 또 다른 파이프 라인 표현과 함께 "자체 조인"할 수 있습니다.

    db.books.aggregate([
      { "$group": {
        "_id": "$addr",
        "count": { "$sum": 1 }
      }},
      { "$sort": { "count": -1 } },
      { "$limit": 2 },
      { "$lookup": {
        "from": "books",
        "let": {
          "addr": "$_id"
        },
        "pipeline": [
          { "$match": { 
            "$expr": { "$eq": [ "$addr", "$$addr"] }
          }},
          { "$group": {
            "_id": "$book",
            "count": { "$sum": 1 }
          }},
          { "$sort": { "count": -1  } },
          { "$limit": 2 }
        ],
        "as": "books"
      }}
    ])
    

    다른 추가는 여기에 "참여"에 일치하는 항목을 선택하여 $ 일치를 사용하여 $ EXPR을 통해 변수를 보간 물론 능력이지만, 일반적인 전제는 내부 콘텐츠를 필터링 할 수있는 "파이프 라인 내에서 파이프 라인"입니다 부모에서 일치합니다. 그들은 모두 "파이프 라인"자신 때문에 우리는 개별적으로 각 결과를 제한 $ 할 수 있습니다.

    이 병렬 쿼리를 실행하기 다음으로 좋은 옵션이 될 것입니다, 그리고 $ 일치 허용하고 "하위 파이프 라인"처리에 인덱스를 사용할 수 있다면 실제로 더 좋을 것이다. 이는 참조 문제가 묻습니다로 "$ 추진에 한계"를 사용하지 않습니다 그래서, 실제로 잘 작동해야 무언가를 제공합니다.

    당신은 최고 "N"문제 우연히 발견 한 것 같다. 어떤면에서 문제가없는 정확한 당신이 물어 제한에 불구하고 해결하기 위해 매우 간단합니다 :

    db.books.aggregate([
        { "$group": {
            "_id": {
                "addr": "$addr",
                "book": "$book"
            },
            "bookCount": { "$sum": 1 }
        }},
        { "$group": {
            "_id": "$_id.addr",
            "books": { 
                "$push": { 
                    "book": "$_id.book",
                    "count": "$bookCount"
                },
            },
            "count": { "$sum": "$bookCount" }
        }},
        { "$sort": { "count": -1 } },
        { "$limit": 2 }
    ])
    

    이제 당신이 같은 결과를 줄 것이다 :

    {
        "result" : [
                {
                        "_id" : "address1",
                        "books" : [
                                {
                                        "book" : "book4",
                                        "count" : 1
                                },
                                {
                                        "book" : "book5",
                                        "count" : 1
                                },
                                {
                                        "book" : "book1",
                                        "count" : 3
                                }
                        ],
                        "count" : 5
                },
                {
                        "_id" : "address2",
                        "books" : [
                                {
                                        "book" : "book5",
                                        "count" : 1
                                },
                                {
                                        "book" : "book1",
                                        "count" : 2
                                }
                        ],
                        "count" : 3
                }
        ],
        "ok" : 1
    }
    

    우리가 기본이 "책"선택 결과 만 필요한 양에 한정되지 않고 주소 값의 최고 결과를 얻을 할 동안, 그에서 요구하는 것과이 다릅니다 그래서.

    아웃이 회전은 할 매우 어려운,하지만 복잡성이 당신이 일치해야하는 항목의 수에 따라 증가하지만이를 수행 할 수 있습니다. 그것을 간단하게 유지하기 위해 우리는이 경기에서 가장에서이 문제를 유지할 수 있습니다 :

    db.books.aggregate([
        { "$group": {
            "_id": {
                "addr": "$addr",
                "book": "$book"
            },
            "bookCount": { "$sum": 1 }
        }},
        { "$group": {
            "_id": "$_id.addr",
            "books": { 
                "$push": { 
                    "book": "$_id.book",
                    "count": "$bookCount"
                },
            },
            "count": { "$sum": "$bookCount" }
        }},
        { "$sort": { "count": -1 } },
        { "$limit": 2 },
        { "$unwind": "$books" },
        { "$sort": { "count": 1, "books.count": -1 } },
        { "$group": {
            "_id": "$_id",
            "books": { "$push": "$books" },
            "count": { "$first": "$count" }
        }},
        { "$project": {
            "_id": {
                "_id": "$_id",
                "books": "$books",
                "count": "$count"
            },
            "newBooks": "$books"
        }},
        { "$unwind": "$newBooks" },
        { "$group": {
          "_id": "$_id",
          "num1": { "$first": "$newBooks" }
        }},
        { "$project": {
            "_id": "$_id",
            "newBooks": "$_id.books",
            "num1": 1
        }},
        { "$unwind": "$newBooks" },
        { "$project": {
            "_id": "$_id",
            "num1": 1,
            "newBooks": 1,
            "seen": { "$eq": [
                "$num1",
                "$newBooks"
            ]}
        }},
        { "$match": { "seen": false } },
        { "$group":{
            "_id": "$_id._id",
            "num1": { "$first": "$num1" },
            "num2": { "$first": "$newBooks" },
            "count": { "$first": "$_id.count" }
        }},
        { "$project": {
            "num1": 1,
            "num2": 1,
            "count": 1,
            "type": { "$cond": [ 1, [true,false],0 ] }
        }},
        { "$unwind": "$type" },
        { "$project": {
            "books": { "$cond": [
                "$type",
                "$num1",
                "$num2"
            ]},
            "count": 1
        }},
        { "$group": {
            "_id": "$_id",
            "count": { "$first": "$count" },
            "books": { "$push": "$books" }
        }},
        { "$sort": { "count": -1 } }
    ])
    

    그 사실은 당신에게 정상이 "주소"항목에서 상위 2 "책을"줄 것이다 그래서.

    하지만 내 돈을 위해, 다음 첫 번째 양식 단순히 "조각"최초의 "N"요소를 취할 반환되는 배열의 요소와 숙박.

    데모 코드는 v8.x 및 v10.x 릴리스 NodeJS의 현재 LTS 버전과 사용에 적합하다. 즉 대부분 비동기 / await를 구문이지만, 심지어 다시 일반 콜백 구현에 작은 일반 약속을 변경 또는과 그러한 제한이 일반적인 흐름 내에서 정말 아무것도, 그리고 적응이 없습니다.

    하는 index.js

    const { MongoClient } = require('mongodb');
    const fs = require('mz/fs');
    
    const uri = 'mongodb://localhost:27017';
    
    const log = data => console.log(JSON.stringify(data, undefined, 2));
    
    (async function() {
    
      try {
        const client = await MongoClient.connect(uri);
    
        const db = client.db('bookDemo');
        const books = db.collection('books');
    
        let { version } = await db.command({ buildInfo: 1 });
        version = parseFloat(version.match(new RegExp(/(?:(?!-).)*/))[0]);
    
        // Clear and load books
        await books.deleteMany({});
    
        await books.insertMany(
          (await fs.readFile('books.json'))
            .toString()
            .replace(/\n$/,"")
            .split("\n")
            .map(JSON.parse)
        );
    
        if ( version >= 3.6 ) {
    
        // Non-correlated pipeline with limits
          let result = await books.aggregate([
            { "$group": {
              "_id": "$addr",
              "count": { "$sum": 1 }
            }},
            { "$sort": { "count": -1 } },
            { "$limit": 2 },
            { "$lookup": {
              "from": "books",
              "as": "books",
              "let": { "addr": "$_id" },
              "pipeline": [
                { "$match": {
                  "$expr": { "$eq": [ "$addr", "$$addr" ] }
                }},
                { "$group": {
                  "_id": "$book",
                  "count": { "$sum": 1 },
                }},
                { "$sort": { "count": -1 } },
                { "$limit": 2 }
              ]
            }}
          ]).toArray();
    
          log({ result });
        }
    
        // Serial result procesing with parallel fetch
    
        // First get top addr items
        let topaddr = await books.aggregate([
          { "$group": {
            "_id": "$addr",
            "count": { "$sum": 1 }
          }},
          { "$sort": { "count": -1 } },
          { "$limit": 2 }
        ]).toArray();
    
        // Run parallel top books for each addr
        let topbooks = await Promise.all(
          topaddr.map(({ _id: addr }) =>
            books.aggregate([
              { "$match": { addr } },
              { "$group": {
                "_id": "$book",
                "count": { "$sum": 1 }
              }},
              { "$sort": { "count": -1 } },
              { "$limit": 2 }
            ]).toArray()
          )
        );
    
        // Merge output
        topaddr = topaddr.map((d,i) => ({ ...d, books: topbooks[i] }));
        log({ topaddr });
    
        client.close();
    
      } catch(e) {
        console.error(e)
      } finally {
        process.exit()
      }
    
    })()
    

    books.json

    { "addr": "address1",  "book": "book1"  }
    { "addr": "address2",  "book": "book1"  }
    { "addr": "address1",  "book": "book5"  }
    { "addr": "address3",  "book": "book9"  }
    { "addr": "address2",  "book": "book5"  }
    { "addr": "address2",  "book": "book1"  }
    { "addr": "address1",  "book": "book1"  }
    { "addr": "address15", "book": "book1"  }
    { "addr": "address9",  "book": "book99" }
    { "addr": "address90", "book": "book33" }
    { "addr": "address4",  "book": "book3"  }
    { "addr": "address5",  "book": "book1"  }
    { "addr": "address77", "book": "book11" }
    { "addr": "address1",  "book": "book1"  }
    
  2. ==============================

    2.아래와 같은 집계 함수를 사용하여 :

    아래와 같은 집계 함수를 사용하여 :

    [
    {$group: {_id : {book : '$book',address:'$addr'}, total:{$sum :1}}},
    {$project : {book : '$_id.book', address : '$_id.address', total : '$total', _id : 0}}
    ]
    

    당신이 다음과 같은 결과를 줄 것이다 :

            {
                "total" : 1,
                "book" : "book33",
                "address" : "address90"
            }, 
            {
                "total" : 1,
                "book" : "book5",
                "address" : "address1"
            }, 
            {
                "total" : 1,
                "book" : "book99",
                "address" : "address9"
            }, 
            {
                "total" : 1,
                "book" : "book1",
                "address" : "address5"
            }, 
            {
                "total" : 1,
                "book" : "book5",
                "address" : "address2"
            }, 
            {
                "total" : 1,
                "book" : "book3",
                "address" : "address4"
            }, 
            {
                "total" : 1,
                "book" : "book11",
                "address" : "address77"
            }, 
            {
                "total" : 1,
                "book" : "book9",
                "address" : "address3"
            }, 
            {
                "total" : 1,
                "book" : "book1",
                "address" : "address15"
            }, 
            {
                "total" : 2,
                "book" : "book1",
                "address" : "address2"
            }, 
            {
                "total" : 3,
                "book" : "book1",
                "address" : "address1"
            }
    

    나는 꽤 당신의 예상 된 결과 형식을 얻을 당신이 필요로하는 일이 자유롭게 수정할하지 않았다.

  3. ==============================

    3.쿼리 아래의 원하는 응답에 주어진 정확히 같은 결과를 제공 할 것입니다 :

    쿼리 아래의 원하는 응답에 주어진 정확히 같은 결과를 제공 할 것입니다 :

    db.books.aggregate([
        {
            $group: {
                _id: { addresses: "$addr", books: "$book" },
                num: { $sum :1 }
            }
        },
        {
            $group: {
                _id: "$_id.addresses",
                bookCounts: { $push: { bookName: "$_id.books",count: "$num" } }
            }
        },
        {
            $project: {
                _id: 1,
                bookCounts:1,
                "totalBookAtAddress": {
                    "$sum": "$bookCounts.count"
                }
            }
        }
    
    ]) 
    

    응답은 아래와 같이보고합니다 :

    /* 1 */
    {
        "_id" : "address4",
        "bookCounts" : [
            {
                "bookName" : "book3",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 2 */
    {
        "_id" : "address90",
        "bookCounts" : [
            {
                "bookName" : "book33",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 3 */
    {
        "_id" : "address15",
        "bookCounts" : [
            {
                "bookName" : "book1",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 4 */
    {
        "_id" : "address3",
        "bookCounts" : [
            {
                "bookName" : "book9",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 5 */
    {
        "_id" : "address5",
        "bookCounts" : [
            {
                "bookName" : "book1",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 6 */
    {
        "_id" : "address1",
        "bookCounts" : [
            {
                "bookName" : "book1",
                "count" : 3
            },
            {
                "bookName" : "book5",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 4
    },
    
    /* 7 */
    {
        "_id" : "address2",
        "bookCounts" : [
            {
                "bookName" : "book1",
                "count" : 2
            },
            {
                "bookName" : "book5",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 3
    },
    
    /* 8 */
    {
        "_id" : "address77",
        "bookCounts" : [
            {
                "bookName" : "book11",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    },
    
    /* 9 */
    {
        "_id" : "address9",
        "bookCounts" : [
            {
                "bookName" : "book99",
                "count" : 1
            }
        ],
        "totalBookAtAddress" : 1
    }
    
  4. from https://stackoverflow.com/questions/22932364/mongodb-group-values-by-multiple-fields by cc-by-sa and MIT license