Запрос для документов, размер массива которых превышает 1

У меня есть коллекция MongoDB с документами в следующем формате:

{
  "_id" : ObjectId("4e8ae86d08101908e1000001"),
  "name" : ["Name"],
  "zipcode" : ["2223"]
}
{
  "_id" : ObjectId("4e8ae86d08101908e1000002"),
  "name" : ["Another ", "Name"],
  "zipcode" : ["2224"]
}

В настоящее время я могу получить документы, соответствующие определенному размеру массива:

db.accommodations.find({ name : { $size : 2 }})

Это правильно возвращает документы с 2 элементами в массиве name. Однако я не могу сделать команду $gt для возврата всех документов, где поле name имеет размер массива больше 2:

db.accommodations.find({ name : { $size: { $gt : 1 } }})

Как я могу выбрать все документы с массивом name размером более одного (желательно без изменения текущей структуры данных)?

Ответ 1

Update:

Для версий mongodb 2.2 + более эффективный способ сделать это, описанный @JohnnyHK в другом .


1.Использование $где

db.accommodations.find( { $where: "this.name.length > 1" } );

Но...

Javascript выполняется медленнее, чем исходные операторы, перечисленные в эта страница, но очень гибкая. См. Страницу обработки на стороне сервера для получения дополнительной информации.

2.Создайте дополнительное поле NamesArrayLength, обновите его длиной массива имен и затем используйте в запросах:

db.accommodations.find({"NamesArrayLength": {$gt: 1} });

Это будет лучшее решение и будет работать намного быстрее (вы можете создать на нем индекс).

Ответ 2

Есть более эффективный способ сделать это в MongoDB 2. 2+ теперь, когда вы можете использовать числовые индексы массива в ключах объекта запроса.

// Find all docs that have at least two name array elements.
db.accommodations.find({'name.1': {$exists: true}})

Вы можете поддержать этот запрос с помощью индекса, который использует выражение частичного фильтра (требуется 3. 2+):

// index for at least two name array elements
db.accommodations.createIndex(
    {'name.1': 1},
    {partialFilterExpression: {'name.1': {$exists: true}}}
);

Ответ 3

Я считаю, что это самый быстрый запрос, который отвечает на ваш вопрос, потому что он не использует интерпретированное предложение $where:

{$nor: [
    {name: {$exists: false}},
    {name: {$size: 0}},
    {name: {$size: 1}}
]}

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

Тест:

> db.test.save({})
> db.test.save({name: []})
> db.test.save({name: ['George']})
> db.test.save({name: ['George', 'Raymond']})
> db.test.save({name: ['George', 'Raymond', 'Richard']})
> db.test.save({name: ['George', 'Raymond', 'Richard', 'Martin']})
> db.test.find({$nor: [{name: {$exists: false}}, {name: {$size: 0}}, {name: {$size: 1}}]})
{ "_id" : ObjectId("511907e3fb13145a3d2e225b"), "name" : [ "George", "Raymond" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225c"), "name" : [ "George", "Raymond", "Richard" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225d"), "name" : [ "George", "Raymond", "Richard", "Martin" ] }
>

Ответ 4

Вы также можете использовать агрегат:

db.accommodations.aggregate(
[
     {$project: {_id:1, name:1, zipcode:1, 
                 size_of_name: {$size: "$name"}
                }
     },
     {$match: {"size_of_name": {$gt: 1}}}
])

//вы добавляете "size_of_name" для переноса документа и используете его для фильтрации размера имени

Ответ 5

Ничто из этого не помогло мне. Я сделал это:

db.collection.find( {arrayName : {$exists:true}, $where:'this.arrayName.length>1'} )

Ответ 6

Попробуйте сделать что-то вроде этого:

db.getCollection('collectionName').find({'ArrayName.1': {$exists: true}})

1 - это номер, если вы хотите получить запись больше 50, то ArrayName.50 Спасибо.

Ответ 7

Вы можете просто найти, существует ли второй элемент.

db.accommodations.find({'name.1': {$exists: true}});

Ответ 8

db.accommodations.find({"name":{"$exists":true, "$ne":[], "$not":{"$size":1}}})

Ответ 9

Вы можете использовать $ expr (оператор версии 3.6 mongo) для использования функций агрегации в регулярном запросе.

Сравните query operators сравнения aggregation comparison operators.

db.accommodations.find({$expr:{$gt:[{$size:"$name"}, 1]}})

Ответ 10

Я нашел это решение, чтобы найти элементы с полем массива больше определенной длины

db.allusers.aggregate([
  {$match:{username:{$exists:true}}},
  {$project: { count: { $size:"$locations.lat" }}},
  {$match:{count:{$gt:20}}}
])

В первом агрегате $match используется аргумент true для всех документов. Если пусто, я бы получил

"errmsg" : "exception: The argument to $size must be an Array, but was of type: EOO"

Ответ 11

MongoDB 3.6 включает $ expr https://docs.mongodb.com/manual/reference/operator/query/expr/

Вы можете использовать $ expr для оценки выражения внутри $ match или найти.

{ $match: {
           $expr: {$gt: [{$size: "$yourArrayField"}, 0]}
         }
}

или найти

collection.find({$expr: {$gte: [{$size: "$yourArrayField"}, 0]}});

Ответ 12

Ваш код:

db.accommodations.find({ name : { $size: { $gt : 1 } }})

Не работает, потому что это означает: найти имя, где больше, чем один размер

Вместо этого вы должны были написать:

db.accommodations.find({ name : { $gt: { $size : 1 } }})

Что значит: найти имя, где размер больше единицы

Ответ 13

Несмотря на то, что выше сказанное все работает, то, что вы изначально пытались сделать, было правильным способом, однако вы просто имеете синтаксис назад (переключатель "$ size" и "$ gt" ).

Правильно:

db.collection.find({items: {$gt: {$size: 1}}})

Неправильно:

db.collection.find({items: {$size: {$gt: 1}}})