Статьи

MongoDB Revisited

В моей предыдущей статье « Введение в MongoDB» я обсуждал установку Mongo, его PHP-расширение и способы выполнения простых операций вставки и поиска. Конечно, есть намного больше возможностей, чем я упомянул, поэтому я хотел написать еще одну статью, чтобы показать вам некоторые из них. В этой статье вы узнаете о курсорах, дополнительных фильтрах запросов и выполнении запросов к массивам и внедренным документам.

курсоры

Сначала поговорим о курсорах в MongoDB. В предыдущей статье вы видели пример операции поиска, подобной приведенной ниже, которая выбирает все документы, найденные в коллекции, соответствующие пройденным критериям:

<?php $cursor = $collection->find(array("author" => "shreef")); 

В то время я лишь кратко упомянул, что метод find() возвращает экземпляр MongoCursor (а не список фактически найденных документов). Ничего не запрашивается из MongoDB, пока вы не вызовете результат от курсора.

Курсор Монго имеет две жизненные стадии. Первый этап — «этап предварительного запроса». На этом этапе курсор не пытался выполнить запрос, и у вас есть возможность добавить больше деталей и ограничений. Например, если вы хотите указать максимальное количество документов, которые должны быть возвращены, вы можете использовать метод limit() курсора.

 <?php $cursor = $collection->find(array("author" => "shreef")); $cursor = $cursor->limit(5); 

Часто вы увидите вызовы методов, соединенные вместе, вот так:

 <?php $cursor = $collection->find(array("author" => "shreef"))->limit(5); 

