Поиск значений с помощью индекса в mongodb - PullRequest
0 голосов
/ 14 ноября 2018

Я новичок в Mongodb и хочу реализовать поиск по полю в коллекции монго.

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

{
  'key': <unique key>,
  'val_arr': [
               ['laptop', 'macbook pro', '16gb', 'i9', 'spacegrey'],
               ['cellphone', 'iPhone', '4gb', 't2', 'rose gold'],
               ['laptop', 'macbook air', '8gb', 'i5', 'black'],
               ['router', 'huawei', '10x10', 'white'],
               ['laptop', 'macbook', '8gb', 'i5', 'silve'],
}

И я хочу найти их по номеру и значению индекса, т.е. Найдите запись, в которой первый элемент любого из val_arr равен laptop, а значение третьего элемента равно 8gb.

Я попытался просмотреть составные индексы в mongodb, но у них есть ограничение в 32 ключа для индексации. Любая помощь в этом направлении приветствуется.

1 Ответ

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

Существует ограничение на индексы здесь , но это действительно не должно иметь значения.В вашем случае вы на самом деле говорите 'key': <unique key>.Так что, если это действительно «уникальный», то это единственное, что в коллекции нужно индексировать , при условии, что вы действительно включаете этот "key" в каждый запрос, который вы делаете, так как это будет определять васвыберите документ.

Индексы для массивов «внутри» документа на самом деле не имеют большого значения, если только вы не собираетесь непосредственно искать эти элементы в документе.Это может иметь место, но на самом деле это не имеет никакого отношения к сопоставлению ваших значений по пронумерованным индексным позициям:

db.collection.find(
  {
    "val_arr": {
      "$elemMatch": { "0": "laptop", "2": "8gb" }
    }
  },
  {  "val_arr.$": 1 }
)

Что будет возвращать:

{
    "val_arr" : [
        [
            "laptop",
            "macbook air",
            "8gb",
            "i5",
            "black"
        ]
    ]
}

$elemMatch позволяет выразить «несколько условий» для одного элемента массива.Это необходимо для стандартных форм записи точек, потому что в противном случае условие просто ищет "любой" элемент массива, который соответствует значению в индексе.Например:

db.collection.find({ "val_arr.0": "laptop", "val_arr.2": "4gb" })

Фактически соответствует данному документу, даже если эта «комбинация» не существует в одной «строке», но оба значения фактически присутствуют в массиве в целом.Но только у разных участников.Используя те же значения с $elemMatch, убедитесь, что пара соответствует одному и тому же элементу.

Обратите внимание на { "val_arr.$": 1 } в вышеприведенном примере, который является проекцией для "одиночного"согласованный элемент.Это необязательно, но речь идет только об идентификации совпадений.

Использование .find() - это столько, сколько вы можете сделать, и ограничение позиционного оператора в том смысле, что он может идентифицировать только один совпадающий элемент,Способ сделать это для «нескольких совпадений» - использовать aggregate() с $filter:

db.collection.aggregate([
  { "$match": {
    "val_arr": {
      "$elemMatch": { "0": "laptop", "2": "8gb" }
    }
  }},
  { "$addFields": {
    "val_arr": {
      "$filter": {
        "input": "$val_arr",
        "cond": {
          "$and": [
            { "$eq": [ { "$arrayElemAt": [ "$$this", 0 ] }, "laptop" ] },
            { "$eq": [ { "$arrayElemAt": [ "$$this", 2 ] }, "8gb" ] }
          ]
        }
      }
    }
  }}
])

, который возвращает:

{
        "key" : "k",
        "val_arr" : [
                [
                        "laptop",
                        "macbook air",
                        "8gb",
                        "i5",
                        "black"
                ],
                [
                        "laptop",
                        "macbook",
                        "8gb",
                        "i5",
                        "silve"
                ]
        ]
}

Исходное значениеусловия запроса, которые фактически выбирают соответствующий документ, входят в $match и в точности совпадают с условиями запроса, показанными ранее.$filter применяется только для получения элементов, которые действительно соответствуют его условиям.Эти условия аналогично используют $arrayElemAt внутри логического выражения в отношении того, как значения индекса "0" и "2" применяются в самих условиях запроса.

Использование любогоВыражение агрегации требует дополнительных затрат по сравнению со стандартными возможностями механизма запросов.Поэтому всегда лучше подумать, действительно ли вам нужно , нужно , перед погружением и использовать это утверждение.Регулярные выражения запросов всегда лучше, если они выполняют свою работу.

Изменение структуры

Конечно, хотя можно сопоставить позиции индекса в массиве, ничто из этого на самом деле не помогает в возможностина самом деле создать «индекс», который можно использовать для ускорения запросов.

Лучший способ здесь - это использовать значимые имена свойств вместо простых массивов:

{
  'key': "k",
  'val_arr': [
    { 
      'type': 'laptop',
      'name': 'macbook pro',
      'memory': '16gb',
      'processor': 'i9',
      'color': 'spacegrey'
    },
    {
      'type': 'cellphone',
      'name': 'iPhone',
      'memory': '4gb',
      'processor': 't2',
      'color': 'rose gold'
    },
    {
      'type': 'laptop',
      'name': 'macbook air',
      'memory': '8gb',
      'processor': 'i5',
      'color': 'black'
    },
    { 
      'type':'router',
      'name': 'huawei',
      'size': '10x10',
      'color': 'white'
    },
    { 
      'type': 'laptop',
      'name': 'macbook',
      'memory': '8gb',
      'processor': 'i5',
      'color': 'silve'
    }
  ]
}

Это позволяетВы «в пределах разумного» включаете пути к именам свойств в массиве как часть составного индекса.Например:

db.collection.createIndex({ "val_arr.type": 1, "val_arr.memory": 1 })

И тогда фактическая выдача запросов выглядит гораздо более информативно в коде, чем загадочные значения 0 и 2:

db.collection.aggregate([
  { "$match": {
    "val_arr": {
      "$elemMatch": { "type": "laptop", "memory": "8gb" }
    }
  }},
  { "$addFields": {
    "val_arr": {
      "$filter": {
        "input": "$val_arr",
        "cond": {
          "$and": [
            { "$eq": [ "$$this.type", "laptop" ] },
            { "$eq": [ "$$this.memory", "8gb" ] }
          ]
        }
      }
    }
  }}
])

Ожидаемые результаты и многое другоезначимое:

{
        "key" : "k",
        "val_arr" : [
                {
                        "type" : "laptop",
                        "name" : "macbook air",
                        "memory" : "8gb",
                        "processor" : "i5",
                        "color" : "black"
                },
                {
                        "type" : "laptop",
                        "name" : "macbook",
                        "memory" : "8gb",
                        "processor" : "i5",
                        "color" : "silve"
                }
        ]
}

Общая причина, по которой большинство людей приходят к такой структуре, как вы, в этом вопросе, обычно заключается в том, что они думают, что экономят пространство.Это не просто не соответствует действительности , и с большинством современных оптимизаций для механизмов хранения MongoDB использует его, в основном, не имеет значения по сравнению с любыми небольшими выигрышами, которые можно было бы ожидать.

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

И снова, если весь ваш шаблон использования этих данных не использует свойство key документа в запросах, то, вероятно, было бы лучше сохранить эти записи как отдельные документы, а не в массиве. совсем. Это также делает получение результатов более эффективным.

Итак, чтобы разобраться, все ваши варианты здесь:

  • Вы на самом деле всегда включаете key в свой запрос, поэтому индексы в любом другом месте, кроме этого свойства, не имеют значения.
  • Вы переходите на использование именованных свойств для значений в элементах массива, что позволяет индексировать эти свойства без нажатия «Ограничения для нескольких клавиш»
  • Вы решаете, что никогда не обращаетесь к этим данным с помощью key, так что вы просто записываете все данные массива как отдельные документы в коллекции с правильными именованными свойствами.

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

NB В действительности не имеет ничего общего с обсуждаемой темой (за исключением, возможно, примечания по объему хранилища), но, как правило, рекомендуется использовать вещи с внутренним числовым значением, таким как memory или "8gb" типы данных на самом деле выражаются как числовые, а не как «строки».

Простое объяснение состоит в том, что хотя вы можете запрашивать "8gb" как равенство, это не поможет вам с диапазонами, такими как "от 4 до 12 гигабайт.

Поэтому обычно имеет больше смысла использовать числовые значения, такие как 8 или даже 8000. Обратите внимание, что числовые значения на самом деле будут влиять на хранилище, поскольку они обычно занимают меньше места, чем строки. Что, учитывая, что отсутствие имен свойств, возможно, пыталось уменьшить объем хранилища, но ничего не делает, показывает фактическую область, где также может быть уменьшен размер хранилища.

...