Суть в общем вопросе - НЕ пытаться вообще "запросить" в этой форме, а просто принять совет и переписать данные.Но существуют разные подходы к записи.
Переписать коллекцию
Как вам уже сказали, лучше всего переделать коллекцию, чтобы это были настоящие "массивы", а не "объекты" сименованные ключи.
Общие случаи на самом деле либо «повторяют» элементы коллекции и переписывают их:
var updates = [];
db.getCollection('test').find().forEach(doc => {
var info = Object.keys(doc.info).map(k =>
Object.assign({}, doc.info[k], { status: doc.info[k].status === "true" }) );
updates.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "doc.info": info } }
}
});
if (updates.length >= 1000) {
db.getCollection('test').bulkWrite(updates);
updates = [];
}
});
if (updates.length >= 0) {
db.getCollection('test').bulkWrite(updates);
updates = [];
}
Или пишут совершенно новую коллекцию:
db.getCollection('test').aggregate([
{ "$addFields": {
"info": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": {
"$mergeObjects": [
"$$this.v",
{ "status": { "$toBool": "$$this.v.status" }
]
}
}
}
}},
{ "$out": "newtest" }
])
Основная надежда заключается в том, что вы можете терпеть «новую коллекцию» и действительно иметь такие функции, как $objectToArray
, доступные для вашей версии MongoDB.
Даже при «обновлении» обычно рекомендуется(особенно для производства), который вы вместо этого используете из Как обновить несколько элементов массива в mongodb , поскольку использование $set
для замены всего свойства - это "грубая сила", а небезопасен для производства.Хорошо, но только на вашей собственной системе для тестирования.
После того, как любая из этих форм заполнена, вы можете в основном $filter
только для совпадений, которые true
, как в:
collection.aggregate([
// Finds valid "documents"
{ "$match": { "info.status": true } },
// "filters" the array content
{ "$addFields": {
"info": {
"$filter": { "input": "$info", "cond": "$$this.status" }
}
}}
])
Query In Place
Конечно, вы "можете" на самом деле запрашивать существующую структуру документа, но просто не рекомендуется :
collection.aggregate([
// Match on transformed object
{ "$match": {
"$expr": {
"$gt": [
{ "$size": {
"$filter": {
"input": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": "$$this.v"
}
},
"cond": { "$toBool": "$$this.status" }
}
}},
0
]
}
}},
// Transform remaining objects
{ "$addFields": {
"info": {
"$filter": {
"input": {
"$map": {
"input": { "$objectToArray": "$info" },
"in": "$$this.v"
}
},
"cond": { "$toBool": "$$this.status" }
}
}
}}
])
Или даже с выражением JavaScript в $where
, конечно, без поддержки фактической «фильтрации» содержимого результатов перед извлечением с сервера:
collection.find({
"$where": function() {
return Object.keys(this.info).map( k => this.info[k])
.some(e => e.status === "true")
}
})
Единственное, что изменяет документы с помощью JavaScript на сервере, это mapReduce
, конечно, с собственным форматом:
collection.mapReduce(
function() {
var id = this._id;
delete this._id;
this.info = Object.keys(this.info)
.map(k => this.info[k])
.filter(o => o.status === "true")
emit(id,this);
},
function() {},
{
"out": { "inline": 1 },
"query": {
"$where": function() {
return Object.keys(this.info).map( k => this.info[k])
.some(e => e.status === "true")
}
}
}
)
В любом случае это действительно «ужасно» , поскольку они в основном полагаются на преобразование каждого документа в фактическую форму «массива» до условий, которые могут быть сопоставлены.Переписывание коллекции, с другой стороны, фактически позволяет выполнить эту работу заранее, удаляя таким образом вычисление , а также позволяя указать "index" для ускорения реального мира.Результаты запроса.
Короче говоря, Перепишите его , и не "запрашивайте" его, как оно есть в настоящее время, поскольку базы данных не оптимизированы для "именованных ключей" при запросахчерез документы.