문서 메뉴

문서 홈애플리케이션 개발Atlas Device SDK

MongoDB 쿼리 - React Native SDK

이 페이지의 내용

  • 사용 사례
  • 전제 조건
  • 연결된 클러스터에 연결하기
  • 읽기 작업
  • 단일 문서 찾기
  • 여러 문서 찾기
  • 문서 수 계산
  • 쓰기 작업
  • 단일 문서 삽입
  • 여러 문서를 삽입합니다.
  • 단일 문서 업데이트
  • 여러 문서 업데이트하기
  • 문서 업서트
  • 단일 문서 삭제
  • 여러 문서 삭제
  • 실시간 변경 알림
  • 컬렉션의 모든 변경사항 보기
  • 컬렉션의 특정 변경사항 보기
  • 집계 작업
  • Aggregation pipeline실행

쿼리 API 가 포함된 Realm React Native SDK의 MongoDB 클라이언트 를 사용하여 클라이언트 애플리케이션 코드에서 직접 MongoDB Atlas에 저장된 데이터를 쿼리할 수 있습니다. Atlas App Services는 로그인한 사용자 또는 각 문서의 콘텐츠를 기반으로 결과를 안전하게 검색할 수 있도록 컬렉션에 대한 데이터 액세스 규칙 을 제공합니다.

참고

예제 데이터 세트

이 페이지의 예제에서는 식물 매장 체인의 재고를 설명하는 MongoDB 컬렉션을 사용합니다. 컬렉션 스키마 및 문서 콘텐츠에 대한 자세한 내용은 예제 데이터를 참조하세요.

MongoDB 데이터 소스를 쿼리해야 하는 이유는 여러 가지가 있습니다. Atlas Device Sync를 통해 클라이언트의 데이터로 작업하는 것이 항상 실용적이거나 가능한 것은 아닙니다. 다음과 같은 경우에는 MongoDB를 쿼리해야 할 수 있습니다.

  • 데이터 세트의 용량이 크거나, 클라이언트 디바이스로 데이터 세트 전체를 로드하는 데 제약이 있을 경우

  • 사용자 지정 사용자 데이터 생성 또는 업데이트를 실행하려는 경우

  • Realm에서 모델링되지 않은 문서를 검색하려는 경우

  • 앱으로 엄격한 스키마가 없는 컬렉션에 액세스해야 하는 경우

  • 비Realm 서비스를 통해 액세스하고자 하는 컬렉션을 생성할 경우

다음 내용은 MongoDB를 직접 쿼리할 수 있는 일반적인 사용 사례입니다. 단, 해당 사례들이 전부는 아닙니다.

React Native 애플리케이션에서 MongoDB를 쿼리하려면 먼저 App Services 앱에서 MongoDB 데이터 액세스를 설정해야 합니다. Realm SDK가 Atlas를 쿼리할 수 있도록 백엔드 앱을 설정하는 방법을 알아보려면 App Services 문서에서 MongoDB 데이터 액세스 설정 을 참조하세요.

예제

데이터 예시

이 페이지의 예제에서는 식물 매장 체인에서 판매되는 다양한 식물을 설명하는 다음 MongoDB 컬렉션을 사용합니다.

{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8c5"), name: "sweet basil", sunlight: "partial", color: "green", type: "annual", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8c6"), name: "thai basil", sunlight: "partial", color: "green", type: "perennial", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8c7"), name: "helianthus", sunlight: "full", color: "yellow", type: "annual", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8c8"), name: "petunia", sunlight: "full", color: "purple", type: "annual", _partition: "Store 47" }

plants 컬렉션의 문서들은 다음 스키마를 사용합니다.

클라이언트 애플리케이션에서 연결된 클러스터에 액세스하려면 사용자를 인증하고 클러스터 이름을 User.mongoClient()에 전달합니다. 이렇게 하면 클러스터의 데이터베이스 및 컬렉션에 액세스할 때 사용할 수 있는 MongoDB 서비스 인터페이스가 반환됩니다.

@realm/react를 사용하는 경우 UserProvider로 래핑된 구성 요소의 useUser() 후크를 사용하여 MongoDB 클라이언트에 액세스할 수 있습니다.

단일 문서를 찾으려면 해당 문서와 일치하는 쿼리를 collection.findOne() 에 전달합니다. 쿼리를 전달하지 않으면 findOne() 이(가) 컬렉션에서 찾은 첫 번째 문서와 일치합니다.

