diff --git a/snooty.toml b/snooty.toml index de16c44e..e6fd2e0a 100644 --- a/snooty.toml +++ b/snooty.toml @@ -35,6 +35,8 @@ patch-version-number = "{+version-number+}.1" # a version = "v{+version-number+}" stable-api = "Stable API" api-root = "https://pymongo.readthedocs.io/en/{+patch-version-number+}/api/" +django-odm = "Django MongoDB Backend" +django-api = "https://django-mongodb.readthedocs.io/en/latest/" string-data-type = "``str``" int-data-type = "``int``" bool-data-type = "``bool``" diff --git a/source/interact-data.txt b/source/interact-data.txt new file mode 100644 index 00000000..024caa34 --- /dev/null +++ b/source/interact-data.txt @@ -0,0 +1,17 @@ +.. _django-interact-data: + +================== +Interact with Data +================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: odm, crud, query + +.. toctree:: + :caption: Interact with Data + + Perform Raw Queries \ No newline at end of file diff --git a/source/interact-data/raw-queries.txt b/source/interact-data/raw-queries.txt new file mode 100644 index 00000000..7fc172bc --- /dev/null +++ b/source/interact-data/raw-queries.txt @@ -0,0 +1,383 @@ +.. _django-raw-queries: + +============================ +Perform Raw Database Queries +============================ + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: number, amount, estimation, code example + +Overview +--------- + +In this guide, you can learn how to use {+django-odm+} to run +raw queries on your MongoDB database. Raw queries allow you to +query the database by using MongoDB's aggregation pipeline syntax +rather than Django methods. + +The Django QuerySet API provides a ``QuerySet.raw()`` method, which allows +you to perform raw SQL queries on relational databases. However, {+django-odm+} +does not support the ``raw()`` method. Instead, the ODM provides the +``QuerySet.raw_aggregate()`` method, which you can use to send instructions +to the database in pipeline stages. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``Movie`` and ``Theater`` models, which +represent collections in the ``sample_mflix`` database from the :atlas:`Atlas sample datasets `. +The model classes have the following definitions: + +.. code-block:: python + + from django.db import models + from django_mongodb_backend.fields import EmbeddedModelField, ArrayField + from django_mongodb_backend.managers import MongoManager + + class Movie(models.Model): + title = models.CharField(max_length=200) + plot = models.TextField(null=True) + runtime = models.IntegerField(default=0) + released = models.DateTimeField("release date", null=True) + awards = EmbeddedModelField(Award) + genres = ArrayField(models.CharField(max_length=100), blank=True) + objects = MongoManager() + + class Meta: + db_table = "movies" + + def __str__(self): + return self.title + + class Theater(models.Model): + theaterId = models.IntegerField(default=0) + objects = MongoManager() + + class Meta: + db_table = "theaters" + + def __str__(self): + return self.title + +To learn how to create a Django application that uses a similar ``Movie`` +model to interact with MongoDB documents, visit the :ref:`django-get-started` +tutorial. + +.. _django-raw-queries-run: + +Run Raw Queries +--------------- + +To run a raw database query, pass an aggregation pipeline +to the ``QuerySet.raw_aggregate()`` method. Aggregation pipelines +contain one or more stages that provide instructions on how to +process documents. After calling the ``raw_aggregate()`` method, +{+django-odm+} passes your pipeline to the ``pymongo.collection.Collection.aggregate()`` +method and returns the query results as model objects. + +.. tip:: + + To learn more about constructing aggregation pipelines, see + :manual:`Aggregation Pipeline ` + in the {+mdb-server+} manual. + +This section shows how to use the ``raw_aggregate()`` method +to perform the following tasks: + +- :ref:`django-raw-queries-filter-group` +- :ref:`django-raw-queries-search` +- :ref:`django-raw-queries-geospatial` + +.. _django-raw-queries-filter-group: + +Filter and Project Document Fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This example runs a raw database query by calling the +``raw_aggregate()`` method on your ``Movie`` objects, +which represent documents in the ``sample_mflix.movies`` +collection. The code passes the following aggregation pipeline stages +to ``raw_aggregate()``: + +- ``$match``: Filters for documents that have a ``title`` + field value of ``"The Parent Trap"`` + +- ``$project``: Includes the ``title`` and ``released`` fields + of the returned model objects + +.. io-code-block:: + :copyable: true + + .. input:: + :language: python + + from sample_mflix.models import Movie + + movies = Movie.objects.raw_aggregate([ + {"$match": {"title": "The Parent Trap"}}, + {"$project": { + "title": 1, + "released": 1 + } + }]) + + for m in movies: + print(f"Plot of {m.title}, released on {m.released}: {m.plot}\n") + + .. output:: + :language: none + :visible: false + + Plot of The Parent Trap, released on 1961-06-21 00:00:00+00:00: + Teenage twin girls swap places and scheme to reunite their divorced parents. + + Plot of The Parent Trap, released on 1998-07-29 00:00:00+00:00: + Identical twins, separated at birth and each raised by one of their + biological parents, discover each other for the first time at summer + camp and make a plan to bring their wayward parents back together. + +.. note:: + + The ``raw_aggregate()`` method returns deferred model instances, + which means that you can load fields omitted by the ``$project`` stage + on demand. In the preceding example, the query retrieves the ``title`` + and ``released`` fields. The print statement runs a separate query + to retrieve the ``plot`` field. + +.. _django-raw-queries-search: + +Run an Atlas Search Query +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can run Atlas Search queries on your database to perform +fine-grained text searches. These queries provide advanced search +functionality, such as matching text phrases, scoring results for +relevance, and highlighting matches. + +To specify an Atlas Search query, create an Atlas Search index +that covers the fields you want to query. Then, pass a ``$search`` +or ``$searchMeta`` stage in an aggregation pipeline parameter to +the ``raw_aggregate()`` method. + +.. important:: + + You cannot use {+django-odm+} to create Atlas Search indexes. + + For instructions on using the PyMongo driver to create an Atlas + Search index, see `Atlas Search and Vector Search Indexes + `__ + in the PyMongo documentation. + + For instructions on alternative methods of creating search indexes, + see :atlas:`Create an Atlas Search Index ` + in the Atlas documentation. + +This example runs an Atlas Search query by passing the ``$search`` pipeline +stage to the ``raw_aggregate()`` method. The code performs the following +actions: + +- Specifies the Atlas Search index that covers the ``plot`` field +- Queries for documents whose ``plot`` values contain the string + ``"whirlwind romance"`` with no more than ``3`` words between them +- Returns portions of the ``plot`` string values that match + the query and metadata that indicates where the matches + occurred +- Includes the ``title`` field and the ``highlight``, or matching text, + of each result + +.. io-code-block:: + :copyable: true + + .. input:: + :language: python + + movies = Movie.objects.raw_aggregate([ + { + "$search": { + "index": "", + "phrase": { + "path": "plot", + "query": "whirlwind romance", + "slop": 3 + }, + "highlight": { + "path": "plot" + } + } + }, + { + "$project": { + "title": 1, + "highlight": {"$meta": "searchHighlights"} + } + } + ]) + + for m in movies: + print(f"Title: {m.title}, text match details: {m.highlight}\n") + + .. output:: + :language: none + :visible: false + + Title: Tokyo Fiancèe, text match details: [{'score': 2.3079638481140137, 'path': 'plot', + 'texts': [{'value': 'A young Japanophile Belgian woman in Tokyo falls into a ', 'type': 'text'}, + {'value': 'whirlwind', 'type': 'hit'}, {'value': ' ', 'type': 'text'}, {'value': 'romance', + 'type': 'hit'}, {'value': ' with a Francophile Japanese student.', 'type': 'text'}]}] + + Title: Designing Woman, text match details: [{'score': 2.3041324615478516, 'path': 'plot', + 'texts': [{'value': 'A sportswriter and a fashion-designer marry after a ', 'type': 'text'}, + {'value': 'whirlwind', 'type': 'hit'}, {'value': ' ', 'type': 'text'}, {'value': 'romance', + 'type': 'hit'}, {'value': ', and discover they have little in common.', 'type': 'text'}]}] + + Title: Vivacious Lady, text match details: [{'score': 2.220963478088379, 'path': 'plot', + 'texts': [{'value': 'On a quick trip to the city, young university professor Peter Morgan + falls in love with nightclub performer Francey Brent and marries her after a ', 'type': 'text'}, + {'value': 'whirlwind', 'type': 'hit'}, {'value': ' ', 'type': 'text'}, {'value': 'romance', + 'type': 'hit'}, {'value': '. ', 'type': 'text'}]}] + + Title: Ek Hasina Thi, text match details: [{'score': 3.11773419380188, 'path': 'plot', 'texts': + [{'value': 'The ', 'type': 'text'}, {'value': 'whirlwind', 'type': 'hit'}, {'value': ' ', 'type': + 'text'}, {'value': 'romance', 'type': 'hit'}, {'value': ' turns sour when she is framed for his + underworld crimes. ', 'type': 'text'}]}] + + Title: Kick, text match details: [{'score': 2.00649356842041, 'path': 'plot', 'texts': [{'value': + 'An adrenaline junkie walks away from a ', 'type': 'text'}, {'value': 'whirlwind', 'type': 'hit'}, + {'value': ' ', 'type': 'text'}, {'value': 'romance', 'type': 'hit'}, {'value': ' and embraces a new + life as a thief, though he soon finds himself pursued by veteran police officer and engaged in a turf + war with a local gangster.', 'type': 'text'}]}] + + Title: A Tale of Winter, text match details: [{'score': 3.3978850841522217, 'path': 'plot', 'texts': + [{'value': 'Felicie and Charles have a serious if ', 'type': 'text'}, {'value': 'whirlwind', 'type': + 'hit'}, {'value': ' holiday ', 'type': 'text'}, {'value': 'romance', 'type': 'hit'}, {'value': '. ', + 'type': 'text'}]}] + +.. important:: + + When running the preceding example, ensure that you replace + the ```` placeholder with the name of your + Atlas Search index that covers the ``plot`` field. + +.. _django-raw-queries-geospatial: + +Query Geospatial Data +~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``raw_aggregate()`` method to run queries +on fields containing geospatial data. Geospatial data represents +a geographic location on the surface of the Earth or on a +Euclidean plane. + +To run a geospatial query, create a ``2d`` or ``2dsphere`` index on fields +containing geospatial data. Then, pass one of the following +query operators in an aggregation pipeline parameter to +the ``raw_aggregate()`` method: + +- ``$near`` +- ``$geoWithin`` +- ``$nearSphere`` +- ``$geoIntersects`` + +.. important:: + + You cannot use {+django-odm+} to create ``2d`` or ``2dsphere`` indexes. + + For instructions on using the PyMongo driver to create geospatial + indexes, see `Geospatial Indexes + `__ + in the PyMongo documentation. + + For instructions on using the MongoDB Shell to create geospatial + indexes, see :manual:`Geospatial Indexes ` + in the {+mdb-server+} manual. + +This example runs a geospatial query by passing the ``$match`` and +``$geoWithin`` pipeline stages to the ``raw_aggregate()`` method. The +code performs the following actions: + +- Specifies a list of coordinates that represent Chicago's boundaries +- Queries for documents in which the ``location.geo`` field stores a + location within the Chicago area +- Retrieves and prints the ``theaterId`` values of each movie theater in Chicago + +.. io-code-block:: + :copyable: true + + .. input:: + :language: python + + chicago_bounds = { + "type": "Polygon", + "coordinates": [[ + [-87.851, 41.976], + [-87.851, 41.653], + [-87.651, 41.653], + [-87.651, 41.976], + [-87.851, 41.976] + ]] + } + + theaters = Theater.objects.raw_aggregate([ + { + "$match": { + "location.geo": { + "$geoWithin": { + "$geometry": chicago_bounds + } + } + } + }, + { + "$project": { + "theaterId": 1 + } + } + ]) + + for t in theaters: + print(f"Theater ID: {t.theaterId}") + + .. output:: + :language: none + :visible: false + + Theater ID: 2447 + Theater ID: 311 + Theater ID: 320 + Theater ID: 2960 + Theater ID: 2741 + Theater ID: 306 + Theater ID: 322 + Theater ID: 319 + Theater ID: 2862 + Theater ID: 1777 + Theater ID: 814 + Theater ID: 323 + +Additional Information +---------------------- + +To view more examples that use the ``raw_aggregate()`` method, +see `QuerySet API Reference <{+django-api+}querysets.html>`__ +in the {+django-odm+} API documentation. + +To learn more about running aggregation operations, see +:manual:`Aggregation Operations ` +in the {+mdb-server+} manual. + +To learn more about Atlas Search, see :atlas:`Atlas Search ` +in the Atlas documentation. + +To learn more about running geospatial queries, see +:manual:`Geospatial Queries ` +in the {+mdb-server+} manual. \ No newline at end of file