#mongodb #mongodb-query
#mongodb #mongodb-запрос
Вопрос:
У меня есть коллекция в MongoDB, скажем, STUDENT с атрибутами id, name, standard, marks, average. Теперь я хочу написать запрос, чтобы получить только те документы, в которых по крайней мере 3 атрибута содержат ненулевые значения.
Должны быть напечатаны все те документы, которые содержат ненулевое значение в (имя, стандарт, отметки) или (имя, отметки, среднее) или (имя, стандарт, отметки, среднее) или (идентификатор, имя, стандарт, отметки, среднее). Но если какой-либо документ содержит только (name, standard) как ненулевой или (standard, marks), его следует игнорировать.
Комментарии:
1. Для этого нужен гораздо более четкий пример в вашем вопросе. Включите несколько примеров документов, которые соответствуют вашим условиям и не соответствуют вашему ожидаемому результату.
2. Теперь это более понятно?
3. Вы не можете сделать это без какой-либо физической логики, используя либо платформу агрегации, либо какой-либо другой метод в настоящее время. MongoDB не может сообщить в результатах запроса, сколько столбцов совпадают. Я не думаю, что разреженные индексы будут работать и здесь
4. Реальные примеры документов сделают его более понятным для более широкой аудитории. Помните, что не все видят то, что видите вы. Ваша задача — четко представить свое дело миру, который не работает за вашим столом.
5. Ну, при этом 4 оператора $ or будут работать
Ответ №1:
Я бы сказал, что, учитывая «студенческие» документы, подобные этому:
{ name: "a", standard: "b", marks: 10 },
{ name: "b", marks: 5, average: 2 },
{ id: 2, name: "c", marks: 10, average: 7 },
{ name: "c", standard: "b" },
{ standard: "c", marks: 3 }
Тогда «в идеале» вы должны сделать что-то вроде этого:
db.students.find({
"$or": [
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
],
},
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
},
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
},
{
"$and": [
{ "id": { "$exists": true } },
{ "id": { "$ne": null } },
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
}
]
})
Что исключает эти последние два документа.
Также в современных версиях MongoDB 2.6 и выше вы получаете пересечение индексов или версию такого в версиях 2.4 с учетом $or
операнда. Таким образом, вы можете индексировать следующим образом:
db.student.ensureIndex({ "name": 1, "standard": 1, "marks": 1 })
db.student.ensureIndex({ "name": 1, "marks": 1, "average": 1 })
db.student.ensureIndex({ "name": 1, "marks": 1, "standard": 1, "average": 1 })
db.student.ensureIndex({ "id": 1, "name": 1, "marks": 1, "standard": 1, "average": 1 })
Это может привести к значительному использованию «индексного» пространства, поэтому в этом случае средства могут перевесить цели.
Конечно, для более гибкого подхода к определению этого (хотя и не так быстро) вы можете обратиться к структуре агрегации:
db.students.aggregate([
{ "$project": {
"id": { "$ifNull": [ "$id", null ] },
"name": { "$ifNull": [ "$name", null ] },
"marks": { "$ifNull": [ "$marks", null ] },
"standard": { "$ifNull": [ "$standard", null ] },
"average": { "$ifNull": [ "$average", null ] },
"fields": {
"$add": [
{ "$cond": [ { "$ifNull": [ "$id", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$name", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$marks", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$standard", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$average", null ] }, 1, 0 ] },
]
}
}},
{ "$match": { "fields": { "$gte": 3 } } }
])
Что, по сути, является более «буквальной» интерпретацией вашего вопроса, если оно ограничено ограничением структуры агрегации, заключающимся в фактической необходимости объявлять все те «поля», которые возможны.
$ifNull
Оператор выполняет «тяжелую работу», заменяя «несуществующие» null
поля or null
значением для оценки. Мы также надеемся, что вы могли бы «попробовать» фильтровать с помощью a $match
на начальном этапе конвейера, как это было сделано в первом запросе, чтобы уменьшить ввод.
Последняя реальная проблема возникает, если у вас просто ** слишком много * различных комбинаций полей для указания в любой форме, и вам просто нужно знать, что «три» или более ваших полей по существу существуют или не являются нулевыми.
Этот подход сводится к использованию $where
form of evaluation, который является наименее эффективным способом обработки общего запроса, но он наиболее гибкий, поскольку код JavaScript может обрабатывать такие ситуации:
db.students.find(
function() {
var count = 0;
for ( var k in this ) {
if ( ( k != null) amp;amp; ( k != "_id") ) {
count ;
if ( count >= 3 )
break;
}
}
return ( count >= 3 );
}
)
Итак, хотя последние формы «выглядят» просто, на самом деле это довольно ужасно, поскольку нет способа избежать того, что по сути заканчивается как «полное сканирование коллекции», поскольку все поля в каждом документе оцениваются на предмет условий в JavaScript. Ну, по крайней мере, до тех пор, пока не будет достигнуто число «три».
Это дает вам несколько подходов. Надеюсь, первое действительно подходит.