MongoDB 배열 쿼리

MongoDBBeginner
지금 연습하기

소개

이 랩에서는 MongoDB 에서 배열을 효과적으로 쿼리하는 방법을 배우게 됩니다. 배열은 MongoDB 의 기본적인 데이터 구조로, 단일 문서 필드 내에 값 목록을 저장할 수 있게 해줍니다. 이 랩에서는 단순한 요소 일치부터 특수 연산자를 사용한 더 복잡한 쿼리까지, 배열 데이터를 다루는 필수적인 기법들을 안내할 것입니다. 배열 내용, 크기 및 요소 위치를 기반으로 문서를 찾는 방법을 배우게 되며, 이는 MongoDB 를 다루는 모든 개발자에게 중요한 기술입니다.

시작하기 및 기본 배열 일치

첫 번째 단계에서는 MongoDB 서버에 연결하고, 데이터베이스를 생성한 다음 샘플 데이터를 삽입합니다. 그런 다음 배열 내의 요소를 일치시켜 문서를 찾기 위한 기본 쿼리를 수행합니다.

먼저 MongoDB Shell (mongosh) 을 열어 데이터베이스와 상호 작용합니다. 이 명령은 실행 중인 MongoDB 인스턴스에 연결합니다.

mongosh

쉘에 들어가면 test> 프롬프트가 표시됩니다. arraylab이라는 새 데이터베이스로 전환해 보겠습니다. use 명령은 데이터베이스가 존재하지 않으면 생성하고 현재 컨텍스트를 해당 데이터베이스로 전환합니다.

use arraylab

이제 products라는 새 컬렉션에 일부 문서를 삽입해 보겠습니다. 각 문서는 제품을 나타내며 tagscolors라는 두 개의 배열 필드를 포함합니다. 다음 명령을 복사하여 쉘에 붙여넣으세요.

db.products.insertMany([
  {
    name: "Laptop",
    tags: ["electronics", "computer", "work"],
    colors: ["silver", "black", "blue"]
  },
  {
    name: "Smartphone",
    tags: ["electronics", "mobile", "communication"],
    colors: ["red", "blue", "green"]
  },
  {
    name: "Headphones",
    tags: ["electronics", "audio", "music"],
    colors: ["black", "white"]
  }
]);

배열에 특정 요소가 포함된 문서를 찾으려면 해당 값을 직접 쿼리할 수 있습니다. 이 쿼리는 "mobile" 태그를 가진 모든 제품을 찾습니다.

db.products.find({ tags: "mobile" });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  }
]

요소의 정확한 시퀀스와 일치하는 배열을 가진 문서를 찾을 수도 있습니다. 이 쿼리는 tags 배열이 정확히 ["electronics", "computer", "work"]인 제품을 해당 특정 순서대로 찾습니다.

db.products.find({ tags: ["electronics", "computer", "work"] });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

다음 단계를 위해 mongosh 쉘에 계속 머물러도 됩니다. 언제든지 쉘을 종료하려면 exit를 입력하고 Enter 키를 누르세요.

배열 쿼리 연산자 $all$in 사용

단순 일치는 유용하지만, MongoDB 는 더 복잡한 배열 쿼리를 위한 강력한 연산자를 제공합니다. 이 단계에서는 순서에 상관없이 여러 요소를 일치시키는 $all과 요소 목록 중 하나라도 일치시키는 $in을 사용하는 방법을 배우게 됩니다.

이전 단계에서 사용했던 products 컬렉션을 계속 사용합니다.

$all 연산자는 배열 필드가 지정된 모든 요소를 포함하는 문서를 선택합니다. 쿼리에서 요소의 순서는 중요하지 않습니다. "blue"와 "silver" 색상 모두로 제공되는 제품을 찾아보겠습니다.

db.products.find({ colors: { $all: ["blue", "silver"] } });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

$in 연산자는 배열 필드가 지정된 값 중 하나 이상을 포함하는 문서를 선택합니다. 이는 배열 요소에 대한 "OR" 조건과 유사하게 작동합니다. "music" 태그 또는 "work" 태그를 가진 제품을 찾아보겠습니다.

db.products.find({ tags: { $in: ["music", "work"] } });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

이러한 연산자는 배열의 내용을 기반으로 문서를 검색할 때 훨씬 더 많은 유연성을 제공합니다.

배열 크기 및 위치별 쿼리

때로는 배열의 요소 수 또는 특정 요소의 위치가 중요합니다. 이 단계에서는 $size 연산자를 사용하여 배열 크기를 기준으로 쿼리하고 점 표기법 (dot notation) 을 사용하여 요소 위치별로 쿼리하는 방법을 다룹니다.

$size 연산자는 특정 수의 요소를 가진 배열을 포함하는 문서를 일치시킵니다. 사용 가능한 색상이 정확히 두 개인 제품을 찾아보겠습니다.

db.products.find({ colors: { $size: 2 } });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

배열의 특정 위치 (인덱스) 에 있는 요소를 쿼리하려면 "fieldName.index" 형식으로 점 표기법을 사용합니다. 배열 인덱스는 0 부터 시작하므로 첫 번째 요소는 인덱스 0 에 있다는 점을 기억하세요. 이 쿼리는 tags 배열의 첫 번째 요소가 "electronics"인 모든 제품을 찾습니다.

db.products.find({ "tags.0": "electronics" });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Smartphone',
    tags: [ 'electronics', 'mobile', 'communication' ],
    colors: [ 'red', 'blue', 'green' ]
  },
  {
    _id: ObjectId("..."),
    name: 'Headphones',
    tags: [ 'electronics', 'audio', 'music' ],
    colors: [ 'black', 'white' ]
  }
]

