Query

Querying Documents

Writing queries for selecting MongoDB data is easy. Querying in Djongo works exactly like in Django because Djongo translates Django’s ORM queries into MongoDB queries under the hood. You interact with your data using model managers and QuerySets, which are lazy, iterable representations of database queries.

A QuerySet is constructed when you call methods like .all() or .filter(), and only hits the database when you actually evaluate it. This design allows you to build queries incrementally without immediately hitting the database. QuerySets make it easy to reason about database access while writing clean Python code.

qs = Entry.objects.all()
copy code

Retrieving Objects

The most common methods to retrieve data are .all(), .get(), and .filter().

  • .all() returns every record as a QuerySet.

  • .get() returns a single object and raises an error if none or multiple match.

  • .filter() returns a QuerySet matching given conditions.

Filters use field lookups expressed with double underscores (e.g. field__lookup=value) and are injection safe because Django escapes parameters.

post = Entry.objects.get(id=1)
recent_posts = Entry.objects.filter(published_date__year=2024)
copy code

Chaining Filters and Field Lookups

You can chain filters to build complex queries. Each filter call returns a new QuerySet, allowing you to progressively narrow down results. Django supports lookups like exact, iexact, contains, icontains, gte, lte, in, and range.

QuerySets remain lazy until evaluated, so chaining does not hit the database immediately. This pattern allows for clean, readable, composable queries.

qs = Entry.objects.filter(published_date__year=2024)\
                 .filter(title__icontains="djongo")\
                 .order_by('-published_date')
copy code

Ordering, Slicing, and Limiting

You can order results using .order_by() and limit them using Python slicing syntax. Ordering accepts one or more field names, with a - prefix for descending order. Slicing translates to SQL/MongoDB LIMIT and OFFSET.

You can also use .first() and .last() to get a single record or None if none exist. These tools let you fetch only the portion of data you need efficiently.

latest_five = Entry.objects.order_by('-published_date')[:5]
first_post = Entry.objects.order_by('published_date').first()
copy code

Django ORM (and therefore Djongo) supports aggregations with functions like Count, Sum, Avg, Min, and Max. Use .aggregate() to return a single summary value, or .annotate() to add computed values to each result row.

You can traverse related fields using double underscores (following ForeignKey, OneToOneField, or ManyToManyField). This lets you perform joins and calculations without writing raw database queries.

from djongo.models import Count

authors = Author.objects.annotate(num_posts=Count('post'))
for a in authors:
    print(a.name, a.num_posts)
copy code

Evaluating QuerySets

A QuerySet executes when it’s evaluated — when you iterate over it, convert it to a list, access a slice, or call .count(), .exists(), .first(), or .last().

The results are cached after the first evaluation, so repeated iterations don’t hit the database again unless you call .all() to refresh. This lazy and cached behavior helps minimize database traffic and improve performance.

You can learn more about making queries here.

qs = Entry.objects.filter(published_date__year=2024)
print(list(qs))  # Executes the query
print(list(qs))  # Uses the cached results, no new query
copy code

Querying Embedded fields

To query all BlogPost with content made by authors whose name startswith Beatles use the following query:

entries = Entry.objects.filter(blog__startswith={'name': 'Beatles'})
copy code

Internally Djongo converts this query (for BlogPost collection) to the form:

filter = {
    'blog.name': {
        '$regex': '^Beatles.*$'
    }
}
copy code

For querying nested embedded fields provide the appropriate dictionary value

entries = Entry.objects.filter(blog__startswith={'tagline': {'subtitle': 'Artist'}})
copy code

Internally Djongo converts this query (for BlogPost collection) to the form:

filter = {
    'blog.tagline.subtitle': {
            '$regex': '^Artist.*$'
    }
}
copy code

Querying Array fields

Djongo uses a mixture of Django query syntax and MongoDB query syntax. Consider a query to retrieve all entries made by the author Paul. Using ManyToManyField this requires 2 SQL queries. First selects the id for author Paul from the author table. Next, a JOIN with entry_authors and entry gives the corresponding entries.

With ArrayField the query reduces to a single simple query:

entries = Entry.objects.filter(authors={'name': 'Paul'})
copy code

Djongo lets you get even more specific with your queries. To query all entries where the third author is Paul:

entries = Entry.objects.filter(authors={'2.name': 'Paul'})
copy code

Note: In MongoDB the first element in the array starts at index 0.

Djongo lets you run MongoDB text search queries on Django CharField and TextField. To run a text search, use the text_search operator that comes built in with Djongo.

from djongo.models.indexes import TextIndex
from djongo import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    class Meta:
        indexes = [
            TextIndex(fields=['name'])
        ]
copy code
Blog.objects.filter(name__text_search='Paul Lennon')
copy code

This will generate the pymongo command:

db.blog.find( { '$text': { '$search': "Paul Lennon" } } )
copy code

Geospatial Queries

Geospatial queries are carried out in Djongo by using a combination of the near lookup operator and the Near search object.

class Near:
    def __init__(self,
                 type=None,
                 coordinates=None,
                 minDistance=None,
                 maxDistance=None):
copy code

Example

from djongo.models.indexes import TwoDSphereIndex
from djongo import models

class Location(models.Model):
    type = models.CharField(max_length=100)
    coordinates = models.ArrayField()

    class Meta:
        abstract = True

class Entry(models.Model):
    loc = models.EmbeddedField(
        model_container=Location,
    )
    class Meta:
        indexes = [
            TwoDSphereIndex(fields=['loc'])
        ]
copy code
from djongo.models import Near

search_region = Near(
    type='point',
    coordinates=[-33.9, 89.81],
    minDistance=100,
    maxDistance=200
)

Entry.objects.filter(loc__near=search_region)
copy code

This generates the following pymongo search query:

db.entry.find({
     'loc': 
        { '$near':
          {
            '$geometry': { 'type': "Point",  'coordinates': [-33.9, 89.81] },
            '$minDistance': 100,
            '$maxDistance': 200
          }
        }
   })
copy code

Specifying Query Options

Djongo lets you specify the configuration of the find command into your QuerySets. Call the configure method on a QuerySet to configure the find query. All options supported by aggregate or find can be included as kwargs. Example of valid arguments:

Arguments

ArgumentTypeDescription
allowDiskUsebooleanEnables writing to temporary files
collationCollationUsed to specify the collation. Takes an instance of Collation

Example

Blog.objects.filter(name='John Lennon').configure(hint=['-tagline'])
copy code

This generates the following pymongo find query:

db.blog.find({'name': 'John Lennon'}, hint=[('tagline', pymongo.DESCENDING)])
copy code

Tailable Cursors

Tailable cursors are used to retrieve data from capped collections. The querySet first has to be configured using configure to use a tailable cursor in the pymongo find command. Results of the querySet can only be accessed by generating an iterator by calling the QuerySet iterator

Example

iterable = Blog.objects.filter(name='John').configure(cursor_type=CursorType.TAILABLE).iterator()
for blog in iterable:
    blog.name
copy code