다음 스니펫 은 매장 그룹의 판매 대상 식물을 설명하는 문서 컬렉션에서 '파리지옥(venus flytrap)' 식물을 설명하는 문서를 찾습니다.

const venusFlytrap = await plants.findOne({ name: "venus flytrap" });
console.log("venusFlytrap", venusFlytrap);
{
_id: ObjectId("5f87976b7b800b285345a8c4"),
name: "venus flytrap",
sunlight: "full",
color: "white",
type: "perennial",
_partition: "Store 42",
}

여러 문서를 찾으려면 해당 문서와 일치하는 쿼리를 collection.find() 에 전달합니다. 쿼리를 전달하지 않으면 find() 이(가) 컬렉션의 모든 문서와 일치합니다.

다음 스니펫 은 매장 그룹의 판매 대상 식물을 설명하는 문서 컬렉션에서 다년생 식물을 설명하는 모든 문서를 찾습니다.

const perennials = await plants.find({ type: "perennial" });
console.log("perennials", perennials);
[
{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: 'venus flytrap', sunlight: 'full', color: 'white', type: 'perennial', _partition: 'Store 42' },
{ _id: ObjectId("5f87976b7b800b285345a8c6"), name: 'thai basil', sunlight: 'partial', color: 'green', type: 'perennial', _partition: 'Store 42' },
{ _id: ObjectId("5f879f83fc9013565c23360e"), name: 'lily of the valley', sunlight: 'full', color: 'white', type: 'perennial', _partition: 'Store 47' },
{ _id: ObjectId("5f87a0defc9013565c233611"), name: 'rhubarb', sunlight: 'full', color: 'red', type: 'perennial', _partition: 'Store 47' },
{ _id: ObjectId("5f87a0dffc9013565c233612"), name: 'wisteria lilac', sunlight: 'partial', color: 'purple', type: 'perennial', _partition: 'Store 42' },
{ _id: ObjectId("5f87a0dffc9013565c233613"), name: 'daffodil', sunlight: 'full', color: 'yellow', type: 'perennial', _partition: 'Store 42' }
]

문서 수를 계산하려면 해당 문서들과 일치하는 쿼리를 collection.count()( 으)로 전달하세요. 쿼리를 전달하지 않으면 count() 이(가) 컬렉션의 모든 문서 수를 계산합니다.

다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션의 문서 수를 계산합니다.

const numPlants = await plants.count();
console.log(`There are ${numPlants} plants in the collection`);
"There are 9 plants in the collection"

단일 문서를 삽입하려면 collection.insertOne()에 전달합니다.

다음 스니펫 은 매장 그룹의 판매 대상 식물을 설명하는 문서 모음에 '은방울꽃' 식물을 설명하는 단일 문서를 삽입합니다.

const result = await plants.insertOne({
name: "lily of the valley",
sunlight: "full",
color: "white",
type: "perennial",
_partition: "Store 47",
});
console.log(result);
{
insertedId: "5f879f83fc9013565c23360e",
}

여러 문서를 동시에 삽입하려면 해당 문서를 배열로 collection.insertMany()에 전달합니다.

다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음에 식물을 설명하는 문서 3개를 삽입합니다.

const result = await plants.insertMany([
{
name: "rhubarb",
sunlight: "full",
color: "red",
type: "perennial",
_partition: "Store 47",
},
{
name: "wisteria lilac",
sunlight: "partial",
color: "purple",
type: "perennial",
_partition: "Store 42",
},
{
name: "daffodil",
sunlight: "full",
color: "yellow",
type: "perennial",
_partition: "Store 42",
},
]);
console.log(result);
{
insertedIds: [
"5f87a0defc9013565c233611",
"5f87a0dffc9013565c233612",
"5f87a0dffc9013565c233613",
],
}

단일 문서를 업데이트하려면 해당 문서 및 업데이트 문서와 일치하는 쿼리를 collection.updateOne()(으)로 전달하세요.

다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션 에서 단일 문서를 업데이트합니다. 이 작업은 name 필드에 'petunia' 값이 포함된 문서를 쿼리하고 첫 번째 일치하는 문서의 sunlight 필드 값을 'partial'로 변경합니다.

const result = await plants.updateOne(
{ name: "petunia" },
{ $set: { sunlight: "partial" } }
);
console.log(result);
{ matchedCount: 1, modifiedCount: 1 }

