Почему MongoDB не поддерживает запросы свойств встроенных документов, которые хранятся в хеш-массивах?

Почему MongoDB не поддерживает запросы свойств встроенных документов, которые хранятся с использованием хешей?

Например, скажем, у вас есть коллекция под названием "счета-фактуры", которая была создана следующим образом:

db.invoices.insert(
    [
        {
            productsBySku: {
                12432: {
                    price: 49.99,
                    qty_in_stock: 4
                },
                54352: {
                    price: 29.99,
                    qty_in_stock: 5
                }
            }
        },
        {
            productsBySku: {
                42432: {
                    price: 69.99,
                    qty_in_stock: 0
                },
                53352: {
                    price: 19.99,
                    qty_in_stock: 5
                }
            }
        }
    ]
);

При такой структуре запросы MongoDB с $elemMatch, точечным синтаксисом или позиционным оператором ($) не могут получить доступ ни к одному из свойств каждого члена productsBySku.

Например, вы не можете сделать что-либо из этого:

db.invoices.find({"productsBySku.qty_in_stock":0});

db.invoices.find({"productsBySku.$.qty_in_stock":0});

db.invoices.find({"productsBySku.qty_in_stock":{$elemMatch:{$eq:0}}});

db.invoices.find({"productsBySku.$.qty_in_stock":{$elemMatch:{$eq:0}}});

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

db.invoices.find({
    $where: function () {
        for (var i in this.productsBySku)
            if (!this.productsBySku[i].qty_in_stock)
                return this;
    }
});

На техническом уровне... почему они разработали MongoDB с таким жестким ограничением на запросы? Конечно, должна быть какая-то техническая причина для этого кажущегося серьезного недостатка. Является ли эта неспособность работать со списком объектов в виде массива, игнорируя ключи, просто ограничение JavaScript как языка? Или это было результатом какого-то архитектурного решения в MongoDB?

Просто любопытно.

2 ответа

Решение

Если я честен, я не уверен, вы должны спросить у MongoDB Inc. (10gen) сами. Я попытаюсь объяснить некоторые из моих рассуждений.

Я немного искал в Google и, похоже, ничего не появляется: https://www.google.co.uk/search?q=mognodb+jira+support+querying+objects&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla: ан-ГБ: официальный и клиент = светлячок-а & канал = fflb & gfe_rd = CR & е =as9pVOW3OMyq8wfhtYCgCw# RLS = org.mozilla: ан-ГБ: официальный канал = & fflb & д = MongoDB + JIRA + + запрашивающие объекты

Быстро можно увидеть, как использование объектных свойств для ключей может быть выгодным, например: запросы на удаление не должны были бы искать каждый объект и его свойства в массиве, а вместо этого просто находили единственное свойство объекта в родительском объекте и сбрасывали его. По сути это будет разница:

[
    {id:1, d:3, e:54},
    {id:3, t:6, b:56}
]

а также:

{
    1: [d:3, e: 54],
    3: [t:6, b:56]
}

с последним, очевидно, гораздо быстрее удалить идентификатор 3,

Не только это, но и все операции с массивами, которые вводит MongoDB, из $elemMatch в $unwind будет работать с объектами, я имею в виду, как раскручивать:

[
    {id:5, d:4}
]

сильно отличается от раскручивания:

{
    5: {d:4}
}

?

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

На самом деле я зашел так далеко, что нашел несколько раз, в том числе: https://www.google.co.uk/search?q=array+operations+that+do+not+work+on+objects&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-GB:official&client=firefox-a&channel=fflb&gfe_rd=cr&ei=DtNpVLrwDsPo7AaH4oCoDw и я нашел результаты, которые зашли так далеко underscore.js которые фактически выполняют свои функции массива для всех объектов.

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

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

Практическое правило. Обычно это не технические проблемы, а проблемы с моделированием данных. Я еще не нашел вариант использования, в котором имеет смысл иметь ключи, содержащие семантическое значение.

Если бы у вас было что-то вроде

'products':[
     {sku:12432,price:49.99,qty_in_stock:4},
     {sku:54352,price:29.99,qty_in_stock:5}
 ]

Было бы намного больше смысла.

Но: вы моделируете счета. Счет должен - по многим причинам - отражать статус в определенный момент времени. Постоянно меняющийся запас редко относится к счету. Итак, вот как я бы смоделировал данные для товаров и счетов

{
  '_id':'12432',
  'name':'SuperFoo',
  'description':'Without SuperFoo, you can't bar or baz!',
  'current_price':49.99
 }

То же самое с другими предметами.

Теперь счет будет выглядеть довольно просто:

{ _id:"Invoice2",
  customerId:"987654"
  date:ISODate("2014-07-07T12:42:00Z"),
  delivery_address:"Foo Blvd 42, Appt 42, 424242 Bar, BAZ"
  items:
    [{id:'12432', qty: 2, price: 49.99},
     {id:'54352', qty: 1, price: 29.99}
    ]
}

Теперь в счете будут храниться вещи, которые могут быть действительными только в определенный момент времени (цены и адрес доставки могут измениться), и ваш запас и счета будут запрошены легко:

// How many items of 12432 are in stock?
db.products.find({_id:'12432'},{qty_in_stock:1})

// How many items of 12432 were sold during July and what was the average price?
db.invoices.aggregate([
  {$unwind:"$items"},
  {
    $match:{
      "items.id":"12432",
      "date":{
         $gt:ISODate("2014-07-01T00:00:00Z"),
         $lt:ISODate("2014-08-01T00:00:00Z")
      }
     }
   },
   {$group : { _id:"$items.id", count: { $sum:"$items.qty" }, avg:{$avg:"$items.price"} } }
])

// How many items of each product sold did I sell yesterday?
db.invoices.aggregate([
  {$match:{ date:{$gte:ISODate("2014-11-16T00:00:00Z"),$lt:ISODate("2014-11-17T00:00:00Z")}}},
  {$unwind:"$items"},
  {$group: { _id:"$items.id",count:{$sum:"$qty"}}}
])

В сочетании с запросом о количестве товаров каждого товара на складе вы можете узнать, нужно ли вам что-то заказывать (вы должны выполнить этот расчет в своем коде, в MongoDB нет простого способа сделать это).

Видите ли, с "небольшим" изменением вы получите ответы на множество вопросов.

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

Как я могу получить свои ответы из этих данных?

В NoSQL вообще и особенно с MongoDB вы сначала спросите

На какие вопросы мне нужно получить ответ?

и смоделируйте свои данные соответственно. Тонкое, но важное отличие.

Другие вопросы по тегам