Курсор фактически выполняет запрос и переходит на его второй этап, «этап после запроса», когда вы пытаетесь прочитать результаты из курсора, либо вызвав метод next() напрямую, либо выполнив итерацию курсора:

 <?php foreach ($cursor as $doc) { // do something } 

Также стоит отметить, что не все документы, соответствующие критериям вашего запроса, будут возвращены одновременно. Если общий размер результатов велик, вы, возможно, не захотите загружать все эти данные в память. MonogDB имеет предел возвращаемых результатов в 4-16 МБ. Когда вы закончите итерацию по первому пакету результатов, курсор будет прозрачно извлекать следующий пакет документов. Все это происходит в фоновом режиме, поэтому вам не нужно беспокоиться об этом при написании кода, но стоит упомянуть, чтобы вы знали, что на самом деле происходит.

По умолчанию MongoDB будет поддерживать курсор на сервере до тех пор, пока вы не закончите чтение всех назначенных ему результатов или не пройдет 10 минут с момента его создания. Вы можете использовать MongoCursor timeout() MongoCursor для увеличения или уменьшения срока службы курсора (в миллисекундах). Вы также можете передать -1 в timeout() чтобы отключить поведение времени ожидания, но тогда вам придется перебирать все результаты, иначе курсор будет жить вечно и исчерпывать ресурсы сервера.

Операторы запросов

Запросы в MongoDB легко понять, увидев лишь несколько примеров. Вы видели, что вы можете отправить запрос в виде массива, содержащего значения для сопоставления, и вы можете точно настроить критерии соответствия, используя специальные $ -операторы, поддерживаемые MongoDB. Я хотел бы показать вам операторы, используемые для логических сравнений, но сначала есть одно важное замечание: всегда не забывайте использовать одинарные кавычки с $ -операторами или избегать их. Вы можете догадаться, почему.

$ lt, $ lte, $ gt, $ gte

Операторы $lt , $lte , $gt и $gte эквивалентны < , <= , > и >= . Чтобы найти все документы в коллекции блогов с числом просмотров, превышающим или равным 50 000, вы должны построить запрос следующим образом:

 <?php $collection->find(array("views" => array('$gte' => 50000))); 

views — это имя поля, которое должно содержать значение, большее или равное 50 000.

$ и, $ или, и $ ни

Иногда вам нужно убедиться, что значение удовлетворяет более чем одному условию или хотя бы одному из нескольких условий. Операторы $and и $or используются для предоставления логических условий, таких же, как вы уже привыкли. Если вы хотите найти все посты в блоге с количеством просмотров, превышающим или равным 50 000, написанных «Shreef» или «Timothy», вы должны написать запрос, подобный этому:

 <?php $collection->find(array( "views" => array('$gte' => 50000), "$or" => array( array("author" => "Shreef"), array("author" => "Timothy")))); 

Оператор $nor используется аналогично, но гарантирует, что ни одно из условий не будет выполнено.

$ in и $ nin (не в)

Оператор $in полезен, когда вы хотите передать список значений, одно из которых должно соответствовать полю, которое вы проверяете. Оператор $nin делает обратное, проверяя, что поле не соответствует ни одному из значений. Это часто может быть более читабельным, чем использование ранее упомянутых логических операторов, когда вы выполняете простой запрос.

 <?php $collection->find(array( "authors" => array('$in' => array("Shreef", "Timothy")))); 

Запросы к массивам

Предыдущие примеры демонстрировали возможность создавать и запрашивать поля, содержащие одно значение, но MongoDB также поддерживает значения массива. Например, чтобы предоставить список тегов, которые организуют записи в блоге, вы можете просто указать их как массив.

 <?php $collection->insert(array( "title"  => "More Mongo",    "author" => "Shreef",    "tags"   => array("php", "mongodb"))); 

Теперь, чтобы найти документы с тегом «php», вы можете сделать следующее:

 <?php $collection->find(array("tags" => "php")); 

Запрос к массиву — это то же самое, что запрос к полю с одним значением, и любой массив, в котором указано «php» в качестве одного из значений тега, будет совпадать. Вы также можете использовать все ранее упомянутые $ -операторы с массивами, плюс оператор $all который позволяет вам проверять массив, содержащий все переданные значения.

 <?php $collection->find(array( "tags" => array('$all' => array("php", "mongodb"))); 

Теперь с $all этот запрос будет сопоставлять только документы с тегами «php» и «mongodb». Наличие только одного из этих значений будет недостаточно для соответствия.

Запросы на встроенные документы

Встраивание документов — это одна из вещей, с которой вам, вероятно, придется столкнуться, если вы используете MongoDB для любого серьезного приложения. Например, может быть логичным встроить все комментарии в сообщение в одном документе в блоге. Давайте предположим, что София добавила новый комментарий; Вы можете обновить документ блога, нажав на его комментарий в массиве комментариев следующим образом:

 <?php $postId = "xxx"; $collection->update( array("_id" => new Mongo($postId)), array('$push' => array( "comments" => array( "author"  => "Sophia",            "content" => "hi...")))); 

Как вы видели, я использовал оператор $push который, по его названию, как вы можете догадаться, помещает новый элемент в массив. С точки зрения производительности этот подход лучше, чем загрузка всего документа из базы данных, его изменение и последующая запись в базу данных.

Теперь, когда вы хотите получить все комментарии, сделанные Софией, вы можете запросить их следующим образом:

 <?php $collection->find(array("comments.author" => "Sophia")); 

Вы можете писать имена полей, как это, так как MongoDB поддерживает точечную запись. Точечная запись позволяет вам писать имена полей, как если бы они были свойствами объекта; написав «comments.author», я могу сослаться на значение поля author которое существует в объекте comments .

Методы sort () и skip ()

Я уже упоминал метод limit() который принимает количество документов, возвращаемых при выполнении запроса. MongoCursor предлагает другие методы, которые вы, несомненно, найдете полезными, такие как sort() и skip() .

Метод sort() похож на предложение ORDER BY в SQL — вы предоставляете несколько полей, которые будут использоваться для сортировки результатов, и указываете, как каждое из них сортируется. 1 представляет восходящий и -1 представляет нисходящий.

 <?php $collection ->find() ->sort(array("createdAt" => -1 , "author" => 1)); 

Это позволит отсортировать соответствующие документы по дате их создания в порядке убывания, а затем по автору в порядке возрастания.

Метод skip() передает указанное количество документов, соответствующих запросу. Например:

 <?php $collection ->find(array("author" => "Shreef")) ->sort(array("createdAt" => -1)) ->limit(5) ->skip(10); 

Запрос выполняет поиск всех документов, созданных Shreef, упорядоченных по времени их создания, а затем пропускает первые 10 документов, которые в противном случае были бы возвращены, вместо этого возвращая только следующие 5 документов.

Сортировка документов приводит к очень важному моменту: индексация в MongoDB так же важна, как и в СУБД, такой как MySQL.

Индексы

Выполнение запросов без индексов не имеет большого смысла ни в одной базе данных. Вы должны создать индексы для полей, на которые будете ссылаться в своих запросах, включая те, которые вы будете использовать для сортировки. Вы можете создать индекс в MongoDB, используя метод ensureIndex() . Метод принимает список полей в качестве первого аргумента и необязательный список параметров в качестве второго. Вот пример:

 <?php $collection->ensureIndex( array("author" => 1), array("name" => "idx_author")); 

Это создаст восходящий индекс, используя поле author и я по выбору решил назвать индекс «idx_author». Вы можете создать индекс с несколькими полями, добавив имена полей в качестве ключей к массиву, переданному в первом аргументе, и установите их значения в 1 или -1. Использование 1 означает, что вы хотите, чтобы индексирование поля было восходящим, а использование -1 означает, что вы хотите, чтобы оно было убывающим.

Другие опции, которые могут вам понадобиться, это unique опции и опции dropDups Вы можете создать индекс, чтобы обеспечить уникальность поля для всех документов в коллекции, установив для параметра «true» unique значение. Если вы установите dropDups true, MongoDB dropDups все дубликаты, кроме одного.

 <?php $collection->ensureIndex( array("title" => 1), array("unique" => true, "dropDups" => true)); 

MongoDB пытается угадать лучший индекс для использования при выполнении вашего запроса, но иногда он не может выбрать правильный. Вы используете метод hint() чтобы сообщить ему об используемых полях.

 <?php $collection ->find(array("author" => "shreef")) ->hint(array("author" => 1)); 

В этом примере я сказал Mongo использовать индекс, состоящий из поля author , отсортированного по возрастанию. Вы должны пройти те же критерии, которые вы использовали для создания индекса, и в том же порядке. Если нет индекса, состоящего из переданных полей в том же порядке, будет сгенерировано исключение.

Вы можете быть удивлены, почему вы не можете просто использовать имя индекса вместо этого. На самом деле, это поддерживается Mongo, но похоже, что оно не реализовано в PHP API. Метод hint() в PHP API принимает только массив. Будем надеяться, что это будет исправлено в ближайшее время!

Резюме

MongoDB улучшается с каждым выпуском, и есть еще много функций, которые я не упомянул здесь. Руководство по PHP дает вам некоторую информацию, но лучше прочитать документацию MongoDB, чтобы узнать о последних и лучших возможностях. Эта статья содержит множество вещей, которые вы можете попробовать, например, использование $ -операторов, запрос встроенных документов, сортировку и пропуск результатов. Не стесняйтесь возиться с ними и оставить свои вопросы и свои выводы в комментариях.

Изображение через Пахнющу / Shutterstock