이제 두 번째 색상 (인덱스 1) 이 "black"인 제품을 찾아보겠습니다.

db.products.find({ "colors.1": "black" });

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    colors: [ 'silver', 'black', 'blue' ]
  }
]

내장 문서 배열 쿼리

MongoDB 에서 일반적이고 강력한 패턴은 다른 문서를 포함하는 배열을 갖는 것입니다. 이는 포함된 문서 또는 중첩된 문서로 알려져 있습니다. 이러한 문서를 쿼리하려면 모든 조건이 동일한 배열 요소에 적용되도록 보장하는 특수 연산자인 $elemMatch가 필요합니다.

먼저 specs 배열을 가진 새로운 제품을 추가해 보겠습니다. specs 배열의 각 요소는 ram, storage, price 필드를 가진 문서입니다.

db.products.insertMany([
  {
    name: "Gaming Laptop",
    tags: ["electronics", "computer", "gaming"],
    specs: [
      { ram: 16, storage: 512, price: 1200 },
      { ram: 32, storage: 1024, price: 2000 }
    ]
  },
  {
    name: "Office Laptop",
    tags: ["electronics", "computer", "work"],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]);

이제 RAM 이 최소 16GB 이고 가격이 1000 달러 미만인 구성을 가진 노트북을 찾고 싶다고 가정해 보겠습니다. 단순한 쿼리는 올바르게 보일 수 있지만 잘못된 결과로 이어질 수 있습니다. 예를 들어, db.products.find({ "specs.ram": { $gte: 16 }, "specs.price": { $lt: 1000 } })는 하나의 포함된 문서가 충분한 RAM 을 가지고 있고 다른 문서가 충분히 낮은 가격을 가지고 있다면 문서를 일치시킬 것입니다.

배열의 단일 포함된 문서가 모든 조건을 충족하도록 하려면 $elemMatch 연산자를 사용해야 합니다.

db.products.find({
  specs: {
    $elemMatch: { ram: { $gte: 16 }, price: { $lt: 1000 } }
  }
});

이 쿼리는 "Office Laptop"만 올바르게 찾습니다. 왜냐하면 이 노트북은 두 조건을 동시에 충족하는 사양 요소 { ram: 16, storage: 512, price: 800 }를 가지고 있기 때문입니다.

예시 출력:

[
  {
    _id: ObjectId("..."),
    name: 'Office Laptop',
    tags: [ 'electronics', 'computer', 'work' ],
    specs: [
      { ram: 8, storage: 256, price: 600 },
      { ram: 16, storage: 512, price: 800 }
    ]
  }
]

배열 필드 프로젝션

종종 쿼리에서 전체 문서를 반환할 필요가 없을 수 있습니다. 투영 (Projection) 을 사용하면 포함하거나 제외할 필드를 지정할 수 있습니다. 배열의 경우 MongoDB 는 반환할 배열 요소를 제어하기 위해 $slice와 같은 특수 투영 연산자를 제공합니다.

$slice 투영 연산자는 배열의 하위 집합을 반환합니다. 양수를 지정하여 처음 N 개의 요소를 가져오거나 음수를 지정하여 마지막 N 개의 요소를 가져올 수 있습니다.

모든 제품을 검색하되, 각 제품에 대해 name과 처음 두 개의 tags만 반환해 보겠습니다. find의 첫 번째 인수인 {}는 모든 문서를 일치시키는 빈 쿼리 문서입니다. 두 번째 인수는 투영 문서입니다. 기본 _id 필드를 제외하기 위해 _id: 0을 사용합니다.

db.products.find({}, { name: 1, tags: { $slice: 2 }, _id: 0 });

예시 출력:

[
  { "name": "Laptop", "tags": ["electronics", "computer"] },
  { "name": "Smartphone", "tags": ["electronics", "mobile"] },
  { "name": "Headphones", "tags": ["electronics", "audio"] },
  { "name": "Gaming Laptop", "tags": ["electronics", "computer"] },
  { "name": "Office Laptop", "tags": ["electronics", "computer"] }
]

또 다른 유용한 투영 연산자는 위치 지정자 $ 연산자입니다. 배열을 쿼리할 때 이 연산자는 투영된 배열 필드에서 쿼리 조건을 일치시킨 첫 번째 요소만 반환하도록 보장합니다.

"mobile" 태그가 있는 제품을 찾고 tags 배열에서 해당 특정 일치 태그와 name만 반환해 보겠습니다.

db.products.find({ tags: "mobile" }, { name: 1, "tags.$": 1, _id: 0 });

예시 출력:

[{ "name": "Smartphone", "tags": ["mobile"] }]

이는 애플리케이션에서 전체 배열을 처리할 필요 없이 대규모 배열의 관련 부분만 검색하는 데 매우 효율적입니다.

요약

이 실습에서는 MongoDB 에서 배열을 쿼리하는 기본적인 기술을 배웠습니다. 기본적인 요소 일치부터 시작하여 $all, $in, $size와 같은 강력한 배열 연산자를 사용하는 방법을 익혔습니다. 또한 요소의 위치별 쿼리 방법과 $elemMatch를 사용하여 포함된 문서의 배열을 올바르게 쿼리하는 방법을 탐구했습니다. 마지막으로 $slice와 위치 지정자 $ 연산자를 사용하여 특정 배열 요소를 투영함으로써 쿼리 출력을 모양화하는 방법을 배웠습니다. 이러한 기술은 MongoDB 의 복잡한 문서 구조에서 데이터를 효과적으로 관리하고 검색하는 데 필수적입니다.