# CRUD Operations CRUD is an acronym for Create, Read, Update, and Delete. These operations may be performed via the [MongoDB\Collection][collection] class, which implements MongoDB's cross-driver [CRUD specification][crud-spec]. This page will demonstrate how to insert, query, update, and delete documents using the library. A general introduction to CRUD operations in MongoDB may be found in the [MongoDB Manual][crud]. [collection]: ../classes/collection.md [crud-spec]: https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst [crud]: https://docs.mongodb.org/manual/crud/ This page covers the following common use cases: * Querying for [one](#finding-one-document) or [many](#finding-many-documents) documents at a time * [Projecting](#query-projection) fields in a query * Applying [limit, sort, and skip options](#limit-sort-and-skip-options) to a query * Inserting [one](#inserting-one-document) or [many](#inserting-many-documents) documents at a time * Updating [one](#updating-one-document) or [many](#updating-many-documents) documents at a time * [Replacing](#replacing-a-document) a document * [Upserting](#upserting-a-document) a document * Deleting [one](#deleting-one-document) or [many](#deleting-many-documents) documents at a time * [Aggregating](#aggregating-documents) documents Note that the use of arrays to express documents in the following examples was done for simplicity. The driver will also accept instances of stdClass or [MongoDB\BSON\Serializable][serializable]) for these arguments (e.g. query filters, inserted documents, update documents). [serializable]: http://php.net/mongodb-bson-serializable Documents destined for database storage (e.g. insert documents, replacement documents, embedded documents included in an update operation) may also be instances of [MongoDB\BSON\Persistable][persistable]. See [Persistable Classes][persistable-classes] for more information. [persistable]: http://php.net/mongodb-bson-persistable [persistable-classes]: bson.md#persistable-classes ## Finding One Document The [findOne()][findone] method returns the first matched document, or null if no document was matched. [findone]: ../classes/collection.md#findone ``` demo->zips; $document = $collection->findOne(['_id' => '94301']); var_dump($document); ``` The above example would output something similar to: ``` object(MongoDB\Model\BSONDocument)#13 (1) { ["storage":"ArrayObject":private]=> array(5) { ["_id"]=> string(5) "94301" ["city"]=> string(9) "PALO ALTO" ["loc"]=> object(MongoDB\Model\BSONArray)#12 (1) { ["storage":"ArrayObject":private]=> array(2) { [0]=> float(-122.149685) [1]=> float(37.444324) } } ["pop"]=> int(15965) ["state"]=> string(2) "CA" } } ``` ## Finding Many Documents The [find()][find] method returns a [MongoDB\Driver\Cursor][cursor] object, which may be iterated upon to access all matched documents. The following example queries for all zip codes in a given city: [find]: ../classes/collection.md#find [cursor]: http://php.net/mongodb-driver-cursor ``` demo->zips; $cursor = $collection->find(['city' => 'JERSEY CITY', 'state' => 'NJ']); foreach ($cursor as $document) { echo $document['_id'], "\n"; } ``` The above example would output something similar to: ``` 07302 07304 07305 07306 07307 07310 ``` ## Query Projection Queries may include a [projection][projection] to include or exclude specific fields in the returned documents. The following example selects only the population field for the zip code: [projection]: https://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/ ``` demo->zips; $document = $collection->findOne( ['_id' => '10011'], ['projection' => ['pop' => 1]] ); var_dump($document); ``` The above example would output something similar to: ``` object(MongoDB\Model\BSONDocument)#12 (1) { ["storage":"ArrayObject":private]=> array(2) { ["_id"]=> string(5) "10011" ["pop"]=> int(46560) } } ``` **Note:** the "_id" field is included by default unless explicitly excluded. ## Limit, Sort, and Skip Options In addition to criteria, queries may take options to limit, sort, and skip returned documents. The following example queries for the five most populous zip codes in the United States: ``` demo->zips; $cursor = $collection->find( [], [ 'limit' => 5, 'sort' => ['pop' => -1], ] ); foreach ($cursor as $document) { echo $document['_id'], "\n"; } ``` The above example would output something similar to: ``` 60623: CHICAGO, IL 11226: BROOKLYN, NY 10021: NEW YORK, NY 10025: NEW YORK, NY 90201: BELL GARDENS, CA ``` ## Inserting One Document The [insertOne()][insertone] method may be used to insert a single document. This method returns an instance of `MongoDB\InsertOneResult`, which may be used to access the ID of the inserted document. Note that if a document does not contain an `_id` field at the time of insertion, the driver will generate a `MongoDB\BSON\ObjectID` to use as its ID. [insertone]: ../classes/collection.md#insertone ``` demo->users; $collection->drop(); $insertOneResult = $collection->insertOne(['name' => 'Bob']); printf("Inserted %d document(s)\n", $insertOneResult->getInsertedCount()); var_dump($insertOneResult->getInsertedId()); ``` The above example would output something similar to: ``` Inserted 1 document(s) object(MongoDB\BSON\ObjectID)#10 (1) { ["oid"]=> string(24) "5750905b6118fd170565aa81" } ``` The following example inserts a document with an ID. Note that if an ID is not unique for the collection, the insert will fail due to a duplicate key error. ``` demo->users; $collection->drop(); $insertOneResult = $collection->insertOne(['_id' => 1, 'name' => 'Alice']); printf("Inserted %d document(s)\n", $insertOneResult->getInsertedCount()); var_dump($insertOneResult->getInsertedId()); ``` The above example would output: ``` Inserted 1 document(s) int(1) ``` ## Inserting Many Documents The [insertMany()][insertmany] method may be used to insert multiple documents at a time. This method returns an instance of `MongoDB\InsertManyResult`, which may be used to access the IDs of the inserted documents. [insertmany]: ../classes/collection.md#insertmany ``` demo->users; $collection->drop(); $insertManyResult = $collection->insertMany([ ['name' => 'Bob'], ['_id' => 1, 'name' => 'Alice'], ]); printf("Inserted %d document(s)\n", $insertManyResult->getInsertedCount()); var_dump($insertManyResult->getInsertedIds()); ``` The above example would output something similar to: ``` Inserted 2 document(s) array(2) { [0]=> object(MongoDB\BSON\ObjectID)#10 (1) { ["oid"]=> string(24) "5750927b6118fd1ed64eb141" } [1]=> int(1) } ``` ## Updating One Document The [updateOne()][updateone] method may be used to update a single document matching a filter. This method returns an instance of `MongoDB\UpdateResult`, which may be used to access statistics about the update operation. [updateone]: ../classes/collection.md#updateone This method has two required parameters: a query filter and an update document. The query filter is similar to what might be provided to [find()][find]. The update document consists of one or more [update operators][updateops]. [updateops]: https://docs.mongodb.com/manual/reference/operator/update/ ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); $updateResult = $collection->updateOne( ['state' => 'ny'], ['$set' => ['country' => 'us']] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); ``` The above example would output something similar to: ``` Matched 1 document(s) Modified 1 document(s) ``` Note that it is possible for a document to match the filter but not be modified by an update: ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); $updateResult = $collection->updateOne( ['name' => 'Bob'], ['$set' => ['state' => 'ny']] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); ``` The above example would output something similar to: ``` Matched 1 document(s) Modified 0 document(s) ``` ## Updating Many Documents The [updateMany()][updatemany] method may be used to update multiple documents at a time. This method returns an instance of `MongoDB\UpdateResult`, which may be used to access statistics about the update operation. [updatemany]: ../classes/collection.md#updatemany This method has two required parameters: a query filter and an update document. The query filter is similar to what might be provided to [find()][find]. The update document consists of one or more [update operators][updateops]. ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny', 'country' => 'us']); $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); $collection->insertOne(['name' => 'Sam', 'state' => 'ny']); $updateResult = $collection->updateMany( ['state' => 'ny'], ['$set' => ['country' => 'us']] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); ``` The above example would output something similar to: ``` Matched 3 document(s) Modified 2 document(s) ``` ## Replacing a Document The [replaceOne()][replaceone] method may be used to replace a single document matching a filter. This method returns an instance of `MongoDB\UpdateResult`, which may be used to access statistics about the replacement operation. [replaceone]: ../classes/collection.md#replaceone This method has two required parameters: a query filter and a replacement document. The query filter is similar to what might be provided to [find()][find]. The replacement document will be used to overwrite the matched document (excluding its ID, which is immutable) and must not contain [update operators][updateops]. Note that the very nature of a replacement operation makes it easy to inadvertently overwrite or delete fields in a document. When possible, users should consider updating individual fields with [updateOne()][updateone] or [updateMany()][updatemany]. ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); $updateResult = $collection->replaceOne( ['name' => 'Bob'], ['name' => 'Robert', 'state' => 'ca'] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); ``` The above example would output something similar to: ``` Matched 1 document(s) Modified 1 document(s) ``` Note that it is possible for a document to match the filter but not be modified by a replacement (i.e. the matched document and replacement may be the same). ## Upserting a Document An upsert is a variation of an update or replace operation, whereby a new document is inserted if the query filter does not match an existing document. An upsert may be specified via the `upsert` option for [updateOne()][updateone], [updateMany()][updatemany], or [replaceOne()][replaceone]. The logic by which the inserted document is created is discussed in the [MongoDB manual][upsert]. [upsert]: https://docs.mongodb.com/manual/reference/method/db.collection.update/#upsert-parameter If a document has been upserted, its ID will be accessible via `MongoDB\UpdateResult::getUpsertedId()`. The following example demonstrates an upsert via [updateOne()][updateone]: ``` demo->users; $collection->drop(); $updateResult = $collection->updateOne( ['name' => 'Bob'], ['$set' => ['state' => 'ny']], ['upsert' => true] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); var_dump($collection->findOne(['_id' => $updateResult->getUpsertedId()])); ``` The above example would output something similar to: ``` Matched 0 document(s) Modified 0 document(s) object(MongoDB\Model\BSONDocument)#16 (1) { ["storage":"ArrayObject":private]=> array(3) { ["_id"]=> object(MongoDB\BSON\ObjectID)#15 (1) { ["oid"]=> string(24) "57509c4406d7241dad86e7c3" } ["name"]=> string(3) "Bob" ["state"]=> string(2) "ny" } } ``` The following example demonstrates an upsert via [replaceOne()][replaceone]: ``` demo->users; $collection->drop(); $updateResult = $collection->replaceOne( ['name' => 'Bob'], ['name' => 'Alice', 'state' => 'ny'], ['upsert' => true] ); printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); var_dump($collection->findOne(['_id' => $updateResult->getUpsertedId()])); ``` The above example would output something similar to: ``` Matched 0 document(s) Modified 0 document(s) object(MongoDB\Model\BSONDocument)#16 (1) { ["storage":"ArrayObject":private]=> array(3) { ["_id"]=> object(MongoDB\BSON\ObjectID)#15 (1) { ["oid"]=> string(24) "57509c6606d7241dad86e7c4" } ["name"]=> string(5) "Alice" ["state"]=> string(2) "ny" } } ``` ## Deleting One Document The [deleteOne()][deleteone] method may be used to delete a single document matching a filter. This method returns an instance of `MongoDB\DeleteResult`, which may be used to access statistics about the delete operation. [deleteone]: ../classes/collection.md#deleteone This method has two required parameters: a query filter. The query filter is similar to what might be provided to [find()][find]. ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); $deleteResult = $collection->deleteOne(['state' => 'ny']); printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); ``` The above example would output something similar to: ``` Deleted 1 document(s) ``` ## Deleting Many Documents The [deleteMany()][deletemany] method may be used to delete multiple documents at a time. This method returns an instance of `MongoDB\DeleteResult`, which may be used to access statistics about the delete operation. [deletemany]: ../classes/collection.md#deletemany This method has two required parameters: a query filter. The query filter is similar to what might be provided to [find()][find]. ``` demo->users; $collection->drop(); $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); $deleteResult = $collection->deleteMany(['state' => 'ny']); printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); ``` The above example would output something similar to: ``` Deleted 2 document(s) ``` ## Aggregating Documents The [Aggregation Framework][aggregation] may be used to issue complex queries that filter, transform, and group collection data. The [aggregate()][aggregate] method returns a [Traversable][traversable] object, which may be iterated upon to access the results of an aggregation pipeline. [aggregation]: https://docs.mongodb.org/manual/core/aggregation-pipeline/ [aggregate]: ../classes/collection.md#aggregate [traversable]: http://php.net/traversable ``` demo->zips; $cursor = $collection->aggregate([ ['$group' => ['_id' => '$state', 'count' => ['$sum' => 1]]], ['$sort' => ['count' => -1]], ['$limit' => 5], ]); foreach ($cursor as $state) { printf("%s has %d zip codes\n", $state['_id'], $state['count']); } ``` The above example would output something similar to: ``` TX has 1671 zip codes NY has 1595 zip codes CA has 1516 zip codes PA has 1458 zip codes IL has 1237 zip codes ``` **Note:** [aggregate()][aggregate] is documented as returning a [Traversable][traversable] object because the [aggregate][aggregate-cmd] command may return its results inline (i.e. a single result document's array field, which the library will package as a PHP iterator) or via a command cursor (i.e. [MongoDB\Driver\Cursor][cursor]). [aggregate-cmd]: (http://docs.mongodb.org/manual/reference/command/aggregate/)