From fe183df15ce250d9d6df6a40e9f1503f30bec177 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Wed, 19 Mar 2025 14:27:57 -0400 Subject: [PATCH 1/3] DOCSP-48165: Aggregation async examples --- source/aggregation.txt | 51 ++++-- source/aggregation/aggregation-tutorials.txt | 19 +- .../aggregation-tutorials/filtered-subset.txt | 62 +++++-- .../aggregation-tutorials/group-total.txt | 60 +++++-- .../multi-field-join.txt | 87 +++++++--- .../aggregation-tutorials/one-to-one-join.txt | 87 +++++++--- .../aggregation-tutorials/unpack-arrays.txt | 60 +++++-- .../aggregation/aggregation-template-async.py | 39 +++++ .../aggregation/filtered-subset-async.py | 141 +++++++++++++++ .../includes/aggregation/group-total-async.py | 133 ++++++++++++++ .../aggregation/multi-field-join-async.py | 163 ++++++++++++++++++ .../aggregation/one-to-one-join-async.py | 137 +++++++++++++++ .../aggregation/unpack-arrays-async.py | 135 +++++++++++++++ 13 files changed, 1078 insertions(+), 96 deletions(-) create mode 100644 source/includes/aggregation/aggregation-template-async.py create mode 100644 source/includes/aggregation/filtered-subset-async.py create mode 100644 source/includes/aggregation/group-total-async.py create mode 100644 source/includes/aggregation/multi-field-join-async.py create mode 100644 source/includes/aggregation/one-to-one-join-async.py create mode 100644 source/includes/aggregation/unpack-arrays-async.py diff --git a/source/aggregation.txt b/source/aggregation.txt index 82d4160f..0137177b 100644 --- a/source/aggregation.txt +++ b/source/aggregation.txt @@ -103,21 +103,48 @@ of New York. To do so, it uses an aggregation pipeline with the following stages documents by the ``borough`` field, accumulating a count of documents for each distinct value. -.. code-block:: python - :copyable: true +Select the :guilabel:`Synchronous` or :guilabel:`Asynchronous` tab to see the +corresponding code: - # Define an aggregation pipeline with a match stage and a group stage - pipeline = [ - { "$match": { "cuisine": "Bakery" } }, - { "$group": { "_id": "$borough", "count": { "$sum": 1 } } } - ] +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. code-block:: python + :copyable: true + + # Define an aggregation pipeline with a match stage and a group stage + pipeline = [ + { "$match": { "cuisine": "Bakery" } }, + { "$group": { "_id": "$borough", "count": { "$sum": 1 } } } + ] + + # Execute the aggregation + aggCursor = collection.aggregate(pipeline) + + # Print the aggregated results + for document in aggCursor: + print(document) + + .. tab:: Asynchronous + :tabid: async + + .. code-block:: python + :copyable: true + + # Define an aggregation pipeline with a match stage and a group stage + pipeline = [ + { "$match": { "cuisine": "Bakery" } }, + { "$group": { "_id": "$borough", "count": { "$sum": 1 } } } + ] - # Execute the aggregation - aggCursor = collection.aggregate(pipeline) + # Execute the aggregation + aggCursor = await collection.aggregate(pipeline) - # Print the aggregated results - for document in aggCursor: - print(document) + # Print the aggregated results + async for document in aggCursor: + print(document) The preceding code example produces output similar to the following: diff --git a/source/aggregation/aggregation-tutorials.txt b/source/aggregation/aggregation-tutorials.txt index 7be3e3d8..10b7c5a8 100644 --- a/source/aggregation/aggregation-tutorials.txt +++ b/source/aggregation/aggregation-tutorials.txt @@ -68,12 +68,29 @@ pipeline in each tutorial. Once you install the driver, create a file called ``agg_tutorial.py``. Paste the following code in this file to create an -app template for the aggregation tutorials: +app template for the aggregation tutorials. Select the :guilabel:`Synchronous` or +:guilabel:`Asynchronous` tab to see the corresponding code: .. literalinclude:: /includes/aggregation/template-app.py :language: python :copyable: true +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/template-app.py + :language: python + :copyable: true + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/aggregation-template-async.py + :language: python + :copyable: true + .. important:: In the preceding code, read the code comments to find the sections of diff --git a/source/aggregation/aggregation-tutorials/filtered-subset.txt b/source/aggregation/aggregation-tutorials/filtered-subset.txt index e9a738b1..2a6c60db 100644 --- a/source/aggregation/aggregation-tutorials/filtered-subset.txt +++ b/source/aggregation/aggregation-tutorials/filtered-subset.txt @@ -57,14 +57,30 @@ following code to the application: :dedent: Delete any existing data in the collections and insert sample data into -the ``persons`` collection as shown in the following code: +the ``persons`` collection as shown in the following code. Select the +:guilabel:`Synchronous` or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/filtered-subset.py - :language: python - :copyable: true - :start-after: start-insert-persons - :end-before: end-insert-persons - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/filtered-subset.py + :language: python + :copyable: true + :start-after: start-insert-persons + :end-before: end-insert-persons + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/filtered-subset-async.py + :language: python + :copyable: true + :start-after: start-insert-persons + :end-before: end-insert-persons + :dedent: Tutorial -------- @@ -134,14 +150,30 @@ Tutorial .. step:: Run the aggregation pipeline Add the following code to the end of your application to perform - the aggregation on the ``persons`` collection: - - .. literalinclude:: /includes/aggregation/filtered-subset.py - :language: python - :copyable: true - :start-after: start-run-agg - :end-before: end-run-agg - :dedent: + the aggregation on the ``persons`` collection. Select the :guilabel:`Synchronous` or + :guilabel:`Asynchronous` tab to see the corresponding code: + + .. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/filtered-subset.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/filtered-subset-async.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: Finally, run the following command in your shell to start your application: diff --git a/source/aggregation/aggregation-tutorials/group-total.txt b/source/aggregation/aggregation-tutorials/group-total.txt index f71a89ab..6f73977c 100644 --- a/source/aggregation/aggregation-tutorials/group-total.txt +++ b/source/aggregation/aggregation-tutorials/group-total.txt @@ -59,14 +59,30 @@ following code to the application: :dedent: Delete any existing data and insert sample data into -the ``orders`` collection as shown in the following code: +the ``orders`` collection as shown in the following code. Select the :guilabel:`Synchronous` +or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/group-total.py - :language: python - :copyable: true - :start-after: start-insert-orders - :end-before: end-insert-orders - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/group-total.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/group-total-async.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: Tutorial -------- @@ -166,14 +182,30 @@ Tutorial .. step:: Run the aggregation pipeline Add the following code to the end of your application to perform - the aggregation on the ``orders`` collection: + the aggregation on the ``orders`` collection. Select the :guilabel:`Synchronous` + or :guilabel:`Asynchronous` tab to see the corresponding code: + + .. tabs:: - .. literalinclude:: /includes/aggregation/group-total.py - :language: python - :copyable: true - :start-after: start-run-agg - :end-before: end-run-agg - :dedent: + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/group-total.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/group-total-async.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: Finally, run the following command in your shell to start your application: diff --git a/source/aggregation/aggregation-tutorials/multi-field-join.txt b/source/aggregation/aggregation-tutorials/multi-field-join.txt index b6f5ad6a..aad28571 100644 --- a/source/aggregation/aggregation-tutorials/multi-field-join.txt +++ b/source/aggregation/aggregation-tutorials/multi-field-join.txt @@ -81,24 +81,55 @@ collections by adding the following code to the application: :dedent: Delete any existing data and insert sample data into -the ``products`` collection as shown in the following code: +the ``products`` collection as shown in the following code. Select the :guilabel:`Synchronous` +or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/multi-field-join.py - :language: python - :copyable: true - :start-after: start-insert-products - :end-before: end-insert-products - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-insert-products + :end-before: end-insert-products + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-insert-products + :end-before: end-insert-products + :dedent: Delete any existing data and insert sample data into the ``orders`` collection as shown in the following code: -.. literalinclude:: /includes/aggregation/multi-field-join.py - :language: python - :copyable: true - :start-after: start-insert-orders - :end-before: end-insert-orders - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: Tutorial -------- @@ -192,14 +223,30 @@ Tutorial .. step:: Run the aggregation pipeline Add the following code to the end of your application to perform - the aggregation on the ``products`` collection: + the aggregation on the ``products`` collection. Select the :guilabel:`Synchronous` + or :guilabel:`Asynchronous` tab to see the corresponding code: - .. literalinclude:: /includes/aggregation/multi-field-join.py - :language: python - :copyable: true - :start-after: start-run-agg - :end-before: end-run-agg - :dedent: + .. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/multi-field-join.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: Finally, run the following command in your shell to start your application: diff --git a/source/aggregation/aggregation-tutorials/one-to-one-join.txt b/source/aggregation/aggregation-tutorials/one-to-one-join.txt index d3d36704..7ce7e410 100644 --- a/source/aggregation/aggregation-tutorials/one-to-one-join.txt +++ b/source/aggregation/aggregation-tutorials/one-to-one-join.txt @@ -76,24 +76,55 @@ collections by adding the following code to the application: :dedent: Delete any existing data and insert sample data into -the ``orders`` collection as shown in the following code: +the ``orders`` collection as shown in the following code. Select the :guilabel:`Synchronous` +or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/one-to-one-join.py - :language: python - :copyable: true - :start-after: start-insert-orders - :end-before: end-insert-orders - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/one-to-one-join.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/one-to-one-join-async.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: Delete any existing data and insert sample data into the ``products`` collection as shown in the following code: -.. literalinclude:: /includes/aggregation/one-to-one-join.py - :language: python - :copyable: true - :start-after: start-insert-products - :end-before: end-insert-products - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/one-to-one-join.py + :language: python + :copyable: true + :start-after: start-insert-products + :end-before: end-insert-products + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/one-to-one-join-async.py + :language: python + :copyable: true + :start-after: start-insert-products + :end-before: end-insert-products + :dedent: Tutorial -------- @@ -171,14 +202,30 @@ Tutorial .. step:: Run the aggregation pipeline Add the following code to the end of your application to perform - the aggregation on the ``orders`` collection: + the aggregation on the ``orders`` collection. Select the :guilabel:`Synchronous` + or :guilabel:`Asynchronous` tab to see the corresponding code: - .. literalinclude:: /includes/aggregation/one-to-one-join.py - :language: python - :copyable: true - :start-after: start-run-agg - :end-before: end-run-agg - :dedent: + .. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/one-to-one-join.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/one-to-one-join-async.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: Finally, run the following command in your shell to start your application: diff --git a/source/aggregation/aggregation-tutorials/unpack-arrays.txt b/source/aggregation/aggregation-tutorials/unpack-arrays.txt index 7a311a22..4d697cdf 100644 --- a/source/aggregation/aggregation-tutorials/unpack-arrays.txt +++ b/source/aggregation/aggregation-tutorials/unpack-arrays.txt @@ -61,14 +61,30 @@ following code to the application: :dedent: Delete any existing data and insert sample data into -the ``orders`` collection as shown in the following code: +the ``orders`` collection as shown in the following code. Select the :guilabel:`Synchronous` +or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/unpack-arrays.py - :language: python - :copyable: true - :start-after: start-insert-orders - :end-before: end-insert-orders - :dedent: +.. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/unpack-arrays.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/unpack-arrays-async.py + :language: python + :copyable: true + :start-after: start-insert-orders + :end-before: end-insert-orders + :dedent: Tutorial -------- @@ -152,14 +168,30 @@ Tutorial .. step:: Run the aggregation pipeline Add the following code to the end of your application to perform - the aggregation on the ``orders`` collection: + the aggregation on the ``orders`` collection. Select the :guilabel:`Synchronous` + or :guilabel:`Asynchronous` tab to see the corresponding code: - .. literalinclude:: /includes/aggregation/unpack-arrays.py - :language: python - :copyable: true - :start-after: start-run-agg - :end-before: end-run-agg - :dedent: + .. tabs:: + + .. tab:: Synchronous + :tabid: sync + + .. literalinclude:: /includes/aggregation/unpack-arrays.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: + + .. tab:: Asynchronous + :tabid: async + + .. literalinclude:: /includes/aggregation/unpack-arrays-async.py + :language: python + :copyable: true + :start-after: start-run-agg + :end-before: end-run-agg + :dedent: Finally, run the following command in your shell to start your application: diff --git a/source/includes/aggregation/aggregation-template-async.py b/source/includes/aggregation/aggregation-template-async.py new file mode 100644 index 00000000..b34a86e6 --- /dev/null +++ b/source/includes/aggregation/aggregation-template-async.py @@ -0,0 +1,39 @@ +import asyncio +from pymongo import AsyncMongoClient + +# Replace the placeholder with your connection string. +uri = "" +client = AsyncMongoClient(uri) +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # Get a reference to relevant collections. + # ... some_coll = + # ... another_coll = + + # Delete any existing documents in collections. + # ... await some_coll.delete_many({}) + + # Insert sample data into the collection or collections. + # ... some_data = [...] + + # ... await some_coll.insert_many(some_data) + + # Create an empty pipeline array. + pipeline = [] + + # Add code to create pipeline stages. + # ... pipeline.append({...}) + + # Run the aggregation. + # ... aggregation_result = ... + + # Print the aggregation results. + for document in aggregation_result: + print(document) + + finally: + await client.close() + +asyncio.run(main()) \ No newline at end of file diff --git a/source/includes/aggregation/filtered-subset-async.py b/source/includes/aggregation/filtered-subset-async.py new file mode 100644 index 00000000..bf4803d0 --- /dev/null +++ b/source/includes/aggregation/filtered-subset-async.py @@ -0,0 +1,141 @@ +import asyncio +from pymongo import AsyncMongoClient +from datetime import datetime + +uri = "" +client = AsyncMongoClient(uri) + +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # start-collection + person_coll = agg_db["persons"] + # end-collection + + # start-insert-persons + await person_coll.delete_many({}) + + person_data = [ + { + "person_id": "6392529400", + "firstname": "Elise", + "lastname": "Smith", + "dateofbirth": datetime(1972, 1, 13, 9, 32, 7), + "vocation": "ENGINEER", + "address": { + "number": 5625, + "street": "Tipa Circle", + "city": "Wojzinmoj", + } + }, + { + "person_id": "1723338115", + "firstname": "Olive", + "lastname": "Ranieri", + "dateofbirth": datetime(1985, 5, 12, 23, 14, 30), + "gender": "FEMALE", + "vocation": "ENGINEER", + "address": { + "number": 9303, + "street": "Mele Circle", + "city": "Tobihbo", + } + }, + { + "person_id": "8732762874", + "firstname": "Toni", + "lastname": "Jones", + "dateofbirth": datetime(1991, 11, 23, 16, 53, 56), + "vocation": "POLITICIAN", + "address": { + "number": 1, + "street": "High Street", + "city": "Upper Abbeywoodington", + } + }, + { + "person_id": "7363629563", + "firstname": "Bert", + "lastname": "Gooding", + "dateofbirth": datetime(1941, 4, 7, 22, 11, 52), + "vocation": "FLORIST", + "address": { + "number": 13, + "street": "Upper Bold Road", + "city": "Redringtonville", + } + }, + { + "person_id": "1029648329", + "firstname": "Sophie", + "lastname": "Celements", + "dateofbirth": datetime(1959, 7, 6, 17, 35, 45), + "vocation": "ENGINEER", + "address": { + "number": 5, + "street": "Innings Close", + "city": "Basilbridge", + } + }, + { + "person_id": "7363626383", + "firstname": "Carl", + "lastname": "Simmons", + "dateofbirth": datetime(1998, 12, 26, 13, 13, 55), + "vocation": "ENGINEER", + "address": { + "number": 187, + "street": "Hillside Road", + "city": "Kenningford", + } + } + ] + + await person_coll.insert_many(person_data) + # end-insert-persons + + pipeline = [] + + # start-match + pipeline.append({ + "$match": { + "vocation": "ENGINEER" + } + }) + # end-match + + # start-sort + pipeline.append({ + "$sort": { + "dateofbirth": -1 + } + }) + # end-sort + + # start-limit + pipeline.append({ + "$limit": 3 + }) + # end-limit + + # start-unset + pipeline.append({ + "$unset": [ + "_id", + "address" + ] + }) + # end-unset + + # start-run-agg + aggregation_result = await person_coll.aggregate(pipeline) + # end-run-agg + + async for document in aggregation_result: + print(document) + + finally: + await client.close() + +asyncio.run(main()) \ No newline at end of file diff --git a/source/includes/aggregation/group-total-async.py b/source/includes/aggregation/group-total-async.py new file mode 100644 index 00000000..f6c9d123 --- /dev/null +++ b/source/includes/aggregation/group-total-async.py @@ -0,0 +1,133 @@ +import asyncio +from pymongo import AsyncMongoClient +from datetime import datetime + +uri = "" +client = AsyncMongoClient(uri) + +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # start-coll + orders_coll = agg_db["orders"] + # end-coll + + # start-insert-orders + await orders_coll.delete_many({}) + + order_data = [ + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 5, 30, 8, 35, 52), + "value": 231 + }, + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 1, 13, 9, 32, 7), + "value": 99 + }, + { + "customer_id": "oranieri@warmmail.com", + "orderdate": datetime(2020, 1, 1, 8, 25, 37), + "value": 63 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2019, 5, 28, 19, 13, 32), + "value": 2 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2020, 11, 23, 22, 56, 53), + "value": 187 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2020, 8, 18, 23, 4, 48), + "value": 4 + }, + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 12, 26, 8, 55, 46), + "value": 4 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2021, 2, 28, 7, 49, 32), + "value": 1024 + }, + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 10, 3, 13, 49, 44), + "value": 102 + } + ] + + await orders_coll.insert_many(order_data) + # end-insert-orders + + pipeline = [] + + # start-match + pipeline.append({ + "$match": { + "orderdate": { + "$gte": datetime(2020, 1, 1, 0, 0, 0), + "$lt": datetime(2021, 1, 1, 0, 0, 0) + } + } + }) + # end-match + + # start-sort1 + pipeline.append({ + "$sort": { + "orderdate": 1 + } + }) + # end-sort1 + + # start-group + pipeline.append({ + "$group": { + "_id": "$customer_id", + "first_purchase_date": {"$first": "$orderdate"}, + "total_value": {"$sum": "$value"}, + "total_orders": {"$sum": 1}, + "orders": {"$push": {"orderdate": "$orderdate", "value": "$value"}} + } + }) + # end-group + + # start-sort2 + pipeline.append({ + "$sort": { + "first_purchase_date": 1 + } + }) + # end-sort2 + + # start-set + pipeline.append({ + "$set": { + "customer_id": "$_id" + } + }) + # end-set + + # start-unset + pipeline.append({"$unset": ["_id"]}) + # end-unset + + # start-run-agg + aggregation_result = await orders_coll.aggregate(pipeline) + # end-run-agg + + async for document in aggregation_result: + print(document) + + finally: + await client.close() + +asyncio.run(main()) \ No newline at end of file diff --git a/source/includes/aggregation/multi-field-join-async.py b/source/includes/aggregation/multi-field-join-async.py new file mode 100644 index 00000000..08a67c6a --- /dev/null +++ b/source/includes/aggregation/multi-field-join-async.py @@ -0,0 +1,163 @@ +import asyncio +from pymongo import AsyncMongoClient +from datetime import datetime + +uri = "" +client = AsyncMongoClient(uri) + +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # start-colls + products_coll = agg_db["products"] + orders_coll = agg_db["orders"] + # end-colls + + # start-insert-products + await products_coll.delete_many({}) + + products_data = [ + { + "name": "Asus Laptop", + "variation": "Ultra HD", + "category": "ELECTRONICS", + "description": "Great for watching movies" + }, + { + "name": "Asus Laptop", + "variation": "Standard Display", + "category": "ELECTRONICS", + "description": "Good value laptop for students" + }, + { + "name": "The Day Of The Triffids", + "variation": "1st Edition", + "category": "BOOKS", + "description": "Classic post-apocalyptic novel" + }, + { + "name": "The Day Of The Triffids", + "variation": "2nd Edition", + "category": "BOOKS", + "description": "Classic post-apocalyptic novel" + }, + { + "name": "Morphy Richards Food Mixer", + "variation": "Deluxe", + "category": "KITCHENWARE", + "description": "Luxury mixer turning good cakes into great" + } + ] + + await products_coll.insert_many(products_data) + # end-insert-products + + # start-insert-orders + await orders_coll.delete_many({}) + + order_data = [ + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 5, 30, 8, 35, 52), + "product_name": "Asus Laptop", + "product_variation": "Standard Display", + "value": 431.43 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2019, 5, 28, 19, 13, 32), + "product_name": "The Day Of The Triffids", + "product_variation": "2nd Edition", + "value": 5.01 + }, + { + "customer_id": "oranieri@warmmail.com", + "orderdate": datetime(2020, 1, 1, 8, 25, 37), + "product_name": "Morphy Richards Food Mixer", + "product_variation": "Deluxe", + "value": 63.13 + }, + { + "customer_id": "jjones@tepidmail.com", + "orderdate": datetime(2020, 12, 26, 8, 55, 46), + "product_name": "Asus Laptop", + "product_variation": "Standard Display", + "value": 429.65 + } + ] + + await orders_coll.insert_many(order_data) + # end-insert-orders + + pipeline = [] + + # start-embedded-pl-match1 + embedded_pl = [ + { + "$match": { + "$expr": { + "$and": [ + {"$eq": ["$product_name", "$$prdname"]}, + {"$eq": ["$product_variation", "$$prdvartn"]} + ] + } + } + } + ] + # end-embedded-pl-match1 + + # start-embedded-pl-match2 + embedded_pl.append({ + "$match": { + "orderdate": { + "$gte": datetime(2020, 1, 1, 0, 0, 0), + "$lt": datetime(2021, 1, 1, 0, 0, 0) + } + } + }) + # end-embedded-pl-match2 + + # start-embedded-pl-unset + embedded_pl.append({ + "$unset": ["_id", "product_name", "product_variation"] + }) + # end-embedded-pl-unset + + # start-lookup + pipeline.append({ + "$lookup": { + "from": "orders", + "let": { + "prdname": "$name", + "prdvartn": "$variation" + }, + "pipeline": embedded_pl, + "as": "orders" + } + }) + # end-lookup + + # start-match + pipeline.append({ + "$match": { + "orders": {"$ne": []} + } + }) + # end-match + + # start-unset + pipeline.append({ + "$unset": ["_id", "description"] + }) + # end-unset + + # start-run-agg + aggregation_result = await products_coll.aggregate(pipeline) + # end-run-agg + + async for document in aggregation_result: + print(document) + + finally: + await client.close() diff --git a/source/includes/aggregation/one-to-one-join-async.py b/source/includes/aggregation/one-to-one-join-async.py new file mode 100644 index 00000000..f5352112 --- /dev/null +++ b/source/includes/aggregation/one-to-one-join-async.py @@ -0,0 +1,137 @@ +import asyncio +from pymongo import AsyncMongoClient +from datetime import datetime + +uri = "" +client = AsyncMongoClient(uri) + +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # start-colls + orders_coll = agg_db["orders"] + products_coll = agg_db["products"] + # end-colls + + # start-insert-orders + await orders_coll.delete_many({}) + + order_data = [ + { + "customer_id": "elise_smith@myemail.com", + "orderdate": datetime(2020, 5, 30, 8, 35, 52), + "product_id": "a1b2c3d4", + "value": 431.43 + }, + { + "customer_id": "tj@wheresmyemail.com", + "orderdate": datetime(2019, 5, 28, 19, 13, 32), + "product_id": "z9y8x7w6", + "value": 5.01 + }, + { + "customer_id": "oranieri@warmmail.com", + "orderdate": datetime(2020, 1, 1, 8, 25, 37), + "product_id": "ff11gg22hh33", + "value": 63.13 + }, + { + "customer_id": "jjones@tepidmail.com", + "orderdate": datetime(2020, 12, 26, 8, 55, 46), + "product_id": "a1b2c3d4", + "value": 429.65 + } + ] + + await orders_coll.insert_many(order_data) + # end-insert-orders + + # start-insert-products + await products_coll.delete_many({}) + + product_data = [ + { + "id": "a1b2c3d4", + "name": "Asus Laptop", + "category": "ELECTRONICS", + "description": "Good value laptop for students" + }, + { + "id": "z9y8x7w6", + "name": "The Day Of The Triffids", + "category": "BOOKS", + "description": "Classic post-apocalyptic novel" + }, + { + "id": "ff11gg22hh33", + "name": "Morphy Richardds Food Mixer", + "category": "KITCHENWARE", + "description": "Luxury mixer turning good cakes into great" + }, + { + "id": "pqr678st", + "name": "Karcher Hose Set", + "category": "GARDEN", + "description": "Hose + nosels + winder for tidy storage" + } + ] + + await products_coll.insert_many(product_data) + # end-insert-products + + pipeline = [] + + # start-match + pipeline.append({ + "$match": { + "orderdate": { + "$gte": datetime(2020, 1, 1, 0, 0, 0), + "$lt": datetime(2021, 1, 1, 0, 0, 0) + } + } + }) + # end-match + + # start-lookup + pipeline.append({ + "$lookup": { + "from": "products", + "localField": "product_id", + "foreignField": "id", + "as": "product_mapping" + } + }) + # end-lookup + + # start-set + pipeline.extend([ + { + "$set": { + "product_mapping": {"$first": "$product_mapping"} + } + }, + { + "$set": { + "product_name": "$product_mapping.name", + "product_category": "$product_mapping.category" + } + } + ]) + # end-set + + # start-unset + pipeline.append({"$unset": ["_id", "product_id", "product_mapping"]}) + # end-unset + + # start-run-agg + aggregation_result = await orders_coll.aggregate(pipeline) + # end-run-agg + + async for document in aggregation_result: + print(document) + + finally: + await client.close() + +asyncio.run(main()) \ No newline at end of file diff --git a/source/includes/aggregation/unpack-arrays-async.py b/source/includes/aggregation/unpack-arrays-async.py new file mode 100644 index 00000000..a57dc867 --- /dev/null +++ b/source/includes/aggregation/unpack-arrays-async.py @@ -0,0 +1,135 @@ +import asyncio +from pymongo import AsyncMongoClient + +uri = "" +client = AsyncMongoClient(uri) + +async def main(): + try: + agg_db = client["agg_tutorials_db"] + + # start-coll + orders_coll = agg_db["orders"] + # end-coll + + # start-insert-orders + await orders_coll.delete_many({}) + + order_data = [ + { + "order_id": 6363763262239, + "products": [ + { + "prod_id": "abc12345", + "name": "Asus Laptop", + "price": 431, + }, + { + "prod_id": "def45678", + "name": "Karcher Hose Set", + "price": 22, + }, + ] + }, + { + "order_id": 1197372932325, + "products": [ + { + "prod_id": "abc12345", + "name": "Asus Laptop", + "price": 429, + } + ] + }, + { + "order_id": 9812343774839, + "products": [ + { + "prod_id": "pqr88223", + "name": "Morphy Richards Food Mixer", + "price": 431, + }, + { + "prod_id": "def45678", + "name": "Karcher Hose Set", + "price": 21, + } + ] + }, + { + "order_id": 4433997244387, + "products": [ + { + "prod_id": "def45678", + "name": "Karcher Hose Set", + "price": 23, + }, + { + "prod_id": "jkl77336", + "name": "Picky Pencil Sharpener", + "price": 1, + }, + { + "prod_id": "xyz11228", + "name": "Russell Hobbs Chrome Kettle", + "price": 16, + } + ] + } + ] + + await orders_coll.insert_many(order_data) + # end-insert-orders + + pipeline = [] + + # start-unwind + pipeline.append({ + "$unwind": { + "path": "$products" + } + }) + # end-unwind + + # start-match + pipeline.append({ + "$match": { + "products.price": { + "$gt": 15 + } + } + }) + # end-match + + # start-group + pipeline.append({ + "$group": { + "_id": "$products.prod_id", + "product": {"$first": "$products.name"}, + "total_value": {"$sum": "$products.price"}, + "quantity": {"$sum": 1} + } + }) + # end-group + + # start-set + pipeline.append({ + "$set": { + "product_id": "$_id" + } + }) + # end-set + + # start-unset + pipeline.append({"$unset": ["_id"]}) + # end-unset + + # start-run-agg + aggregation_result = await orders_coll.aggregate(pipeline) + # end-run-agg + + async for document in aggregation_result: + print(document) + + finally: + await client.close() From 1779841660db114a115d322aa8289477794758ec Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Wed, 19 Mar 2025 14:35:16 -0400 Subject: [PATCH 2/3] Fixes --- source/aggregation/aggregation-tutorials.txt | 4 ---- .../aggregation/aggregation-tutorials/multi-field-join.txt | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/source/aggregation/aggregation-tutorials.txt b/source/aggregation/aggregation-tutorials.txt index 10b7c5a8..e5210ec3 100644 --- a/source/aggregation/aggregation-tutorials.txt +++ b/source/aggregation/aggregation-tutorials.txt @@ -71,10 +71,6 @@ Once you install the driver, create a file called app template for the aggregation tutorials. Select the :guilabel:`Synchronous` or :guilabel:`Asynchronous` tab to see the corresponding code: -.. literalinclude:: /includes/aggregation/template-app.py - :language: python - :copyable: true - .. tabs:: .. tab:: Synchronous diff --git a/source/aggregation/aggregation-tutorials/multi-field-join.txt b/source/aggregation/aggregation-tutorials/multi-field-join.txt index aad28571..c120ed24 100644 --- a/source/aggregation/aggregation-tutorials/multi-field-join.txt +++ b/source/aggregation/aggregation-tutorials/multi-field-join.txt @@ -99,7 +99,7 @@ or :guilabel:`Asynchronous` tab to see the corresponding code: .. tab:: Asynchronous :tabid: async - .. literalinclude:: /includes/aggregation/multi-field-join.py + .. literalinclude:: /includes/aggregation/multi-field-join-async.py :language: python :copyable: true :start-after: start-insert-products @@ -124,7 +124,7 @@ the ``orders`` collection as shown in the following code: .. tab:: Asynchronous :tabid: async - .. literalinclude:: /includes/aggregation/multi-field-join.py + .. literalinclude:: /includes/aggregation/multi-field-join-async.py :language: python :copyable: true :start-after: start-insert-orders @@ -241,7 +241,7 @@ Tutorial .. tab:: Asynchronous :tabid: async - .. literalinclude:: /includes/aggregation/multi-field-join.py + .. literalinclude:: /includes/aggregation/multi-field-join-async.py :language: python :copyable: true :start-after: start-run-agg From c01b27cc1143c084edf35f66d4428ff17486adb5 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Mon, 24 Mar 2025 10:25:47 -0400 Subject: [PATCH 3/3] NS feedback --- source/includes/aggregation/aggregation-template-async.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/includes/aggregation/aggregation-template-async.py b/source/includes/aggregation/aggregation-template-async.py index b34a86e6..7e62d3c6 100644 --- a/source/includes/aggregation/aggregation-template-async.py +++ b/source/includes/aggregation/aggregation-template-async.py @@ -30,7 +30,7 @@ async def main(): # ... aggregation_result = ... # Print the aggregation results. - for document in aggregation_result: + async for document in aggregation_result: print(document) finally: