기초를 넘어서: 데이터 분석을 위한 고급 MongoDB 쿼리 명령어

고급 MongoDB 필터, 프로젝션 및 집계 단계를 사용하여 데이터베이스 밖으로 데이터를 이동하지 않고 데이터를 분석하세요.

기초를 넘어서: 데이터 분석을 위한 고급 MongoDB 쿼리 명령어

MongoDB 쿼리는 단순한 find() 호출을 넘어설 때 더 유용해집니다. 중첩된 문서를 필터링하거나, 출력을 재구성하거나, 그룹화된 결과를 계산해야 하는 경우, 고급 쿼리 연산자와 집계 단계를 사용하면 데이터 가까이에서 작업을 수행할 수 있습니다.

아래 예제는 보고, 문제 해결 및 일회성 분석을 위해 mongosh에서 실행할 수 있는 실용적인 명령어에 초점을 맞춥니다.

쿼리 연산자로 복잡한 필터링 마스터하기

기본 find() 메서드는 간단한 동등성 검사를 처리하지만, 고급 분석은 종종 여러 조건을 결합하거나 특정 필드 구조를 쿼리해야 합니다. MongoDB는 세분화된 필터를 구축하기 위한 풍부한 쿼리 연산자 세트를 제공합니다.

복합 쿼리를 위한 논리 연산자

논리 연산자를 사용하면 여러 쿼리 조건을 결합하여 반환되는 문서를 세밀하게 제어할 수 있습니다. 이는 복잡한 분석 질문을 구조화하는 데 필수적입니다.

  • $and / 암시적 $and: 모두 참이어야 하는 여러 기준을 지정하는 데 사용됩니다. 종종 암시적이지만(쿼리 객체에 조건을 순차적으로 나열), 동일한 필드를 여러 번 쿼리할 때는 $and가 필요합니다.
    // 암시적 $and: 25세 이상이고 뉴욕에 사는 사용자 찾기
    db.users.find({ age: { $gt: 25 }, city: "New York" });
    
    // 명시적 $and: 'score'가 90보다 크고 'level'이 5인 문서 찾기
    db.results.find({ $and: [ { score: { $gt: 90 } }, { level: 5 } ] });
    
  • $or: 지정된 표현식 배열 중 하나라도 일치하는 문서를 선택합니다.
    db.products.find({ $or: [ { category: "Electronics" }, { price: { $lt: 100 } } ] });
    
  • $not: 지정된 표현식의 결과를 부정합니다.

지리 공간 및 배열 연산자

위치 기반 데이터 또는 복잡한 배열의 경우 특수 연산자가 분석 기능을 제공합니다:

  • $geoWithin / $near: 특정 지리적 영역 또는 근접성 내에서 데이터를 찾는 데 필수적입니다.
  • $elemMatch: 포함된 문서 배열을 쿼리하는 데 중요하며, 배열의 하나의 요소가 $elemMatch 내의 모든 지정된 기준과 일치하도록 보장합니다.
    // 'items' 배열에서 가격이 500보다 크고 수량이 1보다 큰 항목이 하나 이상 있는 주문 찾기
    db.orders.find({ items: { $elemMatch: { price: { $gt: 500 }, qty: { $gt: 1 } } } });
    

고급 프로젝션: 출력 형태 만들기

find() 메서드의 두 번째 인수를 사용하여 관리되는 프로젝션은 반환되는 필드를 결정합니다. 고급 프로젝션은 단순한 포함/제외를 넘어 반환된 데이터를 변환하거나 형태를 만듭니다.

필드 제외 및 포함

  • 1은 필드를 포함하고, 0은 필드를 제외합니다.
  • 중요 참고 사항: _id 필드를 제외하고는 포함(1)과 제외(0)를 혼합할 수 없습니다(_id는 기본적으로 포함되며 0으로 설정하여 명시적으로 제외할 수 있습니다).
// 'name'과 'email'만 포함하고 '_id'는 제외
 db.users.find({}, { name: 1, email: 1, _id: 0 });

배열 슬라이싱 및 조작

프로젝션은 $slice를 사용하여 반환되는 배열 요소 수를 제한할 수 있습니다:

  • $slice: N: 처음 N개의 요소를 반환합니다.
  • $slice: -N: 마지막 N개의 요소를 반환합니다.
  • $slice: [M, N]: 인덱스 M에서 시작하여 N개의 요소를 반환합니다.
