AI 에이전트의 경우: 문서 인덱스는 https://www.mongodb.com/ko-kr/docs/llms.txt에서 사용할 수 있으며, 모든 페이지의 마크다운 버전은 어떤 URL 경로에 .md를 추가하여 사용할 수 있습니다.

유형 코덱으로 데이터 인코딩

버전 1.17에 추가 되었습니다.

코덱은 BSON 문서를 PHP 객체로 디코딩하고 PHP 객체를 BSON 문서로 인코딩하는 데 사용됩니다. 다른 방법과 달리(예: 유형 맵) 코덱을 사용하면 다양한 데이터 유형을 더 효과적으로 사용자 지정하고 처리할 수 있습니다. BSON 인코딩 및 디코딩을 위한 로직을 도메인 클래스와 분리하여 BSON을 일반 이전 PHP 객체로 디코딩할 수도 있습니다.

주요 로직은 문서 코덱에 포함되어 있습니다. 이 클래스는 MongoDB\Codec\DocumentCodec 인터페이스를 구현하고 인코딩/디코딩할 수 있는 데이터 유형과 방법을 정의합니다. 다음 예제에서는 Person 클래스와 이를 변환하는 코덱을 정의합니다.

<?php use MongoDB\BSON\ObjectId; final class Person{ public function __construct( public string $name, public readonly ObjectId $id = new ObjectId(), ) { }}
<?php use MongoDB\BSON\Document;use MongoDB\Codec\DecodeIfSupported;use MongoDB\Codec\DocumentCodec;use MongoDB\Codec\EncodeIfSupported;use MongoDB\Exception\UnsupportedValueException; /** @template-implements DocumentCodec<Person> */final class PersonCodec implements DocumentCodec{ // These traits define commonly used functionality to avoid duplication use DecodeIfSupported; use EncodeIfSupported; public function canDecode(mixed $value): bool { return $value instanceof Document && $value->has('name'); } public function canEncode(mixed $value): bool { return $value instanceof Person; } public function decode(mixed $value): Person { if (! $this->canDecode($value)) { throw UnsupportedValueException::invalidDecodableValue($value); } return new Person( $value->get('name'), $value->get('_id'), ); } public function encode(mixed $value): Document { if (! $this->canEncode($value)) { throw UnsupportedValueException::invalidEncodableValue($value); } return Document::fromPHP([ '_id' => $value->id, 'name' => $value->name, ]); }}

그런 다음 컬렉션에서 이 코덱을 사용하려면 컬렉션을 선택할 때 codec 옵션을 지정합니다.

<?php use MongoDB\Client; $client = new Client();$collection = $client->selectCollection('test', 'person', [ 'codec' => new PersonCodec(),]); $person = new Person('Jane Doe');$collection->insertOne($person); $person = $collection->findOne();

위의 예시에서는 collection을 선택하고 문서 인코딩 및 디코딩에 PersonCodec 을 사용하도록 지시합니다. 데이터를 삽입할 때 PersonCodec 을 사용하여 문서를 인코딩합니다. 데이터를 검색할 때 동일한 PersonCodec 가 BSON 데이터를 인스턴스로 디코딩하는 데 Person 사용됩니다. PersonCodec 는 기술적으로 이름 필드가 포함된 모든 BSON 문서를 디코딩할 수 있지만, 다른 문서에는 이를 사용하고 싶지 않습니다. 문서 코덱은 MongoDB\Collection 과 함께 사용되거나 MEAN 문서를 디코딩할 때 사용됩니다.

코덱과 함께 컬렉션을 사용하는 경우 코덱은 특정 작업에 대해 해당 유형의 데이터만 허용하고 반환합니다. 삽입 및 바꾸기 작업(예: insertOne, `findOneAndReplace 및 일부 bulkWrite 연산)은 제공된 코덱을 사용하여 주어진 데이터를 인코딩하려고 시도합니다. 인코딩할 수 없는 문서를 삽입하거나 바꾸려고 하면 예외가 발생합니다. 읽기 작업(예: aggregate, find 및 findOneAndUpdate)은 제공된 코덱을 사용하여 반환된 문서 디코딩을 시도합니다. 코덱이 반환된 데이터를 지원하지 않으면 예외가 발생합니다.

특정 작업에 대한 코덱 사용을 비활성화하거나 작업의 codec 옵션에 null 를 지정하여 다른 코덱을 사용(예: 집계 파이프라인의 결과 디코딩)할 수 있습니다. 또는 typeMap 연산을 사용하여 유형 맵을 지정하면 collection 수준 코덱도 재정의됩니다.

<?php // Overrides the collection codec, falling back to the default type map$collection->aggregate($pipeline, ['codec' => null]); // Overrides the collection codec, using the specified type map$collection->findOne($filter, ['typeMap' => ['root' => 'stdClass']]);

이전 예시에서는 특정 클래스에 대한 코덱을 정의하는 방법을 설명했습니다. 그러나 모든 문서에서 특정 데이터 유형을 처리하는 코덱을 만들 수 있습니다. 이는 MongoDB\Codec\Codec 인터페이스를 구현하여 달성할 수 있습니다.

다음 예시에서는 DateTimeInterface 인스턴스를 BSON 날짜 및 수반되는 시간대 문자열이 포함된 내장된 문서로 저장하는 코덱을 정의합니다. 그런 다음 동일한 내장된 문서를 BSON 디코딩 중에 DateTimeImmutable 로 다시 변환할 수 있습니다.

<?php use MongoDB\BSON\Document;use MongoDB\BSON\UTCDateTime;use MongoDB\Codec\Codec;use MongoDB\Codec\DecodeIfSupported;use MongoDB\Codec\EncodeIfSupported;use MongoDB\Exception\UnsupportedValueException; /** @template-implements Codec<Document, DateTimeImmutable> */final class DateTimeCodec implements Codec{ use DecodeIfSupported; use EncodeIfSupported; public function canDecode(mixed $value): bool { /* This codec inspects the BSON document to ensure it has the fields it expects, and that those fields are of * the correct type. This is a robust approach to avoid decoding document that are not supported and would cause * exceptions. * * For large documents, this can be inefficient as we're inspecting the entire document four times (once for * each call to has() and get()). For small documents, this is not a problem. */ return $value instanceof Document && $value->has('utc') && $value->get('utc') instanceof UTCDateTime && $value->has('tz') && is_string($value->get('tz')); } public function canEncode(mixed $value): bool { return $value instanceof DateTimeInterface; } public function decode(mixed $value): DateTimeImmutable { if (! $this->canDecode($value)) { throw UnsupportedValueException::invalidDecodableValue($value); } $timeZone = new DateTimeZone($value->get('tz')); $dateTime = $value->get('utc') ->toDateTime() ->setTimeZone($timeZone); return DateTimeImmutable::createFromMutable($dateTime); } public function encode(mixed $value): Document { if (! $this->canEncode($value)) { throw UnsupportedValueException::invalidEncodableValue($value); } return Document::fromPHP([ 'utc' => new UTCDateTime($value), 'tz' => $value->getTimezone()->getName(), ]); }}

참고

코덱을 작성할 때는 데이터 처리에 최대한 관대해야 합니다. 이 경우 코덱은 BSON으로 인코딩할 때 모든 DateTimeInterface 를 처리하는데, 이는 이러한 객체에서 UTCDateTime 인스턴스를 생성할 수 있기 때문입니다. BSON에서 데이터를 디코딩할 때는 항상 DateTimeImmutable 인스턴스로 디코딩됩니다.

이제 이 코덱은 날짜 필드를 처리하는 다른 코덱에서 활용할 수 있습니다.

먼저 Person 클래스에 createdAt 필드를 추가합니다.

<?php use MongoDB\BSON\ObjectId; final class Person{ public function __construct( public string $name, public readonly DateTimeImmutable $createdAt = new DateTimeImmutable(), public readonly ObjectId $id = new ObjectId(), ) { }}

마지막으로 새 필드를 처리하도록 코덱을 수정합니다.

<?php use MongoDB\BSON\Document;use MongoDB\Codec\DecodeIfSupported;use MongoDB\Codec\DocumentCodec;use MongoDB\Codec\EncodeIfSupported;use MongoDB\Exception\UnsupportedValueException; /** @template-implements DocumentCodec<Person> */final class PersonCodec implements DocumentCodec{ use DecodeIfSupported; use EncodeIfSupported; public function __construct( private readonly DateTimeCodec $dateTimeCodec = new DateTimeCodec(), ) { } public function canDecode(mixed $value): bool { return $value instanceof Document && $value->has('name'); } public function canEncode(mixed $value): bool { return $value instanceof Person; } public function decode(mixed $value): Person { if (! $this->canDecode($value)) { throw UnsupportedValueException::invalidDecodableValue($value); } return new Person( $value->get('name'), $this->dateTimeCodec->decode($value->get('createdAt')), $value->get('_id'), ); } public function encode(mixed $value): Document { if (! $this->canEncode($value)) { throw UnsupportedValueException::invalidEncodableValue($value); } return Document::fromPHP([ '_id' => $value->id, 'name' => $value->name, 'createdAt' => $this->dateTimeCodec->encode($value->createdAt), ]); }}

이전 예제에서는 단일 문서를 처리하는 방법을 보여 주었습니다. 그러나 내장된 문서가 포함된 필드를 처리하고 싶을 때가 있습니다. 여기서는 Person 문서에 포함시킬 Address 문서를 사용하여 이를 시연해 보겠습니다. 일관성을 보장하기 위해 이 클래스를 읽기 전용 클래스로 만들 것입니다.

<?php final readonly class Address{ public function __construct( public string $street, public string $postCode, public string $city, public string $country, ) { }}

이제 이 클래스에 대한 문서 코덱을 만들 수 있습니다.

<?php use MongoDB\BSON\Document;use MongoDB\Codec\DecodeIfSupported;use MongoDB\Codec\DocumentCodec;use MongoDB\Codec\EncodeIfSupported;use MongoDB\Exception\UnsupportedValueException; /** @template-implements DocumentCodec<Address> */final class AddressCodec implements DocumentCodec{ use DecodeIfSupported; use EncodeIfSupported; public function canDecode(mixed $value): bool { return $value instanceof Document && $value->has('street') && $value->has('postCode') && $value->has('city') && $value->has('country'); } public function canEncode(mixed $value): bool { return $value instanceof Address; } public function decode(mixed $value): Address { if (! $this->canDecode($value)) { throw UnsupportedValueException::invalidDecodableValue($value); } return new Address( $value->get('street'), $value->get('postCode'), $value->get('city'), $value->get('country'), ); } public function encode(mixed $value): Document { if (! $this->canEncode($value)) { throw UnsupportedValueException::invalidEncodableValue($value); } return Document::fromPHP([ 'street' => $value->street, 'postCode' => $value->postCode, 'city' => $value->city, 'country' => $value->country, ]); }}

Person 클래스는 새 address 필드를 가져오지만 이를 선택 사항으로 둡니다.

<?php use MongoDB\BSON\ObjectId; final class Person{ public ?Address $address = null; public function __construct( public string $name, public readonly ObjectId $id = new ObjectId(), ) { }}

이제 PersonCodec 는 데이터를 변환할 때 선택적 address 필드를 처리할 수 있습니다.

<?php use MongoDB\BSON\Document;use MongoDB\Codec\DecodeIfSupported;use MongoDB\Codec\DocumentCodec;use MongoDB\Codec\EncodeIfSupported;use MongoDB\Exception\UnsupportedValueException; /** @template-implements DocumentCodec<Person> */final class PersonCodec implements DocumentCodec{ use DecodeIfSupported; use EncodeIfSupported; public function __construct( private readonly AddressCodec $addressCodec = new AddressCodec(), ) { } public function canDecode(mixed $value): bool { return $value instanceof Document && $value->has('name'); } public function canEncode(mixed $value): bool { return $value instanceof Person; } public function decode(mixed $value): Person { if (! $this->canDecode($value)) { throw UnsupportedValueException::invalidDecodableValue($value); } $person = new Person( $value->get('name'), $value->get('_id'), ); // Address is optional, so only decode if it exists if ($value->has('address')) { $person->address = $this->addressCodec->decode($value->get('address')); } return $person; } public function encode(mixed $value): Document { if (! $this->canEncode($value)) { throw UnsupportedValueException::invalidEncodableValue($value); } $data = [ '_id' => $value->id, 'name' => $value->name, ]; // Don't add a null value to the document if address is not set if ($value->address) { $data['address'] = $this->addressCodec->encode($value->address); } return Document::fromPHP($data); }}