여러 문서를 동시에 업데이트하려면 해당 문서 및 업데이트 설명과 일치하는 쿼리를 collection.updateMany()(으)로 전달하세요.

다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음 에서 여러 문서를 업데이트합니다. 이 작업은 _partition 필드에 'Store 47' 값이 포함된 문서를 쿼리하고 일치하는 각 문서의 _partition 필드 값을 'Store 51'로 변경합니다.

const result = await plants.updateMany(
{ _partition: "Store 47" },
{ $set: { _partition: "Store 51" } }
);
console.log(result);
{ matchedCount: 3, modifiedCount: 3 }

문서를 업서트하려면 업데이트 작업에서 upsert 옵션을 true(으)로 설정하세요. 이 작업의 쿼리가 컬렉션의 어떤 문서와도 일치하지 않는 경우, 업서트는 새 문서 1개를 업데이트가 적용된 상태로 제공된 쿼리 문서와 일치하는 컬렉션에 자동으로 삽입합니다.

다음 스니펫은 매장 그룹의 판매 대상 식물을 설명하는 문서 컬렉션에서 업서트 작업으로 문서를 업데이트합니다. 이 쿼리가 기존 문서와 일치하지 않으므로, MongoDB는 새 문서를 자동으로 생성합니다.

const result = await plants.updateOne(
{
sunlight: "full",
type: "perennial",
color: "green",
_partition: "Store 47",
},
{ $set: { name: "sweet basil" } },
{ upsert: true }
);
console.log(result);
{
matchedCount: 0,
modifiedCount: 0,
upsertedId: ObjectId("5f1f63055512f2cb67f460a3"),
}

컬렉션에서 문서를 1개 삭제하려면 해당 문서와 일치하는 쿼리를 collection.deleteOne()(으)로 전달하세요. 쿼리를 전달하지 않았거나 쿼리가 여러 문서와 일치하면 해당 작업에서 최초로 발견된 문서가 삭제됩니다.

다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음에서 문서 하나를 삭제합니다. 이 작업은 color 필드 값이 '녹색'인 문서를 쿼리하고 일치하는 첫 번째 문서를 삭제합니다.

const result = await plants.deleteOne({ color: "green" });
console.log(result);
{ deletedCount: 1 }

컬렉션에서 여러 문서를 삭제하려면 해당 문서와 일치하는 쿼리를 collection.deleteMany()( 으)로 전달하세요. 쿼리를 전달하지 않으면 deleteMany() 이(가) 컬렉션의 모든 문서를 삭제합니다.

다음 스니펫 은 매장 그룹의 판매 대상 식물을 설명하는 문서 컬렉션에서 'Store 51'에 있는 식물에 대한 모든 문서를 삭제합니다.

const result = await plants.deleteMany({
_partition: "Store 51",
});
console.log(result);
{ deletedCount: 3 }

collection.watch() 를 호출하여 컬렉션의 문서가 추가, 수정 또는 삭제될 때마다 MongoDB가 보내는 실시간 변경 알림을 구독할 수 있습니다. 각 알림은 변경된 문서, 변경 방법, 이벤트를 일으킨 작업 이후의 전체 문서를 지정합니다.

collection.watch() 비동기 생성기 를 반환합니다. 이를 통해 작업이 발생할 때 작업에 대한 변경 이벤트 를 비동기적으로 가져올 수 있습니다.

collection.watch() React Native 클라이언트 앱과 함께 작동하려면 몇 가지 설정이 필요합니다. 컬렉션의 변경 사항을 관찰하려면 먼저 react-native-polyfill-globals를 설치해야 합니다.및 @babel/plugin-proposal-async-generator-functions 패키지.

collection.watch()를 사용하려면:

  1. 종속 요소를 설치합니다.

    npm install react-native-polyfill-globals text-encoding
    npm install --save-dev @babel/plugin-proposal-async-generator-functions
  2. 사용해야 하는 위치보다 더 높은 범위에서 폴리필을 가져옵니다. 예를 들어 index.js이 있습니다.

    import { polyfill as polyfillReadableStream } from "react-native-polyfill-globals/src/readable-stream";
    import { polyfill as polyfillEncoding } from "react-native-polyfill-globals/src/encoding";
    import { polyfill as polyfillFetch } from "react-native-polyfill-globals/src/fetch";
    polyfillReadableStream();
    polyfillEncoding();
    polyfillFetch();

중요

서버리스 제한 사항

