Query Builder API
Querying for documents with Doctrine is just as simple as if you
weren't using Doctrine at all. Of course you always have your
traditional find()
and findOne()
methods but you also have
a Query
object with a fluent API for defining the query that
should be executed.
The Query
object supports several types of queries
- FIND
- FIND_AND_UPDATE
- FIND_AND_REMOVE
- INSERT
- UPDATE
- REMOVE
- DISTINCT_FIELD
- GEO_LOCATION
This section will show examples for the different types of queries.
Finding Documents
You have a few different ways to find documents. You can use the find()
method
to find a document by its identifier:
The find()
method is just a convenience shortcut method to:
The |
On the DocumentRepository
you have a few other methods for finding documents:
findBy
- find documents by an array of criteriafindOneBy
- find one document by an array of criteria
Creating a Query Builder
You can easily create a new Query\Builder
object with the
DocumentManager::createQueryBuilder()
method:
The first and only argument is optional, you can specify it later
with the find()
, update()
(deprecated), updateOne()
,
updateMany()
or remove()
method:
Executing Queries
You can execute a query by getting a Query
through the getQuery()
method:
Now you can execute()
that query and it will return an Iterator
for you to iterate over the results:
Debugging Queries
While building not complicated queries is really simple sometimes it might be hard to wrap your head
around more sophisticated queries that involves building separate expressions to work properly. If
you are not sure if your the query constructed with Builder is in fact correct you may want to debug()
it
At this point your query is prepared - that means ODM done all its job in renaming fields to match their
database name, added discriminator fields, applied filters, created correct references and all other things
you employ ODM to. The array returned by ->debug()
is what is passed to the underlying driver for the
query to be performed.
Getting Single Result
If you want to just get a single result you can use the Query#getSingleResult()
method:
Selecting Fields
You can limit the fields that are returned in the results by using
the select()
method:
In the results only the data from the username and password will be returned.
Index hints
You can force MongoDB to use a specific index for a query with the hint()
method (see hint)
Combining |
Selecting Distinct Values
Sometimes you may want to get an array of distinct values in a
collection. You can accomplish this using the distinct()
method:
The above would give you an array of all the distinct user ages!
MongoDB's distinct command
does not support sorting, so you cannot combine |
Refreshing Documents
When a query (e.g. find) returns one or more hydrated documents whose identifiers are already in the identity map, ODM returns the managed document instances for those results. In this case, a managed document's data may differ from whatever was just returned by the database query.
The query builder's refresh()
method may be used to instruct ODM to override
the managed document with data from the query result. This is comparable to
calling DocumentManager::refresh()
for a managed document. The document's
changeset will be reset in the process.
Refreshing is not applicable if hydration is disabled.
Fetching Documents as Read-Only
Similar to refresh()
, readOnly()
instructs ODM to not only hydrate the
latest data but also to create new document's instance (i.e. if found document
would be already managed by Doctrine, new instance will be returned) and not
register it in UnitOfWork
.
This technique can prove especially useful when using select()
with no intent
to update fetched documents.
Read-Only is not applicable if hydration is disabled.
Read-only mode is not deep, i.e. any references (be it owning or inverse) of fetched WILL be managed by Doctrine. This is a shortcoming of current implementation, may change in future and will not be considered a BC break (will be treated as a feature instead). |
To manage a document previously fetched in read-only mode, always use the
|
Disabling Hydration
For find queries the results by default are hydrated and you get
document objects back instead of arrays. You can disable this and
get the raw results directly back from mongo by using the
hydrate(false)
method:
Disabling Result Caching
Due to MongoDB cursors not being rewindable, ODM uses a caching iterator when returning results from queries. This cache allows you to iterate a result cursor multiple times without re-executing the original query. However, in long-running processes or when handling a large number of results, this can lead to high memory usage. To disable this result cache, you can tell the query builder to not return a caching iterator:
When setting this option to false
, attempting a second iteration will result
in an exception.
Limiting Results
You can limit results similar to how you would in a relational
database with a limit and offset by using the limit()
and
skip()
method.
Here is an example where we get the third page of blog posts when we show twenty at a time:
Sorting Results
You can sort the results by using the sort()
method:
If you want to an additional sort you can call sort()
again. The calls are stacked and ordered
in the order you call the method:
Conditional Operators
The conditional operators in Mongo are available to limit the returned results through a easy to use API. Doctrine abstracts this to a fluent object oriented interface with a fluent API. Here is a list of all the conditional operation methods you can use on the Query\Builder
object.
where($javascript)
in($values)
notIn($values)
equals($value)
notEqual($value)
gt($value)
gte($value)
lt($value)
lte($value)
range($start, $end)
size($size)
exists($bool)
type($type)
all($values)
mod($mod)
addOr($expr)
references($document)
includesReferenceTo($document)
Query for active administrator users:
Query for articles that have some tags:
Read more about the $in operator in the Mongo docs
Query for articles that do not have some tags:
Read more about the $nin operator in the Mongo docs.
Read more about the $ne operator in the Mongo docs.
Query for accounts with an amount due greater than 30:
Query for accounts with an amount due greater than or equal to 30:
Query for accounts with an amount due less than 30:
Query for accounts with an amount due less than or equal to 30:
Query for accounts with an amount due between 10 and 20:
Read more about conditional operators in the Mongo docs.
Query for articles with no comments:
Read more about the $size operator in the Mongo docs.
Query for users that have a login field before it was renamed to username:
Read more about the $exists operator in the Mongo docs.
Query for users that have a type field that is of integer bson type:
Read more about the $type operator in the Mongo docs.
Query for users that are in all the specified Groups:
Read more about the $all operator in the Mongo docs.
Read more about the $mod operator in the Mongo docs.
Query for users who have subscribed or are in a trial.
Read more about the $or operator in the Mongo docs.
The references()
method may be used to query the owning side of a
#[ReferenceOne] relationship. In the
following example, we query for all articles written by a particular user.
The includesReferenceTo()
method may be used to query the owning side of a
#[ReferenceMany] relationship. In
the following example, we query for the user(s) that have access to a particular
account.
Text Search
You can use the $text operator to run a text search against a field with a text index. To do so, create a document with a text index:
You can then run queries using the text operator:
To fetch the calculated score for the text search, use the selectMeta()
method:
You can also change the language used for stemming using the language()
method:
Update Queries
Doctrine also supports executing atomic update queries using the Query\Builder
object. You can use the conditional operations in combination with the ability to
change document field values atomically. Additionally if you are modifying a field
that is a reference you can pass managed document to the Builder and let ODM build
DBRef
object for you.
You have several modifier operations available to you that make it easy to update documents in Mongo:
set($name, $value, $atomic = true)
setNewObj($newObj)
inc($name, $value)
unsetField($field)
push($field, $value)
addToSet($field, $value)
popFirst($field)
popLast($field)
pull($field, $value)
pullAll($field, array $valueArray)
Updating multiple documents
By default Mongo updates only one document unless multi
option is provided and true.
In ODM the distinction is done by explicitly calling updateMany()
method of the builder:
Modifier Operations
Change a users password:
If you want to just set the values of an entirely new object you
can do so by passing false as the third argument of set()
to
tell it the update is not an atomic one:
Read more about the $set modifier in the Mongo docs.
You can set an entirely new object to update as well:
Increment the value of a document:
Read more about the $inc modifier in the Mongo docs.
Unset the login field from users where the login field still exists:
Read more about the $unset modifier in the Mongo docs.
Append new tag to the tags array:
Read more about the $push modifier in the Mongo docs.
Append new tags to the tags array:
Add value to array only if its not in the array already:
Read more about the $addToSet modifier in the Mongo docs.
Add many values to the array only if they do not exist in the array already:
Remove first element in an array:
Remove last element in an array:
Read more about the $pop modifier in the Mongo docs.
Remove all occurrences of value from array:
Read more about the $pull modifier in the Mongo docs.
Read more about the $pullAll modifier in the Mongo docs.
Remove Queries
In addition to updating you can also issue queries to remove documents from a collection. It works pretty much the same way as everything else and you can use the conditional operations to specify which documents you want to remove.
Here is an example where we remove users who have never logged in: