550 lines
14 KiB
Plaintext
550 lines
14 KiB
Plaintext
|
Indexes
|
||
|
=======
|
||
|
|
||
|
Working with indexes in the MongoDB ODM is pretty straight forward.
|
||
|
You can have multiple indexes, they can consist of multiple fields,
|
||
|
they can be unique and you can give them an order. In this chapter
|
||
|
we'll show you examples of indexes using annotations.
|
||
|
|
||
|
First here is an example where we put an index on a single
|
||
|
property:
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/** @Document */
|
||
|
class User
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="string") @Index */
|
||
|
public $username;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<field name="username" index="true" />
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
fields:
|
||
|
username:
|
||
|
index: true
|
||
|
|
||
|
|
||
|
Index Options
|
||
|
-------------
|
||
|
|
||
|
You can customize the index with some additional options:
|
||
|
|
||
|
-
|
||
|
**name** - The name of the index. This can be useful if you are
|
||
|
indexing many keys and Mongo complains about the index name being
|
||
|
too long.
|
||
|
-
|
||
|
**dropDups** - If a unique index is being created and duplicate
|
||
|
values exist, drop all but one duplicate value.
|
||
|
-
|
||
|
**background** - Create indexes in the background while other
|
||
|
operations are taking place. By default, index creation happens
|
||
|
synchronously. If you specify TRUE with this option, index creation
|
||
|
will be asynchronous.
|
||
|
-
|
||
|
**safe** - You can specify a boolean value for checking if the
|
||
|
index creation succeeded. The driver will throw a
|
||
|
MongoCursorException if index creation failed.
|
||
|
-
|
||
|
**expireAfterSeconds** - If you specify this option then the associated
|
||
|
document will be automatically removed when the provided time (in seconds)
|
||
|
has passed. This option is bound to a number of limitations, which
|
||
|
are documented at https://docs.mongodb.com/manual/tutorial/expire-data/.
|
||
|
-
|
||
|
**order** - The order of the index (asc or desc).
|
||
|
-
|
||
|
**unique** - Create a unique index.
|
||
|
-
|
||
|
**sparse** - Create a sparse index. If a unique index is being created
|
||
|
the sparse option will allow duplicate null entries, but the field must be
|
||
|
unique otherwise.
|
||
|
-
|
||
|
**partialFilterExpression** - Create a partial index. Partial indexes only
|
||
|
index the documents in a collection that meet a specified filter expression.
|
||
|
By indexing a subset of the documents in a collection, partial indexes have
|
||
|
lower storage requirements and reduced performance costs for index creation
|
||
|
and maintenance. This feature was introduced with MongoDB 3.2 and is not
|
||
|
available on older versions.
|
||
|
|
||
|
Unique Index
|
||
|
------------
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/** @Document */
|
||
|
class User
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="string") @Index(unique=true, order="asc") */
|
||
|
public $username;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<field fieldName="username" index="true" unique="true" order="asc" />
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
fields:
|
||
|
username:
|
||
|
index: true
|
||
|
unique: true
|
||
|
order: true
|
||
|
|
||
|
For your convenience you can quickly specify a unique index with
|
||
|
``@UniqueIndex``:
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/** @Document */
|
||
|
class User
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="string") @UniqueIndex(order="asc") */
|
||
|
public $username;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<field fieldName="username" unique="true" order="asc" />
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
fields:
|
||
|
username:
|
||
|
unique: true
|
||
|
order: true
|
||
|
|
||
|
If you want to specify an index that consists of multiple fields
|
||
|
you can specify them on the class doc block:
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/**
|
||
|
* @Document
|
||
|
* @UniqueIndex(keys={"accountId"="asc", "username"="asc"})
|
||
|
*/
|
||
|
class User
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="int") */
|
||
|
public $accountId;
|
||
|
|
||
|
/** @Field(type="string") */
|
||
|
public $username;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping"
|
||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping
|
||
|
http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping.xsd">
|
||
|
|
||
|
<document name="Documents\User">
|
||
|
<indexes>
|
||
|
<index>
|
||
|
<option name="unique" value="true" />
|
||
|
<key name="accountId" order="asc" />
|
||
|
<key name="username" order="asc" />
|
||
|
</index>
|
||
|
</indexes>
|
||
|
</document>
|
||
|
</doctrine-mongo-mapping>
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
Documents\User:
|
||
|
indexes:
|
||
|
usernameacctid:
|
||
|
options:
|
||
|
unique: true
|
||
|
keys:
|
||
|
accountId:
|
||
|
order: asc
|
||
|
username:
|
||
|
order: asc
|
||
|
|
||
|
To specify multiple indexes you must use the ``@Indexes``
|
||
|
annotation:
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
/**
|
||
|
* @Document
|
||
|
* @Indexes({
|
||
|
* @Index(keys={"accountId"="asc"}),
|
||
|
* @Index(keys={"username"="asc"})
|
||
|
* })
|
||
|
*/
|
||
|
class User
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="int") */
|
||
|
public $accountId;
|
||
|
|
||
|
/** @Field(type="string") */
|
||
|
public $username;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping"
|
||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping
|
||
|
http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping.xsd">
|
||
|
|
||
|
<document name="Documents\User">
|
||
|
<indexes>
|
||
|
<index>
|
||
|
<key name="accountId" order="asc" />
|
||
|
</index>
|
||
|
<index>
|
||
|
<key name="username" order="asc" />
|
||
|
</index>
|
||
|
</indexes>
|
||
|
</document>
|
||
|
</doctrine-mongo-mapping>
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
Documents\User:
|
||
|
indexes:
|
||
|
accountId:
|
||
|
keys:
|
||
|
accountId:
|
||
|
order: asc
|
||
|
username:
|
||
|
keys:
|
||
|
username:
|
||
|
order: asc
|
||
|
|
||
|
Embedded Indexes
|
||
|
----------------
|
||
|
|
||
|
You can specify indexes on embedded documents just like you do on normal documents. When Doctrine
|
||
|
creates the indexes for a document it will also create all the indexes from its mapped embedded
|
||
|
documents.
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/** @EmbeddedDocument */
|
||
|
class Comment
|
||
|
{
|
||
|
/** @Field(type="date") @Index */
|
||
|
private $date;
|
||
|
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
Now if we had a ``BlogPost`` document with the ``Comment`` document embedded many times:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
namespace Documents;
|
||
|
|
||
|
/** @Document */
|
||
|
class BlogPost
|
||
|
{
|
||
|
// ...
|
||
|
|
||
|
/** @Field(type="string") @Index */
|
||
|
private $slug;
|
||
|
|
||
|
/** @EmbedMany(targetDocument="Comment") */
|
||
|
private $comments;
|
||
|
}
|
||
|
|
||
|
If we were to create the indexes with the ``SchemaManager``:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
$sm->ensureIndexes();
|
||
|
|
||
|
It will create the indexes from the ``BlogPost`` document but will also create the indexes that are
|
||
|
defined on the ``Comment`` embedded document. The following would be executed on the underlying MongoDB
|
||
|
database:
|
||
|
|
||
|
..
|
||
|
|
||
|
db.BlogPost.ensureIndexes({ 'slug' : 1, 'comments.date': 1 })
|
||
|
|
||
|
Also, for your convenience you can create the indexes for your mapped documents from the
|
||
|
:doc:`console <console-commands>`:
|
||
|
|
||
|
..
|
||
|
|
||
|
$ php mongodb.php mongodb:schema:create --index
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
If you are :ref:`mixing document types <embed_mixing_document_types>` for your
|
||
|
embedded documents, ODM will not be able to create indexes for their fields
|
||
|
unless you specify a discriminator map for the :ref:`embed-one <embed_one>`
|
||
|
or :ref:`embed-many <embed_many>` relationship.
|
||
|
|
||
|
Geospatial Indexing
|
||
|
-------------------
|
||
|
|
||
|
You can specify a geospatial index by just specifying the keys and
|
||
|
options structures manually:
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
/**
|
||
|
* @Document
|
||
|
* @Index(keys={"coordinates"="2d"})
|
||
|
*/
|
||
|
class Place
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @EmbedOne(targetDocument="Coordinates") */
|
||
|
public $coordinates;
|
||
|
}
|
||
|
|
||
|
/** @EmbeddedDocument */
|
||
|
class Coordinates
|
||
|
{
|
||
|
/** @Field(type="float") */
|
||
|
public $latitude;
|
||
|
|
||
|
/** @Field(type="float") */
|
||
|
public $longitude;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<indexes>
|
||
|
<index>
|
||
|
<key name="coordinates" order="2d" />
|
||
|
</index>
|
||
|
</indexes>
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
indexes:
|
||
|
coordinates:
|
||
|
keys:
|
||
|
coordinates: 2d
|
||
|
|
||
|
Partial indexes
|
||
|
---------------
|
||
|
|
||
|
You can create a partial index by adding a ``partialFilterExpression`` to any
|
||
|
index.
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
/**
|
||
|
* @Document
|
||
|
* @Index(keys={"city"="asc"}, partialFilterExpression={"version"={"$gt"=1}})
|
||
|
*/
|
||
|
class Place
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="string") */
|
||
|
public $city;
|
||
|
|
||
|
/** @Field(type="int") */
|
||
|
public $version;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
<indexes>
|
||
|
<index>
|
||
|
<key name="city" order="asc" />
|
||
|
<partial-filter-expression>
|
||
|
<field name="version" value="1" operator="gt" />
|
||
|
</partial-filter-expression>
|
||
|
</index>
|
||
|
</indexes>
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
indexes:
|
||
|
partialIndexExample:
|
||
|
keys:
|
||
|
coordinates: asc
|
||
|
options:
|
||
|
partialFilterExpression:
|
||
|
version: { $gt: 1 }
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
Partial indexes are only available with MongoDB 3.2 or newer. For more
|
||
|
information on partial filter expressions, read the
|
||
|
`official MongoDB documentation <https://docs.mongodb.com/manual/core/index-partial/>`_.
|
||
|
|
||
|
Requiring Indexes
|
||
|
-----------------
|
||
|
|
||
|
.. note::
|
||
|
Requiring Indexes was deprecated in 1.2 and will be removed in 2.0.
|
||
|
|
||
|
Sometimes you may want to require indexes for all your queries to ensure you don't let stray unindexed queries
|
||
|
make it to the database and cause performance problems.
|
||
|
|
||
|
|
||
|
.. configuration-block::
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
/**
|
||
|
* @Document(requireIndexes=true)
|
||
|
*/
|
||
|
class Place
|
||
|
{
|
||
|
/** @Id */
|
||
|
public $id;
|
||
|
|
||
|
/** @Field(type="string") @Index */
|
||
|
public $city;
|
||
|
}
|
||
|
|
||
|
.. code-block:: xml
|
||
|
|
||
|
// Documents.Place.dcm.xml
|
||
|
|
||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
||
|
<doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping"
|
||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping
|
||
|
http://doctrine-project.org/schemas/orm/doctrine-mongo-mapping.xsd">
|
||
|
|
||
|
<document name="Documents\Place" require-indexes="true">
|
||
|
<field fieldName="id" id="true" />
|
||
|
<field fieldName="city" type="string" />
|
||
|
<indexes>
|
||
|
<index>
|
||
|
<key name="city">
|
||
|
</index>
|
||
|
</indexes>
|
||
|
</document>
|
||
|
</doctrine-mongo-mapping>
|
||
|
|
||
|
.. code-block:: yaml
|
||
|
|
||
|
# Documents.Place.dcm.yml
|
||
|
|
||
|
Documents\Place:
|
||
|
fields:
|
||
|
id:
|
||
|
id: true
|
||
|
city:
|
||
|
type: string
|
||
|
indexes:
|
||
|
index1:
|
||
|
keys:
|
||
|
city: asc
|
||
|
|
||
|
When you run queries it will check that it is indexed and throw an exception if it is not indexed:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
$qb = $dm->createQueryBuilder('Documents\Place')
|
||
|
->field('city')->equals('Nashville');
|
||
|
$query = $qb->getQuery();
|
||
|
$places = $query->execute();
|
||
|
|
||
|
When you execute the query it will throw an exception if `city` was not indexed in the database. You can control
|
||
|
whether or not an exception will be thrown by using the `requireIndexes()` method:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
$qb->requireIndexes(false);
|
||
|
|
||
|
You can also check if the query is indexed and with the `isIndexed()` method and use it to display your
|
||
|
own notification when a query is unindexed:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
$query = $qb->getQuery();
|
||
|
if (!$query->isIndexed()) {
|
||
|
$notifier->addError('Cannot execute queries that are not indexed.');
|
||
|
}
|
||
|
|
||
|
If you don't want to require indexes for all queries you can set leave `requireIndexes` as false and control
|
||
|
it on a per query basis:
|
||
|
|
||
|
.. code-block:: php
|
||
|
|
||
|
<?php
|
||
|
|
||
|
$qb->requireIndexes(true);
|
||
|
$query = $qb->getQuery();
|
||
|
$results = $query->execute();
|