From 3d8d0954925873020e08873b4540ceeb8f80996f Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:48:48 -0500 Subject: [PATCH 1/9] DOCSP-48028: v5.2 release (#3297) * DOCSP-48028: v5.2 release * wip * wip * add keyword --- docs/compatibility.txt | 8 ++++++- docs/filesystems.txt | 4 ++-- docs/fundamentals/aggregation-builder.txt | 22 ------------------- .../framework-compatibility-laravel.rst | 10 +++++++++ docs/query-builder.txt | 2 +- docs/quick-start.txt | 2 +- docs/quick-start/download-and-install.txt | 2 +- docs/user-authentication.txt | 5 +++-- 8 files changed, 25 insertions(+), 30 deletions(-) diff --git a/docs/compatibility.txt b/docs/compatibility.txt index fd3e2da02..9ee891e20 100644 --- a/docs/compatibility.txt +++ b/docs/compatibility.txt @@ -15,7 +15,7 @@ Compatibility :class: singlecol .. meta:: - :keywords: laravel 9, laravel 10, laravel 11, 4.0, 4.1, 4.2, 5.0, 5.1 + :keywords: laravel 9, laravel 10, laravel 11, laravel 12, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2 Laravel Compatibility --------------------- @@ -28,3 +28,9 @@ the {+odm-short+} that you can use together. To find compatibility information for unmaintained versions of the {+odm-short+}, see `Laravel Version Compatibility <{+mongodb-laravel-gh+}/blob/3.9/README.md#installation>`__ on GitHub. + +PHP Driver Compatibility +------------------------ + +To use {+odm-long+} v5.2 or later, you must install v1.21 of the +{+php-library+} and {+php-extension+}. diff --git a/docs/filesystems.txt b/docs/filesystems.txt index 3ec7ee41f..c62853f58 100644 --- a/docs/filesystems.txt +++ b/docs/filesystems.txt @@ -79,7 +79,7 @@ You can configure the following settings in ``config/filesystems.php``: * - ``throw`` - If ``true``, exceptions are thrown when an operation cannot be performed. If ``false``, - operations return ``true`` on success and ``false`` on error. Defaults to ``false``. + operations return ``true`` on success and ``false`` on error. Defaults to ``false``. You can also use a factory or a service name to create an instance of ``MongoDB\GridFS\Bucket``. In this case, the options ``connection`` and ``database`` are ignored: @@ -133,7 +133,7 @@ metadata, including the file name and a unique ObjectId. If multiple documents share the same file name, they are considered "revisions" and further distinguished by creation timestamps. -The Laravel MongoDB integration uses the GridFS Flysystem adapter. It interacts +{+odm-long+} uses the GridFS Flysystem adapter. It interacts with file revisions in the following ways: - Reading a file reads the last revision of this file name diff --git a/docs/fundamentals/aggregation-builder.txt b/docs/fundamentals/aggregation-builder.txt index 3169acfeb..9ae31f0c1 100644 --- a/docs/fundamentals/aggregation-builder.txt +++ b/docs/fundamentals/aggregation-builder.txt @@ -37,7 +37,6 @@ The {+odm-long+} aggregation builder lets you build aggregation stages and aggregation pipelines. The following sections show examples of how to use the aggregation builder to create the stages of an aggregation pipeline: -- :ref:`laravel-add-aggregation-dependency` - :ref:`laravel-build-aggregation` - :ref:`laravel-aggregation-examples` - :ref:`laravel-create-custom-operator-factory` @@ -49,27 +48,6 @@ aggregation builder to create the stages of an aggregation pipeline: aggregation builder, see :ref:`laravel-query-builder-aggregations` in the Query Builder guide. -.. _laravel-add-aggregation-dependency: - -Add the Aggregation Builder Dependency --------------------------------------- - -The aggregation builder is part of the {+agg-builder-package-name+} package. -You must add this package as a dependency to your project to use it. Run the -following command to add the aggregation builder dependency to your -application: - -.. code-block:: bash - - composer require {+agg-builder-package-name+}:{+agg-builder-version+} - -When the installation completes, verify that the ``composer.json`` file -includes the following line in the ``require`` object: - -.. code-block:: json - - "{+agg-builder-package-name+}": "{+agg-builder-version+}", - .. _laravel-build-aggregation: Create Aggregation Stages diff --git a/docs/includes/framework-compatibility-laravel.rst b/docs/includes/framework-compatibility-laravel.rst index 16c405e21..c642a6763 100644 --- a/docs/includes/framework-compatibility-laravel.rst +++ b/docs/includes/framework-compatibility-laravel.rst @@ -3,21 +3,31 @@ :stub-columns: 1 * - {+odm-long+} Version + - Laravel 12.x - Laravel 11.x - Laravel 10.x - Laravel 9.x + * - 5.2 + - ✓ + - ✓ + - ✓ + - + * - 4.2 to 5.1 + - - ✓ - ✓ - * - 4.1 + - - - ✓ - * - 4.0 + - - - ✓ - diff --git a/docs/query-builder.txt b/docs/query-builder.txt index 76a0d144a..c641323dc 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -227,7 +227,7 @@ value greater than ``8.5`` and a ``year`` value of less than .. tip:: - For compatibility with Laravel, Laravel MongoDB v5.1 supports both arrow + For compatibility with Laravel, {+odm-long+} v5.1 supports both arrow (``->``) and dot (``.``) notation to access nested fields in a query filter. The preceding example uses dot notation to query the ``imdb.rating`` nested field, which is the recommended syntax. diff --git a/docs/quick-start.txt b/docs/quick-start.txt index 1d188ad84..83b0c3937 100644 --- a/docs/quick-start.txt +++ b/docs/quick-start.txt @@ -47,7 +47,7 @@ read and write operations on the data. MongoDB University Learning Byte. If you prefer to connect to MongoDB by using the {+php-library+} without - Laravel, see `Connecting to MongoDB `__ + Laravel, see `Connect to MongoDB `__ in the {+php-library+} documentation. The {+odm-short+} extends the Laravel Eloquent and Query Builder syntax to diff --git a/docs/quick-start/download-and-install.txt b/docs/quick-start/download-and-install.txt index 696861a43..293425791 100644 --- a/docs/quick-start/download-and-install.txt +++ b/docs/quick-start/download-and-install.txt @@ -31,7 +31,7 @@ to a Laravel web application. .. tip:: As an alternative to the following installation steps, you can use Laravel Herd - to install MongoDB and configure a Laravel MongoDB development environment. For + to install MongoDB and configure a development environment for {+odm-long+}. For more information about using Laravel Herd with MongoDB, see the following resources: - `Installing MongoDB via Herd Pro diff --git a/docs/user-authentication.txt b/docs/user-authentication.txt index 88b0da603..63e883d13 100644 --- a/docs/user-authentication.txt +++ b/docs/user-authentication.txt @@ -224,7 +224,7 @@ to the ``guards`` array: ], ], -Use Laravel Passport with Laravel MongoDB +Use Laravel Passport with {+odm-long+} ````````````````````````````````````````` After installing Laravel Passport, you must enable Passport compatibility with MongoDB by @@ -300,4 +300,5 @@ Additional Information To learn more about user authentication, see `Authentication `__ in the Laravel documentation. -To learn more about Eloquent models, see the :ref:`laravel-eloquent-model-class` guide. \ No newline at end of file +To learn more about Eloquent models, see the +:ref:`laravel-eloquent-model-class` guide. From f06d944955fed946fdf94f0a6f01fa48142b1357 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:56:29 -0500 Subject: [PATCH 2/9] Merges the read operation reorganization into 5.2 (#3301) * DOCSP-35945: read operations reorg (#3293) * DOCSP-35945: read operations reorg * skip * small fixes * small fixes * fixes - RM and moved a section * link fic * Fix releasing from development branch (#3299) --------- Co-authored-by: MongoDB PHP Bot <162451593+mongodb-php-bot@users.noreply.github.com> Co-authored-by: Andreas Braun --- .github/workflows/release.yml | 2 +- docs/fundamentals/read-operations.txt | 749 +++--------------- .../read-operations/modify-results.txt | 227 ++++++ .../read-operations/read-pref.txt | 141 ++++ .../fundamentals/read-operations/retrieve.txt | 304 +++++++ .../read-operations/search-text.txt | 157 ++++ docs/fundamentals/write-operations.txt | 3 +- .../before-you-get-started.rst | 15 + 8 files changed, 960 insertions(+), 638 deletions(-) create mode 100644 docs/fundamentals/read-operations/modify-results.txt create mode 100644 docs/fundamentals/read-operations/read-pref.txt create mode 100644 docs/fundamentals/read-operations/retrieve.txt create mode 100644 docs/fundamentals/read-operations/search-text.txt create mode 100644 docs/includes/fundamentals/read-operations/before-you-get-started.rst diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4afbe78f1..bc60a79cc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: run: | echo RELEASE_VERSION=${{ inputs.version }} >> $GITHUB_ENV echo RELEASE_BRANCH=$(echo ${{ inputs.version }} | cut -d '.' -f-2) >> $GITHUB_ENV - echo DEV_BRANCH=$(echo ${{ inputs.version }} | cut -d '.' - f-1).x >> $GITHUB_ENV + echo DEV_BRANCH=$(echo ${{ inputs.version }} | cut -d '.' -f-1).x >> $GITHUB_ENV - name: "Ensure release tag does not already exist" run: | diff --git a/docs/fundamentals/read-operations.txt b/docs/fundamentals/read-operations.txt index f3b02c5ec..367e2d38d 100644 --- a/docs/fundamentals/read-operations.txt +++ b/docs/fundamentals/read-operations.txt @@ -10,7 +10,14 @@ Read Operations :values: tutorial .. meta:: - :keywords: find one, find many, code example + :keywords: find one, find many, skip, limit, paginate, string, code example + +.. toctree:: + + Retrieve Data + Search Text + Modify Query Results + Set Read Preference .. contents:: On this page :local: @@ -21,697 +28,169 @@ Read Operations Overview -------- -In this guide, you can learn how to use {+odm-long+} to perform **find operations** -on your MongoDB collections. Find operations allow you to retrieve documents based on -criteria that you specify. - -This guide shows you how to perform the following tasks: - -- :ref:`laravel-retrieve-matching` -- :ref:`laravel-retrieve-all` -- :ref:`laravel-retrieve-text-search` -- :ref:`Modify Find Operation Behavior ` - -Before You Get Started ----------------------- - -To run the code examples in this guide, complete the :ref:`Quick Start ` -tutorial. This tutorial provides instructions on setting up a MongoDB Atlas instance with -sample data and creating the following files in your Laravel web application: - -- ``Movie.php`` file, which contains a ``Movie`` model to represent documents in the ``movies`` - collection -- ``MovieController.php`` file, which contains a ``show()`` function to run database operations -- ``browse_movies.blade.php`` file, which contains HTML code to display the results of database - operations - -The following sections describe how to edit the files in your Laravel application to run -the find operation code examples and view the expected output. - -.. _laravel-retrieve-matching: - -Retrieve Documents that Match a Query -------------------------------------- - -You can use Laravel's Eloquent object-relational mapper (ORM) to create models -that represent MongoDB collections and chain methods on them to specify -query criteria. - -To retrieve documents that match a set of criteria, call the ``where()`` -method on the collection's corresponding Eloquent model, then pass a query -filter to the method. - -A query filter specifies field value requirements and instructs the find -operation to return only documents that meet these requirements. - -You can use one of the following ``where()`` method calls to build a query: - -- ``where('', )`` builds a query that matches documents in - which the target field has the exact specified value - -- ``where('', '', )`` builds a query - that matches documents in which the target field's value meets the comparison - criteria - -To apply multiple sets of criteria to the find operation, you can chain a series -of ``where()`` methods together. - -After building your query by using the ``where()`` method, chain the ``get()`` -method to retrieve the query results. - -This example calls two ``where()`` methods on the ``Movie`` Eloquent model to -retrieve documents that meet the following criteria: - -- ``year`` field has a value of ``2010`` -- ``imdb.rating`` nested field has a value greater than ``8.5`` - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax - - Use the following syntax to specify the query: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-query - :end-before: end-query - - .. tab:: Controller Method - :tabid: controller - - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: - - .. io-code-block:: - :copyable: true - - .. input:: - :language: php - - class MovieController - { - public function show() - { - $movies = Movie::where('year', 2010) - ->where('imdb.rating', '>', 8.5) - ->get(); - - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } - - .. output:: - :language: none - :visible: false - - Title: Inception - Year: 2010 - Runtime: 148 - IMDB Rating: 8.8 - IMDB Votes: 1294646 - Plot: A thief who steals corporate secrets through use of dream-sharing - technology is given the inverse task of planting an idea into the mind of a CEO. - - Title: Senna - Year: 2010 - Runtime: 106 - IMDB Rating: 8.6 - IMDB Votes: 41904 - Plot: A documentary on Brazilian Formula One racing driver Ayrton Senna, who won the - F1 world championship three times before his death at age 34. - -To learn how to query by using the Laravel query builder instead of the -Eloquent ORM, see the :ref:`laravel-query-builder` page. - -Match Array Field Elements -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can specify a query filter to match array field elements when -retrieving documents. If your documents contain an array field, you can -match documents based on if the value contains all or some specified -array elements. - -You can use one of the following ``where()`` method calls to build a -query on an array field: - -- ``where('', )`` builds a query that matches documents in - which the array field value is exactly the specified array +In this guide, you can see code templates of common +methods that you can use to read data from MongoDB by using +{+odm-long+}. -- ``where('', 'in', )`` builds a query - that matches documents in which the array field value contains one or - more of the specified array elements - -After building your query by using the ``where()`` method, chain the ``get()`` -method to retrieve the query results. - -Select from the following :guilabel:`Exact Array Match` and -:guilabel:`Element Match` tabs to view the query syntax for each pattern: - -.. tabs:: - - .. tab:: Exact Array Match - :tabid: exact-array - - This example retrieves documents in which the ``countries`` array is - exactly ``['Indonesia', 'Canada']``: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-exact-array - :end-before: end-exact-array - - .. tab:: Element Match - :tabid: element-match - - This example retrieves documents in which the ``countries`` array - contains one of the values in the array ``['Canada', 'Egypt']``: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-elem-match - :end-before: end-elem-match - -To learn how to query array fields by using the Laravel query builder instead of the -Eloquent ORM, see the :ref:`laravel-query-builder-elemMatch` section in -the Query Builder guide. - -.. _laravel-retrieve-all: +.. tip:: -Retrieve All Documents in a Collection --------------------------------------- + To learn more about any of the methods included in this guide, + see the links provided in each section. -You can retrieve all documents in a collection by omitting the query filter. -To return the documents, call the ``get()`` method on an Eloquent model that -represents your collection. Alternatively, you can use the ``get()`` method's -alias ``all()`` to perform the same operation. +Find One +-------- -Use the following syntax to run a find operation that matches all documents: +The following code shows how to retrieve the first matching document +from a collection: .. code-block:: php - $movies = Movie::get(); - -.. warning:: - - The ``movies`` collection in the Atlas sample dataset contains a large amount of data. - Retrieving and displaying all documents in this collection might cause your web - application to time out. - - To avoid this issue, specify a document limit by using the ``take()`` method. For - more information about ``take()``, see the :ref:`laravel-modify-find` section of this - guide. - -.. _laravel-retrieve-text-search: - -Search Text Fields ------------------- - -A text search retrieves documents that contain a **term** or a **phrase** in the -text-indexed fields. A term is a sequence of characters that excludes -whitespace characters. A phrase is a sequence of terms with any number -of whitespace characters. + SampleModel::where('', '') + ->first(); -.. note:: +To view a runnable example that finds one document, see the +:ref:`laravel-find-one-usage` usage example. - Before you can perform a text search, you must create a :manual:`text - index ` on - the text-valued field. To learn more about creating - indexes, see the :ref:`laravel-eloquent-indexes` section of the - Schema Builder guide. +To learn more about retrieving documents and the ``first()`` method, see +the :ref:`laravel-fundamentals-read-retrieve` guide. -You can perform a text search by using the :manual:`$text -` operator followed -by the ``$search`` field in your query filter that you pass to the -``where()`` method. The ``$text`` operator performs a text search on the -text-indexed fields. The ``$search`` field specifies the text to search for. +Find Multiple +------------- -After building your query by using the ``where()`` method, chain the ``get()`` -method to retrieve the query results. +The following code shows how to retrieve all documents that match a +query filter from a collection: -This example calls the ``where()`` method on the ``Movie`` Eloquent model to -retrieve documents in which the ``plot`` field contains the phrase -``"love story"``. To perform this text search, the collection must have -a text index on the ``plot`` field. - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax +.. code-block:: php - Use the following syntax to specify the query: + SampleModel::where('', '') + ->get(); - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-text - :end-before: end-text +To view a runnable example that finds documents, see the +:ref:`laravel-find-usage` usage example. - .. tab:: Controller Method - :tabid: controller +To learn more about retrieving documents, see the +:ref:`laravel-fundamentals-read-retrieve` guide. - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: +Return All Documents +-------------------- - .. io-code-block:: - :copyable: true +The following code shows how to retrieve all documents from a +collection: - .. input:: - :language: php +.. code-block:: php - class MovieController - { - public function show() - { - $movies = Movie::where('$text', ['$search' => '"love story"']) - ->get(); + SampleModel::get(); - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } + // Or, use the all() method. + SampleModel::all(); - .. output:: - :language: none - :visible: false +To view a runnable example that finds documents, see the +:ref:`laravel-find-usage` usage example. - Title: Cafè de Flore - Year: 2011 - Runtime: 120 - IMDB Rating: 7.4 - IMDB Votes: 9663 - Plot: A love story between a man and woman ... +To learn more about retrieving documents, see the +:ref:`laravel-fundamentals-read-retrieve` guide. - Title: Paheli - Year: 2005 - Runtime: 140 - IMDB Rating: 6.7 - IMDB Votes: 8909 - Plot: A folk tale - supernatural love story about a ghost ... +Search Text +----------- - Title: Por un puèado de besos - Year: 2014 - Runtime: 98 - IMDB Rating: 6.1 - IMDB Votes: 223 - Plot: A girl. A boy. A love story ... - - ... - -A text search assigns a numerical :manual:`text score ` to indicate how closely -each result matches the string in your query filter. You can sort the -results by relevance by using the ``orderBy()`` method to sort on the -``textScore`` metadata field. You can access this metadata by using the -:manual:`$meta ` operator: - -.. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-text-relevance - :end-before: end-text-relevance - :emphasize-lines: 2 +The following code shows how to perform a full-text search on a string +field in a collection's documents: -.. tip:: +.. code-block:: php - To learn more about the ``orderBy()`` method, see the - :ref:`laravel-sort` section of this guide. + SampleModel::where('$text', ['$search' => '']) + ->get(); -.. _laravel-modify-find: +To learn more about searching on text fields, see the +:ref:`laravel-retrieve-text-search` guide. -Modify Behavior +Count Documents --------------- -You can modify the results of a find operation by chaining more methods -to ``where()``. - -The following sections demonstrate how to modify the behavior of the ``where()`` -method: - -- :ref:`laravel-skip-limit` uses the ``skip()`` method to set the number of documents - to skip and the ``take()`` method to set the total number of documents to return -- :ref:`laravel-sort` uses the ``orderBy()`` method to return query - results in a specified order based on field values -- :ref:`laravel-retrieve-one` uses the ``first()`` method to return the first document - that matches the query filter -- :ref:`laravel-read-pref` uses the ``readPreference()`` method to direct the query - to specific replica set members - -.. _laravel-skip-limit: - -Skip and Limit Results -~~~~~~~~~~~~~~~~~~~~~~ - -This example queries for documents in which the ``year`` value is ``1999``. -The operation skips the first ``2`` matching documents and outputs a total of ``3`` -documents. - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax - - Use the following syntax to specify the query: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-skip-limit - :end-before: end-skip-limit - - .. tab:: Controller Method - :tabid: controller +The following code shows how to count documents in a collection: - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: - - .. io-code-block:: - :copyable: true - - .. input:: - :language: php - - class MovieController - { - public function show() - { - $movies = Movie::where('year', 1999) - ->skip(2) - ->take(3) - ->get(); - - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } - - .. output:: - :language: none - :visible: false - - Title: Three Kings - Year: 1999 - Runtime: 114 - IMDB Rating: 7.2 - IMDB Votes: 130677 - Plot: In the aftermath of the Persian Gulf War, 4 soldiers set out to steal gold - that was stolen from Kuwait, but they discover people who desperately need their help. - - Title: Toy Story 2 - Year: 1999 - Runtime: 92 - IMDB Rating: 7.9 - IMDB Votes: 346655 - Plot: When Woody is stolen by a toy collector, Buzz and his friends vow to rescue him, - but Woody finds the idea of immortality in a museum tempting. - - Title: Beowulf - Year: 1999 - Runtime: 95 - IMDB Rating: 4 - IMDB Votes: 9296 - Plot: A sci-fi update of the famous 6th Century poem. In a besieged land, Beowulf must - battle against the hideous creature Grendel and his vengeance seeking mother. - -.. _laravel-sort: - -Sort Query Results -~~~~~~~~~~~~~~~~~~ - -To order query results based on the values of specified fields, use the ``where()`` method -followed by the ``orderBy()`` method. - -You can set an **ascending** or **descending** sort direction on -results. By default, the ``orderBy()`` method sets an ascending sort on -the supplied field name, but you can explicitly specify an ascending -sort by passing ``"asc"`` as the second parameter. To -specify a descending sort, pass ``"desc"`` as the second parameter. - -If your documents contain duplicate values in a specific field, you can -handle the tie by specifying more fields to sort on. This ensures consistent -results if the other fields contain unique values. - -This example queries for documents in which the value of the ``countries`` field contains -``"Indonesia"`` and orders results first by an ascending sort on the -``year`` field, then a descending sort on the ``title`` field. - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax - - Use the following syntax to specify the query: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-sort - :end-before: end-sort - - .. tab:: Controller Method - :tabid: controller - - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: - - .. io-code-block:: - :copyable: true - - .. input:: - :language: php - - class MovieController - { - public function show() - { - $movies = Movie::where('countries', 'Indonesia') - ->orderBy('year') - ->orderBy('title', 'desc') - ->get(); - - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } - - .. output:: - :language: none - :visible: false - - Title: Joni's Promise - Year: 2005 - Runtime: 83 - IMDB Rating: 7.6 - IMDB Votes: 702 - Plot: A film delivery man promises ... - - Title: Gie - Year: 2005 - Runtime: 147 - IMDB Rating: 7.5 - IMDB Votes: 470 - Plot: Soe Hok Gie is an activist who lived in the sixties ... - - Title: Requiem from Java - Year: 2006 - Runtime: 120 - IMDB Rating: 6.6 - IMDB Votes: 316 - Plot: Setyo (Martinus Miroto) and Siti (Artika Sari Dewi) - are young married couple ... - - ... +.. code-block:: php -.. tip:: + SampleModel::count(); - To learn more about sorting, see the following resources: + // You can also count documents that match a filter. + SampleModel::where('', '') + ->count(); - - :manual:`Natural order ` - in the {+server-docs-name+} glossary - - `Ordering, Grouping, Limit, and Offset `__ - in the Laravel documentation +To view a runnable example that counts documents, see the +:ref:`laravel-count-usage` usage example. -.. _laravel-retrieve-one: +Retrieve Distinct Values +------------------------ -Return the First Result -~~~~~~~~~~~~~~~~~~~~~~~ +The following code shows how to retrieve the distinct values of a +specified field: -To retrieve the first document that matches a set of criteria, use the ``where()`` method -followed by the ``first()`` method. +.. code-block:: php -Chain the ``orderBy()`` method to ``first()`` to get consistent results when you query on a unique -value. If you omit the ``orderBy()`` method, MongoDB returns the matching documents according to -the documents' natural order, or as they appear in the collection. + SampleModel::select('') + ->distinct() + ->get(); -This example queries for documents in which the value of the ``runtime`` field is -``30`` and returns the first matching document according to the value of the ``_id`` -field. +To view a runnable example that returns distinct field values, see the +:ref:`laravel-distinct-usage` usage example. -.. tabs:: +Skip Results +------------ - .. tab:: Query Syntax - :tabid: query-syntax +The following code shows how to skip a specified number of documents +returned from MongoDB: - Use the following syntax to specify the query: +.. code-block:: php - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-first - :end-before: end-first + SampleModel::where('', '') + ->skip() + ->get(); - .. tab:: Controller Method - :tabid: controller +To learn more about modifying how {+odm-long+} returns results, see the +:ref:`laravel-read-modify-results` guide. - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: +Limit Results +------------- - .. io-code-block:: - :copyable: true +The following code shows how to return only a specified number of +documents from MongoDB: - .. input:: - :language: php +.. code-block:: php - class MovieController - { - public function show() - { - $movie = Movie::where('runtime', 30) - ->orderBy('_id') - ->first(); + SampleModel::where('', '') + ->take() + ->get(); - return view('browse_movies', [ - 'movies' => $movie - ]); - } - } +To learn more about modifying how {+odm-long+} returns results, see the +:ref:`laravel-read-modify-results` guide. - .. output:: - :language: none - :visible: false +Sort Results +------------ - Title: Statues also Die - Year: 1953 - Runtime: 30 - IMDB Rating: 7.6 - IMDB Votes: 620 - Plot: A documentary of black art. +The following code shows how to set a sort order on results returned +from MongoDB: -.. tip:: +.. code-block:: php - To learn more about the ``orderBy()`` method, see the - :ref:`laravel-sort` section of this guide. + SampleModel::where('field name', '') + ->orderBy('') + ->get(); -.. _laravel-read-pref: +To learn more about modifying how {+odm-long+} returns results, see the +:ref:`laravel-read-modify-results` guide. Set a Read Preference -~~~~~~~~~~~~~~~~~~~~~ +--------------------- -To specify which replica set members receive your read operations, -set a read preference by using the ``readPreference()`` method. +The following code shows how to set a read preference when performing a +find operation: -The ``readPreference()`` method accepts the following parameters: - -- ``mode``: *(Required)* A string value specifying the read preference - mode. - -- ``tagSets``: *(Optional)* An array value specifying key-value tags that correspond to - certain replica set members. - -- ``options``: *(Optional)* An array value specifying additional read preference options. +.. code-block:: php -.. tip:: + SampleModel::where('field name', '') + ->readPreference(ReadPreference::SECONDARY_PREFERRED) + ->get(); - To view a full list of available read preference modes and options, see - :php:`MongoDB\Driver\ReadPreference::__construct ` - in the MongoDB PHP extension documentation. - -The following example queries for documents in which the value of the ``title`` -field is ``"Carrie"`` and sets the read preference to ``ReadPreference::SECONDARY_PREFERRED``. -As a result, the query retrieves the results from secondary replica set -members or the primary member if no secondaries are available: - -.. tabs:: - - .. tab:: Query Syntax - :tabid: query-syntax - - Use the following syntax to specify the query: - - .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php - :language: php - :dedent: - :start-after: start-read-pref - :end-before: end-read-pref - - .. tab:: Controller Method - :tabid: controller - - To see the query results in the ``browse_movies`` view, edit the ``show()`` function - in the ``MovieController.php`` file to resemble the following code: - - .. io-code-block:: - :copyable: true - - .. input:: - :language: php - - class MovieController - { - public function show() - { - $movies = Movie::where('title', 'Carrie') - ->readPreference(ReadPreference::SECONDARY_PREFERRED) - ->get(); - - return view('browse_movies', [ - 'movies' => $movies - ]); - } - } - - .. output:: - :language: none - :visible: false - - Title: Carrie - Year: 1952 - Runtime: 118 - IMDB Rating: 7.5 - IMDB Votes: 1458 - Plot: Carrie boards the train to Chicago with big ambitions. She gets a - job stitching shoes and her sister's husband takes almost all of her pay - for room and board. Then she injures a finger and ... - - Title: Carrie - Year: 1976 - Runtime: 98 - IMDB Rating: 7.4 - IMDB Votes: 115528 - Plot: A shy, outcast 17-year old girl is humiliated by her classmates for the - last time. - - Title: Carrie - Year: 2002 - Runtime: 132 - IMDB Rating: 5.5 - IMDB Votes: 7412 - Plot: Carrie White is a lonely and painfully shy teenage girl with telekinetic - powers who is slowly pushed to the edge of insanity by frequent bullying from - both her classmates and her domineering, religious mother. - - Title: Carrie - Year: 2013 - Runtime: 100 - IMDB Rating: 6 - IMDB Votes: 98171 - Plot: A reimagining of the classic horror tale about Carrie White, a shy girl - outcast by her peers and sheltered by her deeply religious mother, who unleashes - telekinetic terror on her small town after being pushed too far at her senior prom. +To learn more about read preferences, see the :ref:`laravel-read-pref` +guide. diff --git a/docs/fundamentals/read-operations/modify-results.txt b/docs/fundamentals/read-operations/modify-results.txt new file mode 100644 index 000000000..fd67422ae --- /dev/null +++ b/docs/fundamentals/read-operations/modify-results.txt @@ -0,0 +1,227 @@ +.. _laravel-modify-find: +.. _laravel-read-modify-results: + +==================== +Modify Query Results +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: filter, criteria, CRUD, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to customize the way that {+odm-long+} +returns results from queries. You can modify the results of a find +operation by chaining more methods to the ``where()`` method. + +The following sections demonstrate how to modify the behavior of the +``where()`` method: + +- :ref:`laravel-skip-limit` uses the ``skip()`` method to set the number of documents + to skip and the ``take()`` method to set the total number of documents to return +- :ref:`laravel-sort` uses the ``orderBy()`` method to return query + results in a specified order based on field values + +To learn more about Eloquent models in the {+odm-short+}, see the +:ref:`laravel-eloquent-models` section. + +.. include:: /includes/fundamentals/read-operations/before-you-get-started.rst + +.. _laravel-skip-limit: + +Skip and Limit Results +---------------------- + +This example queries for documents in which the ``year`` value is ``1999``. +The operation skips the first ``2`` matching documents and outputs a total of ``3`` +documents. + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-skip-limit + :end-before: end-skip-limit + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movies = Movie::where('year', 1999) + ->skip(2) + ->take(3) + ->get(); + + return view('browse_movies', [ + 'movies' => $movies + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Three Kings + Year: 1999 + Runtime: 114 + IMDB Rating: 7.2 + IMDB Votes: 130677 + Plot: In the aftermath of the Persian Gulf War, 4 soldiers set out to steal gold + that was stolen from Kuwait, but they discover people who desperately need their help. + + Title: Toy Story 2 + Year: 1999 + Runtime: 92 + IMDB Rating: 7.9 + IMDB Votes: 346655 + Plot: When Woody is stolen by a toy collector, Buzz and his friends vow to rescue him, + but Woody finds the idea of immortality in a museum tempting. + + Title: Beowulf + Year: 1999 + Runtime: 95 + IMDB Rating: 4 + IMDB Votes: 9296 + Plot: A sci-fi update of the famous 6th Century poem. In a besieged land, Beowulf must + battle against the hideous creature Grendel and his vengeance seeking mother. + +.. _laravel-sort: + +Sort Query Results +------------------ + +To order query results based on the values of specified fields, use the ``where()`` method +followed by the ``orderBy()`` method. + +You can set an **ascending** or **descending** sort direction on +results. By default, the ``orderBy()`` method sets an ascending sort on +the supplied field name, but you can explicitly specify an ascending +sort by passing ``"asc"`` as the second parameter. To +specify a descending sort, pass ``"desc"`` as the second parameter. + +If your documents contain duplicate values in a specific field, you can +handle the tie by specifying more fields to sort on. This ensures consistent +results if the other fields contain unique values. + +This example queries for documents in which the value of the ``countries`` field contains +``"Indonesia"`` and orders results first by an ascending sort on the +``year`` field, then a descending sort on the ``title`` field. + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-sort + :end-before: end-sort + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movies = Movie::where('countries', 'Indonesia') + ->orderBy('year') + ->orderBy('title', 'desc') + ->get(); + + return view('browse_movies', [ + 'movies' => $movies + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Joni's Promise + Year: 2005 + Runtime: 83 + IMDB Rating: 7.6 + IMDB Votes: 702 + Plot: A film delivery man promises ... + + Title: Gie + Year: 2005 + Runtime: 147 + IMDB Rating: 7.5 + IMDB Votes: 470 + Plot: Soe Hok Gie is an activist who lived in the sixties ... + + Title: Requiem from Java + Year: 2006 + Runtime: 120 + IMDB Rating: 6.6 + IMDB Votes: 316 + Plot: Setyo (Martinus Miroto) and Siti (Artika Sari Dewi) + are young married couple ... + + ... + +.. tip:: + + To learn more about sorting, see the following resources: + + - :manual:`Natural order ` + in the {+server-docs-name+} glossary + - `Ordering, Grouping, Limit, and Offset `__ + in the Laravel documentation + +Additional Information +---------------------- + +To view runnable code examples that demonstrate how to perform find +operations by using the {+odm-short+}, see the following usage examples: + +- :ref:`laravel-find-one-usage` +- :ref:`laravel-find-usage` + +To learn how to retrieve data based on filter criteria, see the +:ref:`laravel-fundamentals-read-retrieve` guide. diff --git a/docs/fundamentals/read-operations/read-pref.txt b/docs/fundamentals/read-operations/read-pref.txt new file mode 100644 index 000000000..075c74380 --- /dev/null +++ b/docs/fundamentals/read-operations/read-pref.txt @@ -0,0 +1,141 @@ +.. _laravel-read-pref: + +===================== +Set a Read Preference +===================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: consistency, durability, CRUD, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to set a read preference when +performing find operations with {+odm-long+}. + +.. include:: /includes/fundamentals/read-operations/before-you-get-started.rst + +Set a Read Preference +--------------------- + +To specify which replica set members receive your read operations, +set a read preference by using the ``readPreference()`` method. + +The ``readPreference()`` method accepts the following parameters: + +- ``mode``: *(Required)* A string value specifying the read preference + mode. + +- ``tagSets``: *(Optional)* An array value specifying key-value tags that correspond to + certain replica set members. + +- ``options``: *(Optional)* An array value specifying additional read preference options. + +.. tip:: + + To view a full list of available read preference modes and options, see + :php:`MongoDB\Driver\ReadPreference::__construct ` + in the MongoDB PHP extension documentation. + +The following example queries for documents in which the value of the ``title`` +field is ``"Carrie"`` and sets the read preference to ``ReadPreference::SECONDARY_PREFERRED``. +As a result, the query retrieves the results from secondary replica set +members or the primary member if no secondaries are available: + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-read-pref + :end-before: end-read-pref + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movies = Movie::where('title', 'Carrie') + ->readPreference(ReadPreference::SECONDARY_PREFERRED) + ->get(); + + return view('browse_movies', [ + 'movies' => $movies + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Carrie + Year: 1952 + Runtime: 118 + IMDB Rating: 7.5 + IMDB Votes: 1458 + Plot: Carrie boards the train to Chicago with big ambitions. She gets a + job stitching shoes and her sister's husband takes almost all of her pay + for room and board. Then she injures a finger and ... + + Title: Carrie + Year: 1976 + Runtime: 98 + IMDB Rating: 7.4 + IMDB Votes: 115528 + Plot: A shy, outcast 17-year old girl is humiliated by her classmates for the + last time. + + Title: Carrie + Year: 2002 + Runtime: 132 + IMDB Rating: 5.5 + IMDB Votes: 7412 + Plot: Carrie White is a lonely and painfully shy teenage girl with telekinetic + powers who is slowly pushed to the edge of insanity by frequent bullying from + both her classmates and her domineering, religious mother. + + Title: Carrie + Year: 2013 + Runtime: 100 + IMDB Rating: 6 + IMDB Votes: 98171 + Plot: A reimagining of the classic horror tale about Carrie White, a shy girl + outcast by her peers and sheltered by her deeply religious mother, who unleashes + telekinetic terror on her small town after being pushed too + far at her senior prom. + +Additional Information +---------------------- + +To learn how to retrieve data based on filter criteria, see the +:ref:`laravel-fundamentals-read-retrieve` guide. + +To learn how to modify the way that the {+odm-short+} returns results, +see the :ref:`laravel-read-modify-results` guide. diff --git a/docs/fundamentals/read-operations/retrieve.txt b/docs/fundamentals/read-operations/retrieve.txt new file mode 100644 index 000000000..a4ca31091 --- /dev/null +++ b/docs/fundamentals/read-operations/retrieve.txt @@ -0,0 +1,304 @@ +.. _laravel-fundamentals-retrieve-documents: +.. _laravel-fundamentals-read-retrieve: + +============= +Retrieve Data +============= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: filter, criteria, CRUD, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to retrieve data from MongoDB +collections by using {+odm-long+}. This guide describes the Eloquent +model methods that you can use to retrieve data and provides examples +of different types of find operations. + +To learn more about Eloquent models in the {+odm-short+}, see the +:ref:`laravel-eloquent-models` section. + +.. include:: /includes/fundamentals/read-operations/before-you-get-started.rst + +.. _laravel-retrieve-matching: + +Retrieve Documents that Match a Query +------------------------------------- + +You can use Laravel's Eloquent object-relational mapper (ORM) to create models +that represent MongoDB collections and chain methods on them to specify +query criteria. + +To retrieve documents that match a set of criteria, call the ``where()`` +method on the collection's corresponding Eloquent model, then pass a query +filter to the method. + +.. tip:: Retrieve One Document + + The ``where()`` method retrieves all matching documents. To retrieve + the first matching document, you can chain the ``first()`` method. To + learn more and view an example, see the :ref:`laravel-retrieve-one` + section of this guide. + +A query filter specifies field value requirements and instructs the find +operation to return only documents that meet these requirements. + +You can use one of the following ``where()`` method calls to build a query: + +- ``where('', )`` builds a query that matches documents in + which the target field has the exact specified value + +- ``where('', '', )`` builds a query + that matches documents in which the target field's value meets the comparison + criteria + +To apply multiple sets of criteria to the find operation, you can chain a series +of ``where()`` methods together. + +After building your query by using the ``where()`` method, chain the ``get()`` +method to retrieve the query results. + +This example calls two ``where()`` methods on the ``Movie`` Eloquent model to +retrieve documents that meet the following criteria: + +- ``year`` field has a value of ``2010`` +- ``imdb.rating`` nested field has a value greater than ``8.5`` + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-query + :end-before: end-query + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movies = Movie::where('year', 2010) + ->where('imdb.rating', '>', 8.5) + ->get(); + + return view('browse_movies', [ + 'movies' => $movies + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Inception + Year: 2010 + Runtime: 148 + IMDB Rating: 8.8 + IMDB Votes: 1294646 + Plot: A thief who steals corporate secrets through use of dream-sharing + technology is given the inverse task of planting an idea into the mind of a CEO. + + Title: Senna + Year: 2010 + Runtime: 106 + IMDB Rating: 8.6 + IMDB Votes: 41904 + Plot: A documentary on Brazilian Formula One racing driver Ayrton Senna, who won the + F1 world championship three times before his death at age 34. + +To learn how to query by using the Laravel query builder instead of the +Eloquent ORM, see the :ref:`laravel-query-builder` page. + +Match Array Field Elements +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify a query filter to match array field elements when +retrieving documents. If your documents contain an array field, you can +match documents based on if the value contains all or some specified +array elements. + +You can use one of the following ``where()`` method calls to build a +query on an array field: + +- ``where('', )`` builds a query that matches documents in + which the array field value is exactly the specified array + +- ``where('', 'in', )`` builds a query + that matches documents in which the array field value contains one or + more of the specified array elements + +After building your query by using the ``where()`` method, chain the ``get()`` +method to retrieve the query results. + +Select from the following :guilabel:`Exact Array Match` and +:guilabel:`Element Match` tabs to view the query syntax for each pattern: + +.. tabs:: + + .. tab:: Exact Array Match + :tabid: exact-array + + This example retrieves documents in which the ``countries`` array is + exactly ``['Indonesia', 'Canada']``: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-exact-array + :end-before: end-exact-array + + .. tab:: Element Match + :tabid: element-match + + This example retrieves documents in which the ``countries`` array + contains one of the values in the array ``['Canada', 'Egypt']``: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-elem-match + :end-before: end-elem-match + +To learn how to query array fields by using the Laravel query builder instead of the +Eloquent ORM, see the :ref:`laravel-query-builder-elemMatch` section in +the Query Builder guide. + +.. _laravel-retrieve-one: + +Retrieve the First Result +------------------------- + +To retrieve the first document that matches a set of criteria, use the ``where()`` method +followed by the ``first()`` method. + +Chain the ``orderBy()`` method to ``first()`` to get consistent results when you query on a unique +value. If you omit the ``orderBy()`` method, MongoDB returns the matching documents according to +the documents' natural order, or as they appear in the collection. + +This example queries for documents in which the value of the ``runtime`` field is +``30`` and returns the first matching document according to the value of the ``_id`` +field. + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-first + :end-before: end-first + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movie = Movie::where('runtime', 30) + ->orderBy('_id') + ->first(); + + return view('browse_movies', [ + 'movies' => $movie + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Statues also Die + Year: 1953 + Runtime: 30 + IMDB Rating: 7.6 + IMDB Votes: 620 + Plot: A documentary of black art. + +.. tip:: + + To learn more about the ``orderBy()`` method, see the + :ref:`laravel-sort` section of the Modify Query Results guide. + +.. _laravel-retrieve-all: + +Retrieve All Documents in a Collection +-------------------------------------- + +You can retrieve all documents in a collection by omitting the query filter. +To return the documents, call the ``get()`` method on an Eloquent model that +represents your collection. Alternatively, you can use the ``get()`` method's +alias ``all()`` to perform the same operation. + +Use the following syntax to run a find operation that matches all documents: + +.. code-block:: php + + $movies = Movie::get(); + +.. warning:: + + The ``movies`` collection in the Atlas sample dataset contains a large amount of data. + Retrieving and displaying all documents in this collection might cause your web + application to time out. + + To avoid this issue, specify a document limit by using the ``take()`` method. For + more information about ``take()``, see the :ref:`laravel-modify-find` + section of the Modify Query Output guide. + +Additional Information +---------------------- + +To view runnable code examples that demonstrate how to perform find +operations by using the {+odm-short+}, see the following usage examples: + +- :ref:`laravel-find-one-usage` +- :ref:`laravel-find-usage` + +To learn how to insert data into MongoDB, see the +:ref:`laravel-fundamentals-write-ops` guide. + +To learn how to modify the way that the {+odm-short+} returns results, +see the :ref:`laravel-read-modify-results` guide. diff --git a/docs/fundamentals/read-operations/search-text.txt b/docs/fundamentals/read-operations/search-text.txt new file mode 100644 index 000000000..4b465e737 --- /dev/null +++ b/docs/fundamentals/read-operations/search-text.txt @@ -0,0 +1,157 @@ +.. _laravel-fundamentals-search-text: +.. _laravel-retrieve-text-search: + +=========== +Search Text +=========== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: filter, string, CRUD, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to run a **text search** by using +{+odm-long+}. + +You can use a text search to retrieve documents that contain a term or a +phrase in a specified field. A term is a sequence of characters that +excludes whitespace characters. A phrase is a sequence of terms with any +number of whitespace characters. + +This guide describes the Eloquent model methods that you can use to +search text and provides examples. To learn more about Eloquent models +in the {+odm-short+}, see the :ref:`laravel-eloquent-models` section. + +.. include:: /includes/fundamentals/read-operations/before-you-get-started.rst + +Search Text Fields +------------------ + +Before you can perform a text search, you must create a :manual:`text +index ` on +the text-valued field. To learn more about creating +indexes, see the :ref:`laravel-eloquent-indexes` section of the +Schema Builder guide. + +You can perform a text search by using the :manual:`$text +` operator followed +by the ``$search`` field in your query filter that you pass to the +``where()`` method. The ``$text`` operator performs a text search on the +text-indexed fields. The ``$search`` field specifies the text to search for. + +After building your query by using the ``where()`` method, chain the ``get()`` +method to retrieve the query results. + +This example calls the ``where()`` method on the ``Movie`` Eloquent model to +retrieve documents in which the ``plot`` field contains the phrase +``"love story"``. To perform this text search, the collection must have +a text index on the ``plot`` field. + +.. tabs:: + + .. tab:: Query Syntax + :tabid: query-syntax + + Use the following syntax to specify the query: + + .. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-text + :end-before: end-text + + .. tab:: Controller Method + :tabid: controller + + To see the query results in the ``browse_movies`` view, edit the ``show()`` function + in the ``MovieController.php`` file to resemble the following code: + + .. io-code-block:: + :copyable: true + + .. input:: + :language: php + + class MovieController + { + public function show() + { + $movies = Movie::where('$text', ['$search' => '"love story"']) + ->get(); + + return view('browse_movies', [ + 'movies' => $movies + ]); + } + } + + .. output:: + :language: none + :visible: false + + Title: Cafè de Flore + Year: 2011 + Runtime: 120 + IMDB Rating: 7.4 + IMDB Votes: 9663 + Plot: A love story between a man and woman ... + + Title: Paheli + Year: 2005 + Runtime: 140 + IMDB Rating: 6.7 + IMDB Votes: 8909 + Plot: A folk tale - supernatural love story about a ghost ... + + Title: Por un puèado de besos + Year: 2014 + Runtime: 98 + IMDB Rating: 6.1 + IMDB Votes: 223 + Plot: A girl. A boy. A love story ... + + ... + +Search Score +------------ + +A text search assigns a numerical :manual:`text score ` to indicate how closely +each result matches the string in your query filter. You can sort the +results by relevance by using the ``orderBy()`` method to sort on the +``textScore`` metadata field. You can access this metadata by using the +:manual:`$meta ` operator: + +.. literalinclude:: /includes/fundamentals/read-operations/ReadOperationsTest.php + :language: php + :dedent: + :start-after: start-text-relevance + :end-before: end-text-relevance + :emphasize-lines: 2 + +.. tip:: + + To learn more about the ``orderBy()`` method, see the + :ref:`laravel-sort` section of the Modify Query Output guide. + +Additional Information +---------------------- + +To view runnable code examples that demonstrate how to perform find +operations by using the {+odm-short+}, see the following usage examples: + +- :ref:`laravel-find-one-usage` +- :ref:`laravel-find-usage` + +To learn how to retrieve data based on filter criteria, see the +:ref:`laravel-fundamentals-read-retrieve` guide. diff --git a/docs/fundamentals/write-operations.txt b/docs/fundamentals/write-operations.txt index 0a4d8a6ca..1b2f163be 100644 --- a/docs/fundamentals/write-operations.txt +++ b/docs/fundamentals/write-operations.txt @@ -133,8 +133,7 @@ matching document doesn't exist: ['upsert' => true], ); - /* Or, use the upsert() method. */ - + // Or, use the upsert() method. SampleModel::upsert( [], '', diff --git a/docs/includes/fundamentals/read-operations/before-you-get-started.rst b/docs/includes/fundamentals/read-operations/before-you-get-started.rst new file mode 100644 index 000000000..9555856fc --- /dev/null +++ b/docs/includes/fundamentals/read-operations/before-you-get-started.rst @@ -0,0 +1,15 @@ +Before You Get Started +---------------------- + +To run the code examples in this guide, complete the :ref:`Quick Start ` +tutorial. This tutorial provides instructions on setting up a MongoDB Atlas instance with +sample data and creating the following files in your Laravel web application: + +- ``Movie.php`` file, which contains a ``Movie`` model to represent documents in the ``movies`` + collection +- ``MovieController.php`` file, which contains a ``show()`` function to run database operations +- ``browse_movies.blade.php`` file, which contains HTML code to display the results of database + operations + +The following sections describe how to edit the files in your Laravel application to run +the find operation code examples and view the expected output. From 937fb27f6e1c75f1e2fd5e3b1dd11f86f5bd1081 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:44:34 -0500 Subject: [PATCH 3/9] DOCSP-46479: document Scout integration (#3261) * DOCSP-46479: document Scout integration * NR PR fixes 1 * fix spacing * fix spacing * fix spacing * fix spacing * NR PR fixes 2 * JT tech comment * fix spacing * JT tech review 1 * JT tech review 1 * custom index * link to atlas doc --- docs/index.txt | 2 + docs/scout.txt | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 docs/scout.txt diff --git a/docs/index.txt b/docs/index.txt index 2937968a7..1eb1d8657 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -22,6 +22,7 @@ Databases & Collections User Authentication Cache & Locks + Scout Integration HTTP Sessions Queues Transactions @@ -86,6 +87,7 @@ see the following content: - :ref:`laravel-aggregation-builder` - :ref:`laravel-user-authentication` - :ref:`laravel-cache` +- :ref:`laravel-scout` - :ref:`laravel-sessions` - :ref:`laravel-queues` - :ref:`laravel-transactions` diff --git a/docs/scout.txt b/docs/scout.txt new file mode 100644 index 000000000..8f409148b --- /dev/null +++ b/docs/scout.txt @@ -0,0 +1,259 @@ +.. _laravel-scout: + +=========================== +Full-Text Search with Scout +=========================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: php framework, odm, code example, text search, atlas + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use the Laravel Scout feature in +your {+odm-long+} application. Scout enables you to implement full-text +search on your Eloquent models. To learn more, see `Laravel Scout +`__ in the +Laravel documentation. + +The Scout integration for {+odm-long+} provides the following +functionality: + +- Provides an abstraction to create :atlas:`Atlas Search indexes + ` from any MongoDB or SQL model. + + .. important:: Use Schema Builder to Create Search Indexes + + If your documents are already in MongoDB, create Search indexes + by using {+php-library+} or ``Schema`` builder methods to improve + search query performance. To learn more about creating Search + indexes, see the :ref:`laravel-as-index` section of the Atlas + Search guide. + +- Enables you to automatically replicate data from MongoDB into a + search engine such as `Meilisearch `__ + or `Algolia `__. You can use a MongoDB Eloquent + model as the source to import and index. To learn more about indexing + to a search engine, see the `Indexing + `__ + section of the Laravel Scout documentation. + +.. important:: Deployment Compatibility + + You can use Laravel Scout only when you connect to MongoDB Atlas + deployments. This feature is not available for self-managed + deployments. + +Scout for Atlas Search Tutorial +------------------------------- + +This tutorial demonstrates how to use Scout to compound and index +documents for MongoDB Atlas Search from Eloquent models (MongoDB or SQL). + +.. procedure:: + :style: connected + + .. step:: Install the Scout package + + Before you can use Scout in your application, run the following + command from your application's root directory to install the + ``laravel/scout`` package: + + .. code-block:: bash + + composer require laravel/scout + + .. step:: Add the Searchable trait to your model + + Add the ``Laravel\Scout\Searchable`` trait to an Eloquent model to make + it searchable. The following example adds this trait to the ``Movie`` + model, which represents documents in the ``sample_mflix.movies`` + collection: + + .. code-block:: php + :emphasize-lines: 6, 10 + + `__ + section of the Laravel Scout documentation. + + .. step:: Configure Scout in your application + + Ensure that your application is configured to use MongoDB as its + database connection. To learn how to configure MongoDB, see the + :ref:`laravel-quick-start-connect-to-mongodb` section of the Quick Start + guide. + + To configure Scout in your application, create a file named + ``scout.php`` in your application's ``config`` directory. Paste the + following code into the file to configure Scout: + + .. code-block:: php + :caption: config/scout.php + + env('SCOUT_DRIVER', 'mongodb'), + 'mongodb' => [ + 'connection' => env('SCOUT_MONGODB_CONNECTION', 'mongodb'), + ], + 'prefix' => env('SCOUT_PREFIX', 'scout_'), + ]; + + The preceding code specifies the following configuration: + + - Uses the value of the ``SCOUT_DRIVER`` environment variable as + the default search driver, or ``mongodb`` if the environment + variable is not set + + - Specifies ``scout_`` as the prefix for the collection name of the + searchable collection + + In the ``config/scout.php`` file, you can also specify a custom + Atlas Search index definition. To learn more, see the :ref:`custom + index definition example ` in the + following step. + + Set the following environment variable in your application's + ``.env`` file to select ``mongodb`` as the default search driver: + + .. code-block:: none + :caption: .env + + SCOUT_DRIVER=mongodb + + .. tip:: Queueing + + When using Scout, consider configuring a queue driver to reduce + response times for your application's web interface. To learn more, + see the `Queuing section + `__ + of the Laravel Scout documentation and the :ref:`laravel-queues` guide. + + .. step:: Create the Atlas Search index + + After you configure Scout and set your default search driver, you can + create your searchable collection and search index by running the + following command from your application's root directory: + + .. code-block:: bash + + php artisan scout:index 'App\Models\Movie' + + Because you set MongoDB as the default search driver, the preceding + command creates the search collection with an Atlas Search index in your + MongoDB database. The collection is named ``scout_movies``, based on the prefix + set in the preceding step. The Atlas Search index is named ``scout`` + and has the following configuration by default: + + .. code-block:: json + + { + "mappings": { + "dynamic": true + } + } + + .. _laravel-scout-custom-index: + + To customize the index definition, add the ``index-definitions`` + configuration to the ``mongodb`` entry in your + ``config/scout.php`` file. The following code demonstrates how to + specify a custom index definition to create on the + ``scout_movies`` collection: + + .. code-block:: php + + 'mongodb' => [ + 'connection' => env('SCOUT_MONGODB_CONNECTION', 'mongodb'), + 'index-definitions' => [ + 'scout_movies' => [ + 'mappings' => [ + 'dynamic' => false, + 'fields' => ['title' => ['type' => 'string']] + ] + ] + ] + ], ... + + To learn more about defining Atlas Search index definitions, see the + :atlas:`Define Field Mappings + ` guide in the Atlas + documentation. + + .. note:: + + MongoDB can take up to a minute to create and finalize + an Atlas Search index, so the ``scout:index`` command might not + return a success message immediately. + + .. step:: Import data into the searchable collection + + You can use Scout to replicate data from a source collection + modeled by your Eloquent model into a searchable collection. The + following command replicates and indexes data from the ``movies`` + collection into the ``scout_movies`` collection indexed in the + preceding step: + + .. code-block:: bash + + php artisan scout:import 'App\Models\Movie' + + The documents are automatically indexed for Atlas Search queries. + + .. tip:: Select Fields to Import + + You might not need all the fields from your source documents in your + searchable collection. Limiting the amount of data you replicate can improve + your application's speed and performance. + + You can select specific fields to import by defining the + ``toSearchableArray()`` method in your Eloquent model class. The + following code demonstrates how to define ``toSearchableArray()`` to + select only the ``plot`` and ``title`` fields for replication: + + .. code-block:: php + + class Movie extends Model + { + .... + public function toSearchableArray(): array + { + return [ + 'plot' => $this->plot, + 'title' => $this->title, + ]; + } + } + +After completing these steps, you can perform Atlas Search queries on the +``scout_movies`` collection in your {+odm-long+} application. To learn +how to perform full-text searches, see the :ref:`laravel-atlas-search` +guide. From 536327d0ef77d5fc3d8e421f5e8dd35e972daa45 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:09:19 -0500 Subject: [PATCH 4/9] DOCSP-48018: laravel 12 feature compat (#3304) * DOCSP-48018: laravel 12 feature compat * small fixes * JT fix --- docs/eloquent-models/model-class.txt | 7 ++++--- docs/feature-compatibility.txt | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/eloquent-models/model-class.txt b/docs/eloquent-models/model-class.txt index a2a9861bc..6f686e88a 100644 --- a/docs/eloquent-models/model-class.txt +++ b/docs/eloquent-models/model-class.txt @@ -200,9 +200,10 @@ model attribute, stored in MongoDB as a :php:`MongoDB\\BSON\\UTCDateTime .. tip:: Casts in Laravel 11 - In Laravel 11, you can define a ``casts()`` method to specify data type conversions - instead of using the ``$casts`` attribute. The following code performs the same - conversion as the preceding example by using a ``casts()`` method: + Starting in Laravel 11, you can define a ``casts()`` method to + specify data type conversions instead of using the ``$casts`` + attribute. The following code performs the same conversion as the + preceding example by using a ``casts()`` method: .. code-block:: php diff --git a/docs/feature-compatibility.txt b/docs/feature-compatibility.txt index 57c8c7486..965be2ebb 100644 --- a/docs/feature-compatibility.txt +++ b/docs/feature-compatibility.txt @@ -21,7 +21,7 @@ Overview -------- This guide describes the Laravel features that are supported by -{+odm-long+}. This page discusses Laravel version 11.x feature +{+odm-long+}. This page discusses Laravel version 12.x feature availability in the {+odm-short+}. The following sections contain tables that describe whether individual @@ -32,6 +32,7 @@ Database Features .. list-table:: :header-rows: 1 + :widths: 40 60 * - Eloquent Feature - Availability @@ -63,6 +64,12 @@ Database Features * - Database Monitoring - *Unsupported* + * - Multi-database Support / Multiple Schemas + - *Unsupported* Laravel uses a dot separator (``.``) + between SQL schema and table names, but MongoDB allows ``.`` + characters within collection names, which might lead to + unexpected namespace parsing. + Query Features -------------- @@ -114,19 +121,19 @@ The following Eloquent methods are not supported in the {+odm-short+}: * - Unions - *Unsupported* - * - `Basic Where Clauses `__ + * - `Basic Where Clauses `__ - ✓ - * - `Additional Where Clauses `__ + * - `Additional Where Clauses `__ - ✓ * - Logical Grouping - ✓ - * - `Advanced Where Clauses `__ + * - `Advanced Where Clauses `__ - ✓ - * - `Subquery Where Clauses `__ + * - `Subquery Where Clauses `__ - *Unsupported* * - Ordering @@ -136,7 +143,7 @@ The following Eloquent methods are not supported in the {+odm-short+}: - *Unsupported* * - Grouping - - Partially supported, use :ref:`Aggregations `. + - Partially supported. Use :ref:`Aggregations `. * - Limit and Offset - ✓ From 89772e239af7d9d3f51d29816ace06a34ca260ef Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Thu, 6 Mar 2025 10:38:47 -0500 Subject: [PATCH 5/9] DOCSP-47950: Fix all operator section (#3308) * DOCSP-47950: Fix all operator section * review feedback --- docs/includes/query-builder/QueryBuilderTest.php | 2 +- docs/query-builder.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/includes/query-builder/QueryBuilderTest.php b/docs/includes/query-builder/QueryBuilderTest.php index 574fe060f..3f7ea2274 100644 --- a/docs/includes/query-builder/QueryBuilderTest.php +++ b/docs/includes/query-builder/QueryBuilderTest.php @@ -351,7 +351,7 @@ public function testAll(): void { // begin query all $result = DB::table('movies') - ->where('movies', 'all', ['title', 'rated', 'imdb.rating']) + ->where('writers', 'all', ['Ben Affleck', 'Matt Damon']) ->get(); // end query all diff --git a/docs/query-builder.txt b/docs/query-builder.txt index c641323dc..68a9b2102 100644 --- a/docs/query-builder.txt +++ b/docs/query-builder.txt @@ -869,7 +869,8 @@ Contains All Fields Example The following example shows how to use the ``all`` query operator with the ``where()`` query builder method to match -documents that contain all the specified fields: +documents that have a ``writers`` array field containing all +the specified values: .. literalinclude:: /includes/query-builder/QueryBuilderTest.php :language: php From 4fd1b811d1b20eeaafde32f02c2501bf84b59d63 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:17:17 -0500 Subject: [PATCH 6/9] Remove link to builder package/repo (#3312) --- docs/fundamentals/aggregation-builder.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/fundamentals/aggregation-builder.txt b/docs/fundamentals/aggregation-builder.txt index 9ae31f0c1..47994ce9e 100644 --- a/docs/fundamentals/aggregation-builder.txt +++ b/docs/fundamentals/aggregation-builder.txt @@ -66,12 +66,6 @@ to build aggregation stages: - ``MongoDB\Builder\Query`` - ``MongoDB\Builder\Type`` -.. tip:: - - To learn more about builder classes, see the - :github:`mongodb/mongodb-php-builder ` - GitHub repository. - This section features the following examples that show how to use common aggregation stages: From b91a3c5f9afc49e54426e834256e91249feaeb8d Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Tue, 11 Mar 2025 09:35:38 -0400 Subject: [PATCH 7/9] fix line spacing in feature compat doc (#3315) --- docs/feature-compatibility.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/feature-compatibility.txt b/docs/feature-compatibility.txt index 965be2ebb..c36d30812 100644 --- a/docs/feature-compatibility.txt +++ b/docs/feature-compatibility.txt @@ -65,10 +65,11 @@ Database Features - *Unsupported* * - Multi-database Support / Multiple Schemas - - *Unsupported* Laravel uses a dot separator (``.``) - between SQL schema and table names, but MongoDB allows ``.`` - characters within collection names, which might lead to - unexpected namespace parsing. + - | *Unsupported* + | Laravel uses a dot separator (``.``) + between SQL schema and table names, but MongoDB allows ``.`` + characters within collection names, which might lead to + unexpected namespace parsing. Query Features -------------- From 1265bb1e9d5904e585822eb79e2d4d98c8254ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 31 Mar 2025 10:39:05 +0200 Subject: [PATCH 8/9] PHPORM-306 Test with MongoDB Driver v2 (#3319) --- .github/workflows/build-ci-atlas.yml | 23 ++- .github/workflows/build-ci.yml | 25 ++- .github/workflows/coding-standards.yml | 2 +- .github/workflows/static-analysis.yml | 17 +- composer.json | 4 +- src/Eloquent/Builder.php | 4 +- tests/QueryBuilderTest.php | 6 +- tests/Scout/ScoutEngineTest.php | 230 +++++++++++++------------ 8 files changed, 189 insertions(+), 122 deletions(-) diff --git a/.github/workflows/build-ci-atlas.yml b/.github/workflows/build-ci-atlas.yml index 30b4b06b1..339f8fc38 100644 --- a/.github/workflows/build-ci-atlas.yml +++ b/.github/workflows/build-ci-atlas.yml @@ -4,11 +4,15 @@ on: push: pull_request: +env: + MONGODB_EXT_V1: mongodb-1.21.0 + MONGODB_EXT_V2: mongodb-mongodb/mongo-php-driver@v2.x + jobs: build: runs-on: "${{ matrix.os }}" - name: "PHP ${{ matrix.php }} Laravel ${{ matrix.laravel }} Atlas" + name: "PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }}" strategy: matrix: @@ -21,6 +25,13 @@ jobs: laravel: - "11.*" - "12.*" + driver: + - 1 + include: + - php: "8.4" + laravel: "12.*" + os: "ubuntu-latest" + driver: 2 steps: - uses: "actions/checkout@v4" @@ -39,11 +50,19 @@ jobs: run: | docker exec --tty mongodb mongosh --eval "db.runCommand({ serverStatus: 1 })" + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} + key: "extcache-v1" + - name: "Installing php" uses: "shivammathur/setup-php@v2" with: php-version: ${{ matrix.php }} - extensions: "curl,mbstring,xdebug" + extensions: "curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" coverage: "xdebug" tools: "composer" diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 659c316d3..bc799c70e 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -4,11 +4,15 @@ on: push: pull_request: +env: + MONGODB_EXT_V1: mongodb-1.21.0 + MONGODB_EXT_V2: mongodb-mongodb/mongo-php-driver@v2.x + jobs: build: runs-on: "${{ matrix.os }}" - name: "PHP ${{ matrix.php }} Laravel ${{ matrix.laravel }} MongoDB ${{ matrix.mongodb }} ${{ matrix.mode }}" + name: "PHP/${{ matrix.php }} Laravel/${{ matrix.laravel }} Driver/${{ matrix.driver }} Server/${{ matrix.mongodb }} ${{ matrix.mode }}" strategy: matrix: @@ -29,12 +33,21 @@ jobs: - "10.*" - "11.*" - "12.*" + driver: + - 1 include: - php: "8.1" laravel: "10.*" mongodb: "5.0" mode: "low-deps" os: "ubuntu-latest" + driver: 1.x + driver_version: "1.21.0" + - php: "8.4" + laravel: "12.*" + mongodb: "8.0" + os: "ubuntu-latest" + driver: 2 exclude: - php: "8.1" laravel: "11.*" @@ -59,11 +72,19 @@ jobs: if [ "${{ matrix.mongodb }}" = "4.4" ]; then MONGOSH_BIN="mongo"; else MONGOSH_BIN="mongosh"; fi docker exec --tty mongodb $MONGOSH_BIN --eval "db.runCommand({ serverStatus: 1 })" + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} + key: "extcache-v1" + - name: "Installing php" uses: "shivammathur/setup-php@v2" with: php-version: ${{ matrix.php }} - extensions: "curl,mbstring,xdebug" + extensions: "curl,mbstring,xdebug,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" coverage: "xdebug" tools: "composer" diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 24d397294..946e84971 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -5,7 +5,7 @@ on: pull_request: env: - PHP_VERSION: "8.2" + PHP_VERSION: "8.4" DRIVER_VERSION: "stable" jobs: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index a66100d93..e0c907953 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -13,9 +13,12 @@ on: env: PHP_VERSION: "8.2" DRIVER_VERSION: "stable" + MONGODB_EXT_V1: mongodb-1.21.0 + MONGODB_EXT_V2: mongodb-mongodb/mongo-php-driver@v2.x jobs: phpstan: + name: "PHP/${{ matrix.php }} Driver/${{ matrix.driver }}" runs-on: "ubuntu-22.04" continue-on-error: true strategy: @@ -24,6 +27,10 @@ jobs: - '8.1' - '8.2' - '8.3' + - '8.4' + driver: + - 1 + - 2 steps: - name: Checkout uses: actions/checkout@v4 @@ -35,11 +42,19 @@ jobs: run: | echo CHECKED_OUT_SHA=$(git rev-parse HEAD) >> $GITHUB_ENV + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }} + key: "extcache-v1" + - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: curl, mbstring + extensions: "curl,mbstring,${{ matrix.driver == 1 && env.MONGODB_EXT_V1 || env.MONGODB_EXT_V2 }}" tools: composer:v2 coverage: none diff --git a/composer.json b/composer.json index a6f5470aa..2542b51bb 100644 --- a/composer.json +++ b/composer.json @@ -23,14 +23,14 @@ "license": "MIT", "require": { "php": "^8.1", - "ext-mongodb": "^1.21", + "ext-mongodb": "^1.21|^2", "composer-runtime-api": "^2.0.0", "illuminate/cache": "^10.36|^11|^12", "illuminate/container": "^10.0|^11|^12", "illuminate/database": "^10.30|^11|^12", "illuminate/events": "^10.0|^11|^12", "illuminate/support": "^10.0|^11|^12", - "mongodb/mongodb": "^1.21", + "mongodb/mongodb": "^1.21|^2", "symfony/http-foundation": "^6.4|^7" }, "require-dev": { diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index eedbe8712..f85570575 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -11,7 +11,7 @@ use MongoDB\Builder\Type\QueryInterface; use MongoDB\Builder\Type\SearchOperatorInterface; use MongoDB\Driver\CursorInterface; -use MongoDB\Driver\Exception\WriteException; +use MongoDB\Driver\Exception\BulkWriteException; use MongoDB\Laravel\Connection; use MongoDB\Laravel\Helpers\QueriesRelationships; use MongoDB\Laravel\Query\AggregationBuilder; @@ -285,7 +285,7 @@ public function createOrFirst(array $attributes = [], array $values = []) try { return $this->create(array_merge($attributes, $values)); - } catch (WriteException $e) { + } catch (BulkWriteException $e) { if ($e->getCode() === self::DUPLICATE_KEY_ERROR) { return $this->where($attributes)->first() ?? throw $e; } diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index 9592bbe7c..46beebab1 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -161,7 +161,7 @@ public function testFindWithTimeout() $id = DB::table('users')->insertGetId(['name' => 'John Doe']); $subscriber = new class implements CommandSubscriber { - public function commandStarted(CommandStartedEvent $event) + public function commandStarted(CommandStartedEvent $event): void { if ($event->getCommandName() !== 'find') { return; @@ -171,11 +171,11 @@ public function commandStarted(CommandStartedEvent $event) Assert::assertSame(1000, $event->getCommand()->maxTimeMS); } - public function commandFailed(CommandFailedEvent $event) + public function commandFailed(CommandFailedEvent $event): void { } - public function commandSucceeded(CommandSucceededEvent $event) + public function commandSucceeded(CommandSucceededEvent $event): void { } }; diff --git a/tests/Scout/ScoutEngineTest.php b/tests/Scout/ScoutEngineTest.php index 40d943ffb..7b254ec9c 100644 --- a/tests/Scout/ScoutEngineTest.php +++ b/tests/Scout/ScoutEngineTest.php @@ -11,13 +11,11 @@ use Laravel\Scout\Builder; use Laravel\Scout\Jobs\RemoveFromSearch; use LogicException; -use Mockery as m; use MongoDB\BSON\Document; use MongoDB\BSON\UTCDateTime; use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\CursorInterface; -use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Scout\ScoutEngine; use MongoDB\Laravel\Tests\Scout\Models\ScoutUser; use MongoDB\Laravel\Tests\Scout\Models\SearchableModel; @@ -36,7 +34,7 @@ class ScoutEngineTest extends TestCase public function testCreateIndexInvalidDefinition(): void { - $database = m::mock(Database::class); + $database = $this->createMock(Database::class); $engine = new ScoutEngine($database, false, ['collection_invalid' => ['foo' => 'bar']]); $this->expectException(LogicException::class); @@ -53,21 +51,22 @@ public function testCreateIndex(): void ], ]; - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('createCollection') - ->once() + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('createCollection') ->with($collectionName); - $database->shouldReceive('selectCollection') + $database->expects($this->once()) + ->method('selectCollection') ->with($collectionName) - ->andReturn($collection); - $collection->shouldReceive('createSearchIndex') - ->once() + ->willReturn($collection); + $collection->expects($this->once()) + ->method('createSearchIndex') ->with($expectedDefinition, ['name' => 'scout']); - $collection->shouldReceive('listSearchIndexes') - ->once() + $collection->expects($this->once()) + ->method('listSearchIndexes') ->with(['name' => 'scout', 'typeMap' => ['root' => 'bson']]) - ->andReturn(new ArrayIterator([Document::fromPHP(['name' => 'scout', 'status' => 'READY'])])); + ->willReturn(new ArrayIterator([Document::fromPHP(['name' => 'scout', 'status' => 'READY'])])); $engine = new ScoutEngine($database, false, []); $engine->createIndex($collectionName); @@ -90,21 +89,22 @@ public function testCreateIndexCustomDefinition(): void ], ]; - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('createCollection') - ->once() + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('createCollection') ->with($collectionName); - $database->shouldReceive('selectCollection') + $database->expects($this->once()) + ->method('selectCollection') ->with($collectionName) - ->andReturn($collection); - $collection->shouldReceive('createSearchIndex') - ->once() + ->willReturn($collection); + $collection->expects($this->once()) + ->method('createSearchIndex') ->with($expectedDefinition, ['name' => 'scout']); - $collection->shouldReceive('listSearchIndexes') - ->once() + $collection->expects($this->once()) + ->method('listSearchIndexes') ->with(['name' => 'scout', 'typeMap' => ['root' => 'bson']]) - ->andReturn(new ArrayIterator([Document::fromPHP(['name' => 'scout', 'status' => 'READY'])])); + ->willReturn(new ArrayIterator([Document::fromPHP(['name' => 'scout', 'status' => 'READY'])])); $engine = new ScoutEngine($database, false, [$collectionName => $expectedDefinition]); $engine->createIndex($collectionName); @@ -115,26 +115,28 @@ public function testCreateIndexCustomDefinition(): void public function testSearch(Closure $builder, array $expectedPipeline): void { $data = [['_id' => 'key_1', '__count' => 15], ['_id' => 'key_2', '__count' => 15]]; - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('selectCollection') ->with('collection_searchable') - ->andReturn($collection); - $cursor = m::mock(CursorInterface::class); - $cursor->shouldReceive('setTypeMap')->once()->with(self::EXPECTED_TYPEMAP); - $cursor->shouldReceive('toArray')->once()->with()->andReturn($data); - - $collection->shouldReceive('getCollectionName') - ->zeroOrMoreTimes() - ->andReturn('collection_searchable'); - $collection->shouldReceive('aggregate') - ->once() - ->withArgs(function ($pipeline) use ($expectedPipeline) { - self::assertEquals($expectedPipeline, $pipeline); - - return true; - }) - ->andReturn($cursor); + ->willReturn($collection); + $cursor = $this->createMock(CursorInterface::class); + $cursor->expects($this->once()) + ->method('setTypeMap') + ->with(self::EXPECTED_TYPEMAP); + $cursor->expects($this->once()) + ->method('toArray') + ->with() + ->willReturn($data); + + $collection->expects($this->any()) + ->method('getCollectionName') + ->willReturn('collection_searchable'); + $collection->expects($this->once()) + ->method('aggregate') + ->with($expectedPipeline) + ->willReturn($cursor); $engine = new ScoutEngine($database, softDelete: false); $result = $engine->search($builder()); @@ -414,15 +416,15 @@ public function testPaginate() $perPage = 5; $page = 3; - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $cursor = m::mock(CursorInterface::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $cursor = $this->createMock(CursorInterface::class); + $database->method('selectCollection') ->with('collection_searchable') - ->andReturn($collection); - $collection->shouldReceive('aggregate') - ->once() - ->withArgs(function (...$args) { + ->willReturn($collection); + $collection->expects($this->once()) + ->method('aggregate') + ->willReturnCallback(function (...$args) use ($cursor) { self::assertSame([ [ '$search' => [ @@ -468,14 +470,11 @@ public function testPaginate() ], ], $args[0]); - return true; - }) - ->andReturn($cursor); - $cursor->shouldReceive('setTypeMap')->once()->with(self::EXPECTED_TYPEMAP); - $cursor->shouldReceive('toArray') - ->once() - ->with() - ->andReturn([['_id' => 'key_1', '__count' => 17], ['_id' => 'key_2', '__count' => 17]]); + return $cursor; + }); + $cursor->expects($this->once())->method('setTypeMap')->with(self::EXPECTED_TYPEMAP); + $cursor->expects($this->once())->method('toArray')->with() + ->willReturn([['_id' => 'key_1', '__count' => 17], ['_id' => 'key_2', '__count' => 17]]); $engine = new ScoutEngine($database, softDelete: false); $builder = new Builder(new SearchableModel(), 'mustang'); @@ -485,20 +484,27 @@ public function testPaginate() public function testMapMethodRespectsOrder() { - $database = m::mock(Database::class); + $database = $this->createMock(Database::class); + $query = $this->createMock(Builder::class); $engine = new ScoutEngine($database, false); - $model = m::mock(Model::class); - $model->shouldReceive(['getScoutKeyName' => 'id']); - $model->shouldReceive('queryScoutModelsByIds->get') - ->andReturn(LaravelCollection::make([ + $model = $this->createMock(SearchableModel::class); + $model->expects($this->any()) + ->method('getScoutKeyName') + ->willReturn('id'); + $model->expects($this->once()) + ->method('queryScoutModelsByIds') + ->willReturn($query); + $query->expects($this->once()) + ->method('get') + ->willReturn(LaravelCollection::make([ new ScoutUser(['id' => 1]), new ScoutUser(['id' => 2]), new ScoutUser(['id' => 3]), new ScoutUser(['id' => 4]), ])); - $builder = m::mock(Builder::class); + $builder = $this->createMock(Builder::class); $results = $engine->map($builder, [ ['_id' => 1, '__count' => 4], @@ -518,21 +524,27 @@ public function testMapMethodRespectsOrder() public function testLazyMapMethodRespectsOrder() { - $lazy = false; - $database = m::mock(Database::class); + $database = $this->createMock(Database::class); + $query = $this->createMock(Builder::class); $engine = new ScoutEngine($database, false); - $model = m::mock(Model::class); - $model->shouldReceive(['getScoutKeyName' => 'id']); - $model->shouldReceive('queryScoutModelsByIds->cursor') - ->andReturn(LazyCollection::make([ + $model = $this->createMock(SearchableModel::class); + $model->expects($this->any()) + ->method('getScoutKeyName') + ->willReturn('id'); + $model->expects($this->once()) + ->method('queryScoutModelsByIds') + ->willReturn($query); + $query->expects($this->once()) + ->method('cursor') + ->willReturn(LazyCollection::make([ new ScoutUser(['id' => 1]), new ScoutUser(['id' => 2]), new ScoutUser(['id' => 3]), new ScoutUser(['id' => 4]), ])); - $builder = m::mock(Builder::class); + $builder = $this->createMock(Builder::class); $results = $engine->lazyMap($builder, [ ['_id' => 1, '__count' => 4], @@ -553,13 +565,14 @@ public function testLazyMapMethodRespectsOrder() public function testUpdate(): void { $date = new DateTimeImmutable('2000-01-02 03:04:05'); - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('selectCollection') ->with('collection_indexable') - ->andReturn($collection); - $collection->shouldReceive('bulkWrite') - ->once() + ->willReturn($collection); + $collection->expects($this->once()) + ->method('bulkWrite') ->with([ [ 'updateOne' => [ @@ -592,26 +605,23 @@ public function testUpdate(): void public function testUpdateWithSoftDelete(): void { $date = new DateTimeImmutable('2000-01-02 03:04:05'); - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('selectCollection') ->with('collection_indexable') - ->andReturn($collection); - $collection->shouldReceive('bulkWrite') - ->once() - ->withArgs(function ($pipeline) { - $this->assertSame([ - [ - 'updateOne' => [ - ['_id' => 'key_1'], - ['$set' => ['id' => 1, '__soft_deleted' => false]], - ['upsert' => true], - ], + ->willReturn($collection); + $collection->expects($this->once()) + ->method('bulkWrite') + ->with([ + [ + 'updateOne' => [ + ['_id' => 'key_1'], + ['$set' => ['id' => 1, '__soft_deleted' => false]], + ['upsert' => true], ], - ], $pipeline); - - return true; - }); + ], + ]); $model = new SearchableModel(['id' => 1]); $model->delete(); @@ -622,13 +632,14 @@ public function testUpdateWithSoftDelete(): void public function testDelete(): void { - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('selectCollection') ->with('collection_indexable') - ->andReturn($collection); - $collection->shouldReceive('deleteMany') - ->once() + ->willReturn($collection); + $collection->expects($this->once()) + ->method('deleteMany') ->with(['_id' => ['$in' => ['key_1', 'key_2']]]); $engine = new ScoutEngine($database, softDelete: false); @@ -646,13 +657,14 @@ public function testDeleteWithRemoveableScoutCollection(): void $job = unserialize(serialize($job)); - $database = m::mock(Database::class); - $collection = m::mock(Collection::class); - $database->shouldReceive('selectCollection') + $database = $this->createMock(Database::class); + $collection = $this->createMock(Collection::class); + $database->expects($this->once()) + ->method('selectCollection') ->with('collection_indexable') - ->andReturn($collection); - $collection->shouldReceive('deleteMany') - ->once() + ->willReturn($collection); + $collection->expects($this->once()) + ->method('deleteMany') ->with(['_id' => ['$in' => ['key_5']]]); $engine = new ScoutEngine($database, softDelete: false); From 583200745cd698ad03ae0025aa928f84c64fc6e8 Mon Sep 17 00:00:00 2001 From: Ivan Todorovic Date: Tue, 1 Apr 2025 13:36:48 +0200 Subject: [PATCH 9/9] Remove manual dirty _id check when updating a model (#3329) --- src/Query/Builder.php | 7 ------- tests/Ticket/GH3326Test.php | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 tests/Ticket/GH3326Test.php diff --git a/src/Query/Builder.php b/src/Query/Builder.php index f613b6467..5c873380b 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -783,13 +783,6 @@ public function update(array $values, array $options = []) unset($values[$key]); } - // Since "id" is an alias for "_id", we prevent updating it - foreach ($values as $fields) { - if (array_key_exists('id', $fields)) { - throw new InvalidArgumentException('Cannot update "id" field.'); - } - } - return $this->performUpdate($values, $options); } diff --git a/tests/Ticket/GH3326Test.php b/tests/Ticket/GH3326Test.php new file mode 100644 index 000000000..d3f339acc --- /dev/null +++ b/tests/Ticket/GH3326Test.php @@ -0,0 +1,42 @@ +foo = 'bar'; + $model->save(); + + $fresh = $model->fresh(); + + $this->assertEquals('bar', $fresh->foo); + $this->assertEquals('written-in-created', $fresh->extra); + } +} + +class GH3326Model extends Model +{ + protected $connection = 'mongodb'; + protected $collection = 'test_gh3326'; + protected $guarded = []; + + protected static function booted(): void + { + static::created(function ($model) { + $model->extra = 'written-in-created'; + $model->saveQuietly(); + }); + } +}