MongoDB 쿼리 - Java SDK
이 페이지의 내용
- 사용 사례
- 전제 조건
- 프로젝트 설정
- 프로젝트 설정
- Mongo DB Atlas 서비스 클러스터 연결하기
- Realm 종속성 가져오기
- MongoDB 컬렉션 핸들 인스턴스화하기
- 데이터 예시
- 문서 만들기
- 단일 문서 삽입
- 여러 문서를 삽입합니다.
- 문서 읽기
- 단일 문서 찾기
- 여러 문서 찾기
- collection의 문서 수 계산
- 문서 업데이트
- 단일 문서 업데이트
- 여러 문서 업데이트하기
- 문서 업서트
- 문서 삭제
- 단일 문서 삭제
- 여러 문서 삭제
- 변화를 주시하세요
- 컬렉션의 모든 변경 사항 보기
- 필터로 컬렉션의 변경 사항 보기
- collection의 문서 애그리게이션
- 문서 필터링
- 문서 그룹화
- 문서 필드 프로젝트
- 문서에 필드 추가
- 배열 값 풀기
쿼리 API 와 함께 Realm Java SDK의 MongoClient 를 사용하여 Android 애플리케이션 코드에서 직접 MongoDB Atlas 에 저장된 데이터를 쿼리 할 수 있습니다. Atlas App Services 는 로그인한 사용자 또는 각 문서 의 콘텐츠를 기반으로 결과를 안전하게 조회 할 수 있도록 컬렉션에 대한 데이터 액세스 규칙 을 제공합니다.
다음 작업을 수행하면 MongoDB Atlas cluster Realm SDK를 사용하여 Android 애플리케이션에서 연결된 에 액세스할 수 있습니다.
참고
이 페이지에 설명된 각 작업은 쿼리 를 사용하여 작업이 실행되는 컬렉션의 특정 문서를 일치시킵니다. 필터가 컬렉션의 여러 문서와 일치하면 정렬 매개 변수를 지정하지 않는 한 확정되지 않은 순서 로 반환됩니다. 즉, findOne()
, updateOne()
또는 deleteOne()
함수에 대해 정렬을 지정하지 않으면 쿼리와 일치하는 모든 문서와 작업이 일치할 수 있습니다. 정렬에 대한 자세한 내용은 cursor.sort()를 참조하세요.
사용 사례
MongoDB 데이터 소스를 쿼리해야 하는 이유는 여러 가지가 있습니다. Atlas Device Sync를 통해 클라이언트의 데이터로 작업하는 것이 항상 실용적이거나 가능한 것은 아닙니다. 다음과 같은 경우에는 MongoDB를 쿼리해야 할 수 있습니다.
데이터 세트의 용량이 크거나, 클라이언트 디바이스로 데이터 세트 전체를 로드하는 데 제약이 있을 경우
사용자 지정 사용자 데이터 생성 또는 업데이트를 실행하려는 경우
Realm에서 모델링되지 않은 문서를 검색하려는 경우
앱으로 엄격한 스키마가 없는 컬렉션에 액세스해야 하는 경우
비Realm 서비스를 통해 액세스하고자 하는 컬렉션을 생성할 경우
다음 내용은 MongoDB를 직접 쿼리할 수 있는 일반적인 사용 사례입니다. 단, 해당 사례들이 전부는 아닙니다.
전제 조건
Android 애플리케이션에서 MongoDB를 쿼리하려면 먼저 App Services App에서 MongoDB 데이터 액세스를 설정해야 합니다. Realm SDK가 Atlas 를 쿼리할 수 있도록 백엔드 앱을 설정하는 방법을 알아보려면 Atlas 앱 서비스 문서에서 MongoDB 데이터 액세스 설정 을 참조하세요.
프로젝트 설정
프로젝트 설정
Realm Java SDK 설치 가이드의 단계를 따르세요.
Mongo DB Atlas 서비스 클러스터 연결하기
MongoDB 데이터 소스 연결 가이드의 단계를 따르세요. 서비스에 의미 있는 이름을 지정합니다. Realm SDK를 사용하여 클러스터에 연결하는 데 필요합니다.
Realm 종속성 가져오기
원격 MongoDB 컬렉션에 대한 CRUD 작업의 경우 다음 import
문 중 하나 이상을 사용합니다.
// Base Realm Packages import io.realm.mongodb.App; import io.realm.mongodb.AppConfiguration; // Realm Authentication Packages import io.realm.mongodb.User; import io.realm.mongodb.Credentials; // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient; import io.realm.mongodb.mongo.MongoDatabase; import io.realm.mongodb.mongo.MongoCollection; // Utility Packages import org.bson.Document;
// Base Realm Packages import io.realm.mongodb.App import io.realm.mongodb.AppConfiguration // Realm Authentication Packages import io.realm.mongodb.User import io.realm.mongodb.Credentials // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient import io.realm.mongodb.mongo.MongoDatabase import io.realm.mongodb.mongo.MongoCollection // Utility Packages import org.bson.Document
MongoDB 컬렉션 핸들 인스턴스화하기
MongoDB 인스턴스에 연결하려면 MongoDB 컬렉션에 대한 액세스 권한이 있는 사용자가 필요합니다. 이러한 사용자로 애플리케이션에 로그인 한 후 다음 코드를 사용하여 로컬 MongoDB 컬렉션 핸들을 인스턴스화합니다.
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle");
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle")
참고
MongoDB에서 사용자 지정 클래스 사용
MongoDB에서 기본 제공 Document
클래스 이외의 클래스를 사용하려면 MongoCollection
인스턴스에 코덱 을 추가하면 됩니다. 위의 예에서는 PojoCodecProvider
를 추가하여 POJO(Plain Old Java Objects)를 지원합니다. 사용자 지정 객체 지원에는 두 개의 코덱 제공자 가 필요합니다.
내장 Java 유형을 지원하는 기본 코덱 제공자(
AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY
를 통해 액세스)PojoCodecProvider
, POJO 클래스를 지원하는 새 코덱을 자동으로 생성합니다.
SDK는 요청된 클래스에 대한 코덱을 반환할 때까지 레지스트리를 순서대로 확인합니다. 따라서 기본 코덱 레지스트리를 먼저 나열해야 하며 거의 모든 클래스에 대한 코덱을 제공할 수 있는 PojoCodecProvider
이(가) 항상 마지막 CodecProvider여야 합니다.
데이터 예시
다음 예제는 식물 매장 체인의 재고를 설명하는 MongoDB 컬렉션에서 작동합니다. 매장에서 판매하는 다양한 식물을 설명하는 다음 문서 모음을 가정해 보겠습니다.
import org.bson.codecs.pojo.annotations.BsonProperty; import org.bson.types.ObjectId; public class Plant { private ObjectId id; private String name; private String sunlight; private String color; private String type; private String partition; // empty constructor required for MongoDB Data Access POJO codec compatibility public Plant() {} public Plant(ObjectId id, String name, String sunlight, String color, String type, String partition) { this.id = id; this.name = name; this.sunlight = sunlight; this.color = color; this.type = type; this.partition = partition; } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSunlight() { return sunlight; } public void setSunlight(String sunlight) { this.sunlight = sunlight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPartition() { return partition; } public void setPartition(String partition) { this.partition = partition; } public String toString() { return "Plant [id=" + id + ", name=" + name + ", sunlight=" + sunlight + ", color=" + color + ", type=" + type + ", partition=" + partition + "]"; } }
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); mongoCollection.insertMany(Arrays.asList( new Plant(new ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42"), new Plant(new ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42"), new Plant(new ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42"), new Plant(new ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42"), new Plant(new ObjectId(), "petunia", "full", "purple", "annual", "Store 47"))); Log.v("EXAMPLE", "Successfully inserted the sample data.");
import org.bson.codecs.pojo.annotations.BsonProperty import org.bson.types.ObjectId open class Plant(val id : ObjectId = ObjectId(), var name : String? = null, var sunlight : String? = null, var color : String? = null, var type : String? = null, "_partition") // specify that this is a field-level annotation ( var partition : String? = null) { override fun toString(): String { return "Plant [id=$id, name=$name, sunlight=$sunlight, color=$color, type=$type, partition=$partition]" } }
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) mongoCollection.insertMany( listOf( Plant( ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42" ), Plant( ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42" ), Plant( ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42" ), Plant( ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42" ), Plant( ObjectId(), "petunia", "full", "purple", "annual", "Store 47" ) ) ) Log.v("EXAMPLE", "Successfully Successfully inserted the sample data.")
문서 만들기
이 코드 스니펫은 모바일 애플리케이션에서 하나 이상의 문서를 MongoDB 컬렉션에 삽입하는 방법을 보여줍니다. 삽입 작업은 MongoDB에 추가할 문서를 인수로 받고 작업 실행 결과가 포함된 객체로 해석되는 RealmResultTask 를 반환합니다.
단일 문서 삽입
collection.insertOne()을 사용하여 단일 문서를 삽입할 수 있습니다.
다음 스니펫 은 매장 그룹의 판매 대상 식물을 설명하는 문서 모음에 '은방울꽃' 식물을 설명하는 단일 문서를 삽입합니다.
Plant plant = new Plant( new ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47"); mongoCollection.insertOne(plant).getAsync(task -> { if (task.isSuccess()) { Log.v("EXAMPLE", "successfully inserted a document with id: " + task.get().getInsertedId()); } else { Log.e("EXAMPLE", "failed to insert documents with: " + task.getError().getErrorMessage()); } });
val plant = Plant( ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47" ) mongoCollection?.insertOne(plant)?.getAsync { task -> if (task.isSuccess) { Log.v( "EXAMPLE", "successfully inserted a document with id: ${task.get().insertedId}" ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully inserted a document with id: BsonObjectId{value=5f19...}
여러 문서를 삽입합니다.
collection.insertMany()를 사용하여 여러 문서를 동시에 삽입할 수 있습니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음에 식물을 설명하는 문서 3개를 삽입합니다.
List<Plant> plants = Arrays.asList( new Plant(new ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47"), new Plant(new ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42"), new Plant(new ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42")); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val plants = listOf( Plant( ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47" ), Plant( ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42" ), Plant( ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v( "EXAMPLE", "successfully inserted $insertedCount documents into the collection." ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully inserted 3 documents into the collection.
문서 읽기
이 코드 스니펫은 MongoDB 컬렉션 에 저장된 데이터를 모바일 애플리케이션 에서 읽는 방법을 보여줍니다. 읽기 작업은 쿼리 를 사용하여 데이터베이스 에서 반환할 문서를 지정합니다. 읽기 작업은 작업 을 반환합니다. 일치하는 단일 문서 (findOne()
long
의 경우), 숫자 count()
값( 의 경우) 또는 일치하는 문서 컬렉션 find()
을 순회할 수 있는 반복자( 의 경우)로 해석됩니다. ).
단일 문서 찾기
collection.findOne()을 사용하여 하나의 문서를 찾을 수 있습니다.
다음 스니펫은 식물 문서의 type
필드에 string 'perennial' 값이 포함된 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션 에서 단일 문서를 찾습니다.
Document queryFilter = new Document("type", "perennial"); mongoCollection.findOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { Plant result = task.get(); Log.v("EXAMPLE", "successfully found a document: " + result); } else { Log.e("EXAMPLE", "failed to find document with: ", task.getError()); } });
val queryFilter = Document("type", "perennial") mongoCollection.findOne(queryFilter) .getAsync { task -> if (task.isSuccess) { val result = task.get() Log.v("EXAMPLE", "successfully found a document: $result") } else { Log.e("EXAMPLE", "failed to find document with: ${task.error}") } }
이 스니펫을 실행하면 다음과 유사한 출력이 생성됩니다.
V/EXAMPLE: successfully found a document: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42]
여러 문서 찾기
collection.find()를 사용하여 여러 문서를 찾을 수 있습니다.
다음 스니펫은 값이 'Store 42'인 _partition
필드를 포함하는 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션 에서 모든 문서를 찾습니다.
Document queryFilter = new Document("_partition", "Store 42"); RealmResultTask<MongoCursor<Plant>> findTask = mongoCollection.find(queryFilter).iterator(); findTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Plant> results = task.get(); Log.v("EXAMPLE", "successfully found all plants for Store 42:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to find documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 42") val findTask = mongoCollection.find(queryFilter).iterator() findTask.getAsync { task -> if (task.isSuccess) { val results = task.get() Log.v("EXAMPLE", "successfully found all plants for Store 42:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to find documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully found all plants for Store 42: V/EXAMPLE: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=sweet basil, sunlight=partial, color=green, type=annual, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=thai basil, sunlight=partial, color=green, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=helianthus, sunlight=full, color=yellow, type=annual, partition=Store 42]
collection의 문서 수 계산
collection.count() 를 사용하여 컬렉션의 문서 수를 계산할 수 있습니다. 선택적 쿼리를 지정하여 계산할 문서를 결정할 수 있습니다. 쿼리를 지정하지 않으면 작업은 컬렉션의 모든 문서 수를 계산합니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션의 문서 수를 계산합니다.
mongoCollection.count().getAsync(task -> { if (task.isSuccess()) { long count = task.get(); Log.v("EXAMPLE", "successfully counted, number of documents in the collection: " + count); } else { Log.e("EXAMPLE", "failed to count documents with: ", task.getError()); } });
mongoCollection.count().getAsync { task -> if (task.isSuccess) { val count = task.get() Log.v("EXAMPLE", "successfully counted, number of documents in the collection: $count") } else { Log.e("EXAMPLE", "failed to count documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully counted, number of documents in the collection: 5
문서 업데이트
이 코드 스니펫은 MongoDB 컬렉션에 저장된 데이터를 모바일 애플리케이션에서 업데이트하는 방법을 보여줍니다. 업데이트 작업은 쿼리를 사용하여 업데이트할 문서를 지정하고 업데이트 연산자 를 사용하여 쿼리와 일치하는 문서를 변경하는 방법을 설명합니다. 업데이트 작업은 작업 을 반환합니다. 작업 실행 결과를 포함하는 객체로 확인됩니다.
단일 문서 업데이트
collection.updateOne()을 사용하여 단일 문서를 업데이트할 수 있습니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션 에서 단일 문서를 업데이트합니다. 이 작업은 name
필드에 'petunia' 값이 포함된 문서를 쿼리하고 첫 번째 일치하는 문서의 sunlight
필드 값을 'partial'로 변경합니다.
Document queryFilter = new Document("name", "petunia"); Document updateDocument = new Document("$set", new Document("sunlight", "partial")); mongoCollection.updateOne(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully updated a document."); } else { Log.v("EXAMPLE", "did not update a document."); } } else { Log.e("EXAMPLE", "failed to update document with: ", task.getError()); } });
val queryFilter = Document("name", "petunia") val updateDocument = Document("\$set", Document("sunlight", "partial")) mongoCollection.updateOne(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count == 1L) { Log.v("EXAMPLE", "successfully updated a document.") } else { Log.v("EXAMPLE", "did not update a document.") } } else { Log.e("EXAMPLE", "failed to update document with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully updated a document.
여러 문서 업데이트하기
collection.updateMany()를 사용하여 여러 문서를 업데이트할 수 있습니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음 에서 여러 문서를 업데이트합니다. 이 작업은 _partition
필드에 'Store 47' 값이 포함된 문서를 쿼리하고 일치하는 각 문서의 _partition
필드 값을 'Store 51'로 변경합니다.
Document queryFilter = new Document("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("_partition", "Store 51")); mongoCollection.updateMany(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully updated " + count + " documents."); } else { Log.v("EXAMPLE", "did not update any documents."); } } else { Log.e("EXAMPLE", "failed to update documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 47") val updateDocument = Document("\$set", Document("_partition", "Store 51")) mongoCollection.updateMany(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count != 0L) { Log.v("EXAMPLE", "successfully updated $count documents.") } else { Log.v("EXAMPLE", "did not update any documents.") } } else { Log.e("EXAMPLE", "failed to update documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully updated 2 documents.
문서 업서트
업데이트 작업이 컬렉션의 문서와 일치하지 않는 경우 upsert
옵션을 true
로 설정하여 업데이트 쿼리와 일치하는 컬렉션에 새 문서 하나를 자동으로 삽입할 수 있습니다.
다음 스니펫은 매장 그룹의 판매 대상 식물을 설명하는 문서 컬렉션 문서를 업데이트하거나 쿼리와 일치하는 문서가 없는 경우 새 문서를 삽입합니다. 이 작업은 문서의 위치를 쿼리합니다:
sunlight
필드의 값은 "full입니다."type
필드의 값은 "perennial"입니다.color
필드의 값은 'green'입니다._partition
필드의 값은 'Store 47'입니다.
이 스니펫은 upsert
옵션을 true
로 설정하므로 쿼리와 일치하는 문서가 없으면 MongoDB는 쿼리와 지정된 업데이트를 모두 포함하는 새 문서를 만듭니다.
Document queryFilter = new Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("name", "sweet basil")); UpdateOptions updateOptions = new UpdateOptions().upsert(true); mongoCollection.updateOne(queryFilter, updateDocument, updateOptions).getAsync(task -> { if (task.isSuccess()) { if(task.get().getUpsertedId() != null) { Log.v("EXAMPLE", "successfully upserted a document with id " + task.get().getUpsertedId()); } else { Log.v("EXAMPLE", "successfully updated a document."); } } else { Log.e("EXAMPLE", "failed to update or insert document with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47") val updateDocument = Document("\$set", Document("name", "sweet basil")) val updateOptions = UpdateOptions().upsert(true) mongoCollection.updateOne(queryFilter, updateDocument, updateOptions) .getAsync { task -> if (task.isSuccess) { if (task.get().upsertedId != null) { Log.v("EXAMPLE", "successfully upserted a document with id ${task.get().upsertedId}") } else { Log.v("EXAMPLE", "successfully updated a document.") } } else { Log.e("EXAMPLE", "failed to update or insert document with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully upserted a document with id: BsonObjectId{value=5f19...}
문서 삭제
이 코드 스니펫은 MongoDB 컬렉션에 저장된 문서를 모바일 애플리케이션에서 삭제하는 방법을 보여줍니다. 삭제 작업은 쿼리를 사용하여 삭제할 문서를 지정하고 작업 을 반환합니다. 작업 실행 결과를 포함하는 객체로 확인됩니다.
단일 문서 삭제
collection.deleteOne()을 사용하여 컬렉션에서 단일 문서를 삭제할 수 있습니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 모음에서 문서 하나를 삭제합니다. 이 작업은 color
필드 값이 '녹색'인 문서를 쿼리하고 일치하는 첫 번째 문서를 삭제합니다.
Document queryFilter = new Document("color", "green"); mongoCollection.deleteOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully deleted a document."); } else { Log.v("EXAMPLE", "did not delete a document."); } } else { Log.e("EXAMPLE", "failed to delete document with: ", task.getError()); } });
val queryFilter = Document("color", "green") mongoCollection.deleteOne(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count == 1L) { Log.v("EXAMPLE", "successfully deleted a document.") } else { Log.v("EXAMPLE", "did not delete a document.") } } else { Log.e("EXAMPLE", "failed to delete document with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully deleted a document.
여러 문서 삭제
collection.deleteMany()를 사용하여 컬렉션에서 여러 항목을 삭제할 수 있습니다.
다음 스니펫 은 매장 그룹에서 판매할 식물을 설명하는 문서 컬렉션에서 sunlight
필드 값이 'full'이고 type
필드 값이 'annual'인 문서와 일치하는 모든 문서를 삭제합니다. ".
Document queryFilter = new Document("sunlight", "full") .append("type", "annual"); mongoCollection.deleteMany(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully deleted " + count + " documents."); } else { Log.v("EXAMPLE", "did not delete any documents."); } } else { Log.e("EXAMPLE", "failed to delete documents with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full").append("type", "annual") mongoCollection.deleteMany(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count != 0L) { Log.v("EXAMPLE", "successfully deleted $count documents.") } else { Log.v("EXAMPLE", "did not delete any documents.") } } else { Log.e("EXAMPLE", "failed to delete documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: succcessfully deleted 2 documents.
변화를 주시하세요
이 코드 스니펫은 컬렉션에서 감시 작업을 구성하고 실행하는 방법을 보여줍니다.
중요
서버리스 제한 사항
데이터 원본이 Atlas 서버리스 인스턴스인 경우 변경 사항을 모니터링할 수 없습니다. MongoDB 서버리스는 현재 변경 스트림을 지원하지 않습니다. 변경 스트림은 감시되는 컬렉션에서 변경 사항을 수신하는 데 사용됩니다.
컬렉션의 모든 변경 사항 보기
collection.watch() 또는 collection.watchAsync()를 호출하여 컬렉션에 대한 변경 사항 스트림을 열 수 있습니다. 모니터링하려는 객체의 객체 ID를 가변 개수의 인수로 전달하여 컬렉션의 특정 문서에 대한 변경 사항을 감시할 수 있습니다.
다음 스니펫은 plants
컬렉션의 모든 문서에 대한 변경 사항을 감시합니다.
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection.watchAsync(); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ", result.getError()); } }); Plant triffid = new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"); mongoCollection.insertOne(triffid).getAsync(task -> { if (task.isSuccess()) { BsonObjectId insertedId = task.get().getInsertedId().asObjectId(); Log.v("EXAMPLE", "successfully inserted a document with id " + insertedId); } else { Log.e("EXAMPLE", "failed to insert document with: ", task.getError()); } });
val watcher = mongoCollection.watchAsync() watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ${result.error}") } }] val triffid = Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ) mongoCollection.insertOne(triffid).getAsync { task -> if (task.isSuccess) { val insertedId = task.get().insertedId.asObjectId() Log.v("EXAMPLE", "successfully inserted a document with id $insertedId") } else { Log.e("EXAMPLE", "failed to insert document with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully inserted a document with id BsonObjectId{value=5f6bb...} V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=triffid, sunlight=low, color=green, type=perennial, partition=Store 47]
필터로 컬렉션의 변경 사항 보기
collection.watchWithFilter() 또는 collection.watchWithFilterAsync()를 호출하여 특정 기준을 충족하는 컬렉션의 문서에 적용된 변경 사항 스트림을 열 수 있습니다. 두 메서드 모두 Document
BsonDocument
컬렉션을 관찰하는 동안 발생하는 각 데이터베이스 이벤트 를 처리하기 위해 $match 연산자 의 쿼리로 사용되는 또는 매개 변수를 허용합니다.
다음 스니펫은 plants
컬렉션의 문서에 대한 변경 사항을 감시하지만 "Store 42"이라는 파티션에 속한 문서에 해당하는 이벤트에 대해 제공된 콜백만 Atlas 트리거합니다.
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection .watchWithFilterAsync(new Document("fullDocument._partition", "Store 42")); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ", result.getError()); } }); List<Plant> plants = Arrays.asList( new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"), new Plant( new ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" )); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val watcher = mongoCollection .watchWithFilterAsync(Document("fullDocument._partition", "Store 42")) watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ${result.error}") } }] val plants = listOf( Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ), Plant( ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v("EXAMPLE", "successfully inserted $insertedCount documents into the collection.") } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
이 스니펫을 실행하면 다음과 같은 출력이 생성됩니다:
V/EXAMPLE: successfully inserted 2 documents into the collection V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=venomous tentacula, sunlight=low, color=brown, type=annual, partition=Store 42]
collection의 문서 애그리게이션
집계 작업은 집계 파이프라인이라고 하는 일련의 데이터 집계 단계를 통해 컬렉션의 모든 문서를 실행합니다. 집계를 통해 문서 필터링 및 변환, 관련 문서 그룹에 대한 요약 데이터 수집 등의 복잡한 데이터 작업을 수행할 수 있습니다.
collection.aggregate()를 사용하여 집계 파이프라인을 실행할 수 있습니다.
애그리게이션 작업 은 애그리게이션 단계 목록을 입력으로 허용하고 다음을 반환합니다. 파이프라인에서 처리한 문서 컬렉션으로 확인됩니다.
문서 필터링
$match 단계를 사용하여 Query API 쿼리 필터에 따라 문서를 필터링할 수 있습니다.
{ "$match": { "<Field Name>": <Query Expression>, ... } }
예시
다음 $match
단계는 type
필드 값이 '다년생'인 문서만 포함하도록 필터링합니다.
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$match", new Document("type", new Document("$eq", "perennial")))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$match", Document("type", Document("\$eq", "perennial") ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
문서 그룹화
$group 단계를 사용하여 하나 이상의 문서에 대한 요약 데이터를 집계할 수 있습니다. MongoDB는 $group
단계의 _id
필드 값에 정의된 표현식을 기반으로 문서를 그룹화합니다. 필드 이름 앞에 $
를 붙여 특정 문서 필드를 참조할 수 있습니다.
다음 스니펫은 type
값을 기준으로 plants
컬렉션의 모든 문서를 그룹화하고 각 유형의 개수를 집계합니다.
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$group", new Document("_id", "$type") .append("totalCount", new Document("$sum", 1)))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$group", Document("_id", "\$type") .append("totalCount", Document("\$sum", 1)) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
문서 필드 프로젝트
$project 단계를 사용하여 문서의 특정 필드를 포함하거나 생략할 수 있습니다. 또한 애그리게이션 연산자 를 사용하여 새 필드를 계산할 수 있습니다. 프로젝션은 두 가지 방식으로 작동합니다.
값이 '1'인 필드를 명시적으로 포함합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 제외하는 부작용이 있습니다.
값이 '0'인 필드를 암시적으로 제외합니다. 이 방법에는 지정되지 않은 모든 필드를 암시적으로 포함하는 부작용이 있습니다.
이 두 가지 프로젝션 방법은 상호 배타적입니다. 필드를 명시적으로 포함하는 경우 필드를 명시적으로 제외할 수 없으며 그 반대의 경우도 마찬가지입니다.
참고
_id
필드는 특별한 경우에 해당하며 명시적으로 지정하지 않는 한 항상 모든 쿼리에 포함됩니다. 따라서 0
값이 있는 _id
필드를 제외하는 동시에 1
을 사용해 _partition
등의 다른 필드를 포함하는 것이 가능합니다. _id
필드를 제외하는 특수한 경우에만 하나의 $project
단계에서 제외와 포함을 동시에 적용할 수 있습니다.
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
예시
다음 $project
단계에서는 _id
필드를 생략하고 name
필드를 포함하며 storeNumber
라는 새 필드를 생성합니다. storeNumber
는 다음 집계 연산자 두 개를 사용하여 생성됩니다.
$split
값_partition
을 공백 문자를 둘러싼 두 개의 문자열 세그먼트로 분리합니다. 예를 들어 이처럼 '매장 42'로 분할된 값은 다음과 같이 '매장' 및 '42'의 두 개의 요소가 있는 배열을 반환합니다.$arrayElemAt
두 번째 인수를 기반으로 배열에서 특정 요소를 선택합니다. 이 경우 배열 인덱스는0
이므로 값1
은$split
연산자로 생성된 배열에서 두 번째 요소를 선택합니다. 예를 들어 이 연산에 전달된 값 ['매장', '42']는 값 '42'를 반환합니다.
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$project", new Document("_id", 0) .append("name", 1) .append("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList("$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$project", Document("_id", 0) .append("name", 1) .append("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
문서에 필드 추가
$addFields 단계에서 애그리게이션 연산자를 사용하여 계산된 값이 있는 새 필드를 추가할 수 있습니다.
참고
$addFields
는 $project와 유사하지만 필드를 포함하거나 생략할 수 없습니다.
예시
다음 $addFields
단계에서는 storeNumber
라는 새 필드를 만듭니다. 여기서 값은 _partition
필드의 값을 변환하는 두 집계 연산자의 출력입니다.
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$addFields", new Document("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList( "$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$addFields", Document("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
배열 값 풀기
$unwind 단계를 사용하여 배열이 포함된 단일 문서를 해당 배열의 개별 값이 포함된 여러 문서로 변환할 수 있습니다. 배열 필드를 풀면 MongoDB는 배열 필드의 각 요소에 대해 각 문서를 한 번씩 복사하지만 배열 값을 각 사본의 배열 요소로 바꿉니다.
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
예시
다음 $unwind
단계에서는 각 문서에서 items
배열의 각 요소에 대해 새 문서를 만듭니다. 또한 각 새 문서에 itemIndex
라는 필드를 추가하여 원본 배열에서 요소의 위치 인덱스를 지정합니다:
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$unwind", new Document("path", "$items") .append("includeArrayIndex", "itemIndex"))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$unwind", Document("path", "\$items") .append("includeArrayIndex", "itemIndex")) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
구매 컬렉션에서 다음 문서를 고려합니다.
{ _id: 123, customerId: 24601, items: [ { name: "Baseball", quantity: 5 }, { name: "Baseball Mitt", quantity: 1 }, { name: "Baseball Bat", quantity: 1 }, ] }
이 문서에 예시 $unwind
단계를 적용하면 단계는 다음 세 가지 문서를 출력합니다.
{ _id: 123, customerId: 24601, itemIndex: 0, items: { name: "Baseball", quantity: 5 } }, { _id: 123, customerId: 24601, itemIndex: 1, items: { name: "Baseball Mitt", quantity: 1 } }, { _id: 123, customerId: 24601, itemIndex: 2, items: { name: "Baseball Bat", quantity: 1 } }