데이터 원본이 Atlas 서버리스 인스턴스인 경우 변경 사항을 모니터링할 수 없습니다. MongoDB 서버리스는 현재 변경 스트림을 지원하지 않습니다. 변경 스트림은 감시되는 컬렉션에서 변경 사항을 수신하는 데 사용됩니다.

@realm/react 패키지를 사용하는 경우 앱에 대한 사용자 인증을 구성했는지 확인합니다.

컬렉션의 모든 변경 사항을 감시하려면 인수 없이 collection.watch() 를 호출합니다. 이 호출은 인증된 사용자와 함께 UserProvider 로 래핑해야 합니다.

컬렉션의 특정 변경 사항을 감시하려면 변경 이벤트 필드와 일치하는 쿼리를 collection.watch()에 전달합니다.

for await (const change of plants.watch({
filter: {
operationType: "insert",
"fullDocument.type": "perennial",
},
})) {
// The change event will always represent a newly inserted perennial
const { documentKey, fullDocument } = change;
console.log(`new document: ${documentKey}`, fullDocument);
}

집계 작업은 집계 파이프라인 이라는 일련의 단계를 통해 컬렉션의 모든 문서를 실행합니다. 애그리게이션을 사용하면 문서를 필터링 및 변환하고, 관련 문서 그룹에 대한 요약 데이터를 수집하는 등의 복잡한 데이터 작업을 수행할 수 있습니다.

집계 파이프라인을 실행하려면 집계 단계 배열을 collection.aggregate()(으)로 전달하세요. 집계 작업에서는 이 파이프라인 내 마지막 단계의 결과 세트가 반환됩니다.

다음 스니펫은 type 값을 기준으로 plants 컬렉션의 모든 문서를 그룹화하고 각 유형의 개수를 집계합니다.

const result = await plants.aggregate([
{
$group: {
_id: "$type",
total: { $sum: 1 },
},
},
{ $sort: { _id: 1 } },
]);
console.log(result);
[
{ _id: "annual", total: 1 },
{ _id: "perennial", total: 5 },
]

$match 단계를 사용하여 표준 MongoDB 쿼리 구문에 따라 문서를 필터링할 수 있습니다.

{
"$match": {
"<Field Name>": <Query Expression>,
...
}
}

예제

다음 $match 단계는 type 필드 값이 '다년생'인 문서만 포함하도록 필터링합니다.

const perennials = await plants.aggregate([
{ $match: { type: { $eq: "perennial" } } },
]);
console.log(perennials);

$group 단계를 사용하여 하나 이상의 문서에 대한 요약 데이터를 집계할 수 있습니다. MongoDB는 $group 단계의 _id 필드에 정의된 표현식을 기반으로 문서를 그룹화합니다. 필드 이름 앞에 $ 를 붙여 특정 문서 필드를 참조할 수 있습니다.

{
"$group": {
"_id": <Group By Expression>,
"<Field Name>": <Aggregation Expression>,
...
}
}

예제

다음 $group 단계에서는 type 필드 값을 기준으로 문서를 정렬하고 각 고유 type 값이 나타나는 식물 문서 수를 계산합니다.

const result = await plants.aggregate([
{
$group: {
_id: "$type",
numItems: { $sum: 1 },
},
},
{ $sort: { _id: 1 } },
]);
console.log(result);

결과를 페이지로 나누려면 $match, $sort$limit 연산자와 함께 범위 집계 쿼리를 사용할 수 있습니다. 문서 페이지 매김에 대해 자세히 알아보려면 MongoDB Server 문서에서 범위 쿼리 사용하기를 참조하세요.

예제

다음 예제에서는 문서 컬렉션을 오름차순으로 페이지를 매깁니다.

// Paginates through list of plants
// in ascending order by plant name (A -> Z)
async function paginateCollectionAscending(
collection,
nPerPage,
startValue
) {
const pipeline = [{ $sort: { name: 1 } }, { $limit: nPerPage }];
// If not starting from the beginning of the collection,
// only match documents greater than the previous greatest value.
if (startValue !== undefined) {
pipeline.unshift({
$match: {
name: { $gt: startValue },
},
});
}
const results = await collection.aggregate(pipeline);
return results;
}
// Number of results to show on each page
const resultsPerPage = 3;
const pageOneResults = await paginateCollectionAscending(
plants,
resultsPerPage
);
const pageTwoStartValue = pageOneResults[pageOneResults.length - 1].name;
const pageTwoResults = await paginateCollectionAscending(
plants,
resultsPerPage,
pageTwoStartValue
);
// ... can keep paginating for as many plants as there are in the collection

