0

为什么 MongoDB 不支持查询使用哈希存储的嵌入式文档的属性?

例如,假设您有一个名为“invoices”的集合,它是这样创建的:

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
                }
            }
        }
    ]
);

使用这种结构,使用 $elemMatch、点语法或位置运算符 ($) 的 MongoDB 查询无法访问每个 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 内部某些架构决策的结果?

只是好奇。

4

2 回答 2

3

根据经验:通常,这些问题不是技术问题,而是数据建模问题。我还没有找到让键保持语义值有意义的用例。

如果你有类似的东西

'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 中,你首先要问

我需要回答哪些问题?

并相应地为您的数据建模。一个微妙但重要的区别。

于 2014-11-17T10:08:47.120 回答
1

老实说,我不确定,您必须自己询问 MongoDB Inc. (10gen)。我将尝试解释我的一些推理。

我在谷歌上搜索了一下,似乎什么也没有出现:https ://www.google.co.uk/search?q=mognodb+jira+support+querying+objects&ie=utf-8&oe=utf-8&aq=t&rls=org .mozilla:en-GB:official&client=firefox-a&channel=fflb&gfe_rd=cr&ei=as9pVOW3OMyq8wfhtYCgCw#rls=org.mozilla:en-GB:official&channel=fflb&q=mongodb+jira+querying+objects

很快就可以看出使用对象属性作为键是如何有利的,例如:删除查询不必搜索数组中的每个对象及其属性,而只需在父对象中找到单个对象属性并取消设置它。从本质上讲,这将是以下区别:

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

和:

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

显然,后者删除3.

不仅如此,MongoDB 引入的所有数组操作,from $elemMatchto$unwind也适用于对象,我的意思是如何展开:

[
    {id:5, d:4}
]

与展开有很大不同:

{
    5: {d:4}
}

?

所以,老实说,我无法回答你的问题。谷歌对他们的决定没有任何辩护,我能找到的也没有广泛的讨论。

事实上,我已经搜索了几次,包括: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 的观点之一,即使现在也让我感到困惑,因为在我自己的编程中,有很多次在速度和能力上似乎有利于实际使用对象而不是数组。

于 2014-11-17T10:52:32.617 回答