Альтернатива, которая не была упомянута, но которая может быть более эффективной для некоторых (не будет работать с пустыми записями), заключается в использовании разреженного индекса (записи в индексе существуют только тогда, когда в поле что-то есть). Вот пример набора данных:
db.foo.find()
{ "_id" : ObjectId("544540b31b5cf91c4893eb94"), "imageUrl" : "http://example.com/foo.jpg" }
{ "_id" : ObjectId("544540ba1b5cf91c4893eb95"), "imageUrl" : "http://example.com/bar.jpg" }
{ "_id" : ObjectId("544540c51b5cf91c4893eb96"), "imageUrl" : "http://example.com/foo.png" }
{ "_id" : ObjectId("544540c91b5cf91c4893eb97"), "imageUrl" : "http://example.com/bar.png" }
{ "_id" : ObjectId("544540ed1b5cf91c4893eb98"), "otherField" : 1 }
{ "_id" : ObjectId("544540f11b5cf91c4893eb99"), "otherField" : 2 }
Теперь создайте разреженный индекс для поля imageUrl:
db.foo.ensureIndex( { "imageUrl": 1 }, { sparse: true } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Теперь всегда есть шанс (и, в частности, с небольшим набором данных, таким как мой образец), вместо того, чтобы использовать индекс, MongoDB будет использовать сканирование таблицы даже для запроса потенциального покрытого индекса. Как оказалось, это дает мне простой способ проиллюстрировать разницу здесь:
db.foo.find({}, {_id : 0, imageUrl : 1})
{ "imageUrl" : "http://example.com/foo.jpg" }
{ "imageUrl" : "http://example.com/bar.jpg" }
{ "imageUrl" : "http://example.com/foo.png" }
{ "imageUrl" : "http://example.com/bar.png" }
{ }
{ }
ОК, поэтому дополнительные документы без imageUrl
возвращаются, просто пустые, а не то, что мы хотели. Просто чтобы подтвердить почему, сделайте объяснение:
db.foo.find({}, {_id : 0, imageUrl : 1}).explain()
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 6,
"nscannedObjects" : 6,
"nscanned" : 6,
"nscannedObjectsAllPlans" : 6,
"nscannedAllPlans" : 6,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"server" : "localhost:31100",
"filterSet" : false
}
Итак, да, BasicCursor
равно сканированию таблицы, индекс не использовался. Давайте заставим запрос использовать наш разреженный индекс с hint()
:
db.foo.find({}, {_id : 0, imageUrl : 1}).hint({imageUrl : 1})
{ "imageUrl" : "http://example.com/bar.jpg" }
{ "imageUrl" : "http://example.com/bar.png" }
{ "imageUrl" : "http://example.com/foo.jpg" }
{ "imageUrl" : "http://example.com/foo.png" }
И вот результат, который мы искали - возвращаются только документы с заполненным полем. При этом также используется только индекс (т. Е. Это закрытый запрос индекса), поэтому для возврата результатов в память должен быть только индекс.
Это специализированный вариант использования, и его нельзя использовать вообще (см. Другие ответы для этих вариантов). В частности, следует отметить, что, поскольку вещи стоят, вы не можете использовать count()
таким образом (для моего примера это вернет 6, а не 4), поэтому, пожалуйста, используйте только когда это уместно.