Database/Mongo

자주 사용되는 Aggregation Stage

Jeffrey Oh 2023. 3. 14. 14:01
반응형

예제 실습을 위한 데이터 추가

db.orders.insertMany( [
    { _id: 0, name: "Pepperoni", size: "small", price: 19,
      quantity: 10, date: ISODate( "2021-03-13T08:14:30Z" ) },
    { _id: 1, name: "Pepperoni", size: "medium", price: 20,
      quantity: 20, date : ISODate( "2021-03-13T09:13:24Z" ) },
    { _id: 2, name: "Pepperoni", size: "large", price: 21,
      quantity: 30, date : ISODate( "2021-03-17T09:22:12Z" ) },
    { _id: 3, name: "Cheese", size: "small", price: 12,
      quantity: 15, date : ISODate( "2021-03-13T11:21:39.736Z" ) },
    { _id: 4, name: "Cheese", size: "medium", price: 13,
      quantity:50, date : ISODate( "2022-01-12T21:23:13.331Z" ) },
    { _id: 5, name: "Cheese", size: "large", price: 14,
      quantity: 10, date : ISODate( "2022-01-12T05:08:13Z" ) },
    { _id: 6, name: "Vegan", size: "small", price: 17,
      quantity: 10, date : ISODate( "2021-01-13T05:08:13Z" ) },
    { _id: 7, name: "Vegan", size: "medium", price: 18,
      quantity: 10, date : ISODate( "2021-01-13T05:10:13Z" ) }
])

  1. size 가 medium 인 것들을 _id 가 name 이되고, quantity 가 totalQuantity 별칭으로 인 것으로 조회

    db.orders.aggregate([
    {
     $match: {
       size: "medium"
     }
    },
    {
     $group: {
       _id: {
         $getField : "name"
       },
       totalQuantity: {
         $sum: {
           $getField : "quantity"
         }
       }
     }
    }
    ])

위 처럼 getField 를 매번 쓰기는 번거로우니 아래처럼 약어로 사용할 수도 있다

db.orders.aggregate([
  {
    $match: {
      size: "medium"
    }
  },
  {
    $group: {
      _id: "$name",
      totalQuantity: {
        $sum: "$quantity"      
      }
    }
  }
])

  1. 날짜별 매출, 평균 판매 수량을 조회

    db.orders.aggregate([
    // 날짜 기간 조회
    {
     $match: {
       date: {
         $gte: new ISODate("2020-01-30"),
         $lt: new ISODate("2022-01-30")
       }
     }
    },
    // group 처리
    {
     $group: {
       _id: {
         $dateToString: { // dateToString - 날짜를 문자열로 변환해준다
           format: "%Y-%m-%d",
           date: "$date"
         }
       },
       totalOrderValue: { // totalOrderValue 라는 별칭을 줌
         $sum: {
           // 필드를 곱할 때는 multiply 를 사용
           $multiply: ["$price", "$quantity"]
         }
       },
       averageOrderQuantity: {
         $avg: "$quantity"
       }
     }
    },
    // stage 가 끝난 이후에는 별칭으로 정한 필드를 사용가능
    // mysql 에서 as 로 컬럼조회 한 경우 group by 에서 사용 하는 케이스
    // ex) select count(*) as `cnt` from test group by id having cnt > 0
    {
     $sort: {
       totalOrderValue: -1
     }
    }
    ])

예제 실습을 위한 데이터 추가

db.books.insertMany([
    { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
    { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
    { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },
    { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
    { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])
  1. 조회한 값을 배열에 넣는 방법

    db.books.aggregate([
    {
     $group: {
       _id: "$author",
       books: {
         // push : 배열에 넣을 때 사용
         $push : "$title"
       }
     },
    }
    ])

  1. document 자체를 넣는 방법

    db.books.aggregate([
    {
     $group: {
       _id: "$author",
       books: {
         // ROOT - 시스템 변수 이며 $$는 $ 이후에 나오는 것을 참조변수라고 생각하면됨
         // ${$ROOT} - $ROOT 의 결과를 변수화 하여 데이터를 넣은 것
         // ROOT는 top-level document 를 가리킴
         $push : "$$ROOT"
       }
     },
    }
    ])

4-1. group 안에서 별칭을 정의한 경우

db.books.aggregate([
  {
    $group: {
      _id: "$author",
      books: {
        $push : "$$ROOT"
      },
      // group 안에 별칭을 정의한 경우
      totalCopies: {
        $sum: "$copies"
      }
    },
  }
])

4-2. stage 로 꼭 분리해야하는 경우

db.books.aggregate([
  {
    $group: {
      _id: "$author",
      books: {
        $push : "$$ROOT"
      }
    },
  },
  {
    // addFields 를 사용하여 커스텀 필드를 추가
    $addFields: {
      totalCopies: { // totalCopies 별칭 정의
        $sum: "$books.copies"
      }
    }
  }
])

기존 orders 를 삭제 및 새로운 데이터 추가

db.orders.drop()

db.orders.insertMany([
    { "productId" : 1,   "price" : 12,   },
    { "productId" : 2,   "price" : 20,   },
    { "productId" : 3,   "price" : 80,   }
])

db.products.insertMany([
    { "id" : 1,  "instock" : 120 },  
    { "id" : 2,  "instock" : 80  }, 
    { "id" : 3,  "instock" : 60  }, 
    { "id" : 4,  "instock" : 70  }
])
  1. join 맛보기

    db.orders.aggregate([
    // mysql 의 join 함수명을 mql에서는 lookup 이라고 씀
    {
     $lookup: {
       from: "products", // join 시킬 collection
       localField: "productId", // 주체의 key
       foreignField: "id", // join 시킬 fk
       as: "data" // 별칭 지정
     }
    },
    {
     $match: {
       // expr(expression) 같은 필드를 기준으로 비교를 할 때 사용
       $expr: {
         // expr 를 사용할 때 비교값을 array 로 하면 제대로된 값이 나올수 없다
         $gt: ['$data.instock', '$price']
       }
     }
    }
    ])

5-1. unwind 사용하여 expr 사용 시 배열 사용할 수 있게 하기

unwind 는 묶었던 데이터를 다시 풀어서 사용해야할 때 쓴다

db.orders.aggregate([
  {
    $lookup: {
      from: "products",
      localField: "productId",
      foreignField: "id",
      as: "data"
    }
  },
  {
    $unwind: "$data"
  },
  {
    $match: {
      $expr: {
        $gt: ['$data.instock', '$price']
      }
    }
  }
])

  1. sample 사용하여 일부만 조회하기

sample은 랜덤하여 조회하기 때문에 limit 와는 다르다

db.listingsAndReviews.aggregate([
  {
    $sample: { size: 3 }
  },
  {
    $project: {
      name: 1,
      summary: 1
    }
  }
])

  1. skip 사용하기

    db.listingsAndReviews.aggregate([
    {
     $match: {
       property_type: "Apartment"
     },
    },
    {
     $sort: {
       number_of_reviews: -1
     }
    },
    {
     // skip - offset 이라고 생각하면됨
     $skip: 0
    },
    {
     $limit: 5
    },
    {
     $project: {
       name: 1,
       number_of_reviews: 1
     }
    }
    ])

  1. out을 이용하여 조회 결과를 다른 collection 에 저장
db.books.aggregate([
  {
    $group: {
      _id: "$author",
      books: { $push: "$title" }
    }
  },
  {
    // authors 라는 collection 이 새로 만들어지면서 조회된 결과가 저장된다
    $out: "authors"
  }
])
반응형