// 'history' 배열에서 마지막 3개 항목만 반환
 db.logs.find({}, { history: { $slice: -3 } });

집계 프레임워크로 데이터 분석하기

MongoDB 집계 프레임워크는 복잡한 데이터 분석을 위한 가장 강력한 도구로, 파이프라인 단계를 통해 데이터 레코드를 처리할 수 있습니다. 각 단계는 이전 단계에서 전달된 데이터에 대해 특정 변환 또는 작업을 수행합니다.

주요 집계 단계

기본 구조는 db.collection.aggregate([...pipeline])을 사용합니다.

1. $match (필터링)

find()와 유사하게 작동하지만, 후속 단계 전에 적용되어 데이터 세트를 조기에 줄여 성능을 최적화합니다.

2. $group (그룹화 및 계산)

이 단계는 지정된 식별자(_id)로 문서를 그룹화하고 누산기 연산자를 적용하여 요약 통계를 계산합니다.

일반적인 누산기:

  • $sum
  • $avg
  • $min, $max
  • $push (그룹에서 배열 데이터 수집)
// 부서별 평균 점수 계산
 db.scores.aggregate([
  { $group: { 
    _id: "$department", 
    averageScore: { $avg: "$score" },
    totalStudents: { $sum: 1 }
  } }
]);

3. $project (문서 재구성)

집계 내에서 출력 문서를 재구성하는 데 사용되며, find() 프로젝션과 유사하지만 새로 계산된 필드를 만드는 데 자주 사용됩니다.

  • 계산된 필드: 기존 필드를 사용하여 프로젝션 단계 내에서 계산을 수행할 수 있습니다.
// 파이프라인 내에서 이익 마진 계산
 db.sales.aggregate([
  { $project: { 
    _id: 0, 
    productName: 1,
    profit: { $subtract: ["$salePrice", "$cost"] }
  } }
]);

4. $lookup (데이터 조인)

$lookup 단계는 다른 컬렉션에서 일치하는 문서를 추가하며, 왼쪽 외부 조인과 유사합니다. 애플리케이션 코드에서 조인을 수행하지 않고 보고를 위해 문서를 보강해야 할 때 유용합니다.

// 'orders' 컬렉션과 'customers' 컬렉션 조인
 db.orders.aggregate([
  { $match: { status: "Pending" } },
  { $lookup: {
      from: "customers",         // 조인할 컬렉션
      localField: "customerId",  // 입력 문서(orders)의 필드
      foreignField: "_id",       // "from" 컬렉션(customers) 문서의 필드
      as: "customerDetails"      // 출력 배열 필드 이름
  } }
]);

5. $unwind (배열 분해)

배열 필드에 여러 요소가 포함된 경우, $unwind는 배열의 요소에 대해 별도의 출력 문서를 생성하여 배열 내용을 더 쉽게 그룹화하거나 필터링할 수 있도록 데이터를 효과적으로 비정규화합니다.

경고: $unwind는 문서 수를 크게 증가시킬 수 있습니다. 일반적으로 초기 세트를 줄이기 위해 $match 후에 신중하게 사용하십시오.

분석 쿼리를 위한 모범 사례

  1. 진입점 인덱싱: 초기 $match 단계에서 사용되는 필드와 인덱스 기반 $sort 단계를 인덱싱하십시오. $group은 입력 문서 수가 적으면 이점을 얻을 수 있지만, 그룹화된 필드에 인덱스가 있다고 해서 자동으로 빨라지지는 않습니다.
  2. 조기 필터링: 집계 파이프라인에서 가능한 한 빨리 $match 단계를 배치하십시오. 문서 수를 조기에 줄이면 $lookup 또는 $group과 같은 이후의 더 비싼 단계에서 상당한 처리 능력을 절약할 수 있습니다.
  3. 적절한 데이터 유형 사용: 비교 필드(예: 날짜 또는 숫자 값)가 일관되게 저장되었는지 확인하십시오. 유형 불일치는 $match 연산자가 자동으로 또는 비효율적으로 실패하게 만듭니다.

단순한 데이터 검색만으로 충분하지 않을 때 이러한 명령어를 사용하십시오. 선택적 필터로 시작하고, 필요한 필드만 프로젝션한 다음, 그룹화, 재구성 또는 컬렉션 간 보강이 필요할 때 집계로 이동하십시오.