Как перебирать именованные ключи и получать результаты - PullRequest
0 голосов
/ 19 ноября 2018

Я работаю над запросом Nested Json в dong монго, примерная структура данных показана ниже:

{
    "_id" : ObjectId("5bf159cc6bf6ab0ac374f80c"),
    "name" : "Jack",
    "age" : "30",
    "info" : {
        "0" : {
            "status" : "true",
            "name" : "luffy"
        },
        "1" : {
            "status" : "true",
            "name" : "sanji"
        },
        "2" : {
            "status" : "false",
            "name" : "zoro"
        }
    }
}

/* 2 */
{
    "_id" : ObjectId("5bf15f286bf6ab0ac374f8ed"),
    "name" : "Mack",
    "age" : "33",
    "info" : {
        "0" : {
            "status" : "true",
            "name" : "naruto"
        },
        "1" : {
            "status" : "true",
            "name" : "sakura"
        },
        "2" : {
            "status" : "false",
            "name" : "sasuke"

Теперь, что я хотел сделать, это запросить и получить те результаты, где status = 'true'. После некоторого поиска в Google я узнал, как запросить вложенный документ, и придумал образец.

query:db.getCollection('test').find({"info.0.status":"true"})

Но, как вы знаете из приведенного выше запроса, запрос будет извлекать только соответствующие результаты из массива '0th'. Как получить запрос для итерации по массивам и возврата документов с "status": "true". Также я новичок в Mongodb, пожалуйста, игнорируйте любые ошибки.

Примечание : Один из пользователей сказал мне, что я должен переделать мою структуру данных, как показано ниже, а затем использовать оператор $ filter:

[
  {
    "_id": ObjectId("5bf159cc6bf6ab0ac374f80c"),
    "name": "Jack",
    "age": "30",
    "info": [
      {
        "status": "true",
        "name": "luffy"
      },
      {
        "status": "true",
        "name": "sanji"
      },
      {
        "status": "false",
        "name": "zoro"
      }
    ]
  },
  {
    "_id": ObjectId("5bf15f286bf6ab0ac374f8ed"),
    "name": "Mack",
    "age": "33",
    "info": [
      {
        "status": "true",
        "name": "naruto"
      },
      {
        "status": "true",
        "name": "sakura"
      },
      {
        "status": "false",
        "name": "sasuke"
      }
    ]
  }
]

Однако я не понимаю, как изменить свою структуру так, как показывал пользователь. Есть ли какая-либо другая тактика, которую я мог бы использовать?

1 Ответ

0 голосов
/ 19 ноября 2018

Суть в общем вопросе - НЕ пытаться вообще "запросить" в этой форме, а просто принять совет и переписать данные.Но существуют разные подходы к записи.

Переписать коллекцию

Как вам уже сказали, лучше всего переделать коллекцию, чтобы это были настоящие "массивы", а не "объекты" сименованные ключи.

Общие случаи на самом деле либо «повторяют» элементы коллекции и переписывают их:

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" для ускорения реального мира.Результаты запроса.


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

...