$project 단계를 사용하여 문서의 특정 필드를 포함 또는 생략하거나 애그리게이션 연산자 를 사용하여 새 필드를 계산할 수 있습니다. 프로젝션은 두 가지 방식으로 작동합니다.

  • 값이 '1'인 필드를 명시적으로 포함합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 제외하는 부작용이 있습니다.

  • 값이 '0'인 필드를 암시적으로 제외합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 포함하는 부작용이 있습니다.

이 두 가지 프로젝션 방법은 상호 배타적입니다. 필드를 명시적으로 포함하는 경우 필드를 명시적으로 제외할 수 없으며 그 반대의 경우도 마찬가지입니다.

참고

_id 필드는 특별한 경우에 해당하며 명시적으로 지정하지 않는 한 항상 모든 쿼리에 포함됩니다. 따라서 0 값이 있는 _id 필드를 제외하는 동시에 1을 사용해 _partition 등의 다른 필드를 포함하는 것이 가능합니다. _id 필드를 제외하는 특수한 경우에만 하나의 $project 단계에서 제외와 포함을 동시에 적용할 수 있습니다.

{
"$project": {
"<Field Name>": <0 | 1 | Expression>,
...
}
}

예제

다음 $project 단계에서는 _id 필드를 생략하고 name 필드를 포함하며 storeNumber라는 새 필드를 생성합니다. storeNumber는 다음 집계 연산자 두 개를 사용하여 생성됩니다.

  1. $split_partition을 공백 문자를 둘러싼 두 개의 문자열 세그먼트로 분리합니다. 예를 들어 이처럼 '매장 42'로 분할된 값은 다음과 같이 '매장' 및 '42'의 두 개의 요소가 있는 배열을 반환합니다.

  2. $arrayElemAt 두 번째 인수를 기반으로 배열에서 특정 요소를 선택합니다. 이 경우 배열 인덱스는 0이므로 값 1$split 연산자로 생성된 배열에서 두 번째 요소를 선택합니다. 예를 들어 이 연산에 전달된 값 ['매장', '42']는 값 '42'를 반환합니다.

const result = await plants.aggregate([
{
$project: {
_id: 0,
name: 1,
storeNumber: {
$arrayElemAt: [{ $split: ["$_partition", " "] }, 1],
},
},
},
]);
console.log(result);

$addFields 단계에서 애그리게이션 연산자를 사용하여 계산된 값이 있는 새 필드를 추가할 수 있습니다.

{ $addFields: { <newField>: <expression>, ... } }

참고

$addFields$project와 유사하지만 필드를 포함하거나 생략할 수 없습니다.

예제

다음 $addFields 단계에서는 storeNumber라는 새 필드를 만듭니다. 여기서 값은 _partition 필드의 값을 변환하는 두 집계 연산자의 출력입니다.

const result = await plants.aggregate([
{
$addFields: {
storeNumber: {
$arrayElemAt: [{ $split: ["$_partition", " "] }, 1],
},
},
},
]);
console.log(result);

$unwind 단계를 사용하여 배열이 포함된 단일 문서를 해당 배열의 개별 값이 포함된 여러 문서로 변환할 수 있습니다. 배열 필드를 풀면 MongoDB는 배열 필드의 각 요소에 대해 각 문서를 한 번씩 복사하지만 배열 값을 각 사본의 배열 요소로 바꿉니다.

{
$unwind: {
path: <Array Field Path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}
}

예제

다음 예제에서는 각 객체의 typecolor 조합에 대해 $unwind 단계를 사용합니다. 집계 파이프라인은 다음과 같은 단계로 이루어져 있습니다.

  1. $addToSet를 포함해 $group 단계를 사용하여 컬렉션에 있는 해당 꽃 유형의 모든 색상 배열을 포함하는 새 필드 colors를 사용하여 각 type별로 새 문서를 생성합니다.

  2. $unwind 단계를 사용하여 각 유형 및 색상 조합에 대해 별도의 문서를 생성합니다.

  3. $sort 단계를 사용하여 결과를 알파벳 순서로 정렬합니다.

const result = await plants.aggregate([
{ $group: { _id: "$type", colors: { $addToSet: "$color" } } },
{ $unwind: { path: "$colors" } },
{ $sort: { _id: 1, colors: 1 } },
]);
console.log(result);
← 함수 호출 - React Native SDK