1

我有两个系列——购物者(某一天在店里的每个人)和海滩游客(某一天在海滩上的每个人)。每天都有条目,人们可以在海滩上,或者购物,或者两者都做,或者任何一天都不做。我现在想查询 - 过去 7 天内没有去海滩的所有购物者。

我是 Mongo 的新手,所以我的模式设计可能不适合 nosql 数据库。我看到了关于 join 的类似问题,并且在大多数情况下,建议进行非规范化。因此,我能想到的一种解决方案是创建集合 - 活动、日期索引、嵌入用户的操作。所以像

{
   user_id
   date
   actions {
      [action_type, ..]
   }
}

现在插入变得昂贵,因为现在我必须在插入之前进行查询。

4

3 回答 3

3

几点建议。

找出您将要运行的所有查询,以及您需要存储的所有数据类型。例如,您是否希望在未来增加活动,或者海滩和购物会是全部吗?

考虑您将拥有多少写入与读取,哪些必须更快。

确定您的文档将如何随着时间的推移而增长,以确保您的架构在长期内具有可扩展性。

这是一种可能的方法,如果您永远只有这两个活动。每个用户每天一条记录。

{ user: "user1",
  date: "2012-12-01",
  shopped: 0,
  beached: 1
}

现在,无论您有两个还是十个活动,您的查询都变得更加简单。

当有新活动出现时,您总是必须根据它更新正确的记录。如果您认为您可以将一条记录附加到您的集合中,指示用户、日期、活动,那么您的插入会快得多,但是您的查询现在必须对用户、日期活动进行大量的查询工作。

使用建议的模式,这是插入/更新语句:

db.coll.update({"user":"username", "date": "somedate"}, {"shopped":{$inc:1}}, true)

这就是说:“对于某个日期的用户名,将他们的购物属性增加 1,并在它不存在时创建它,即“upsert”(这是最后一个“真”参数)。

这是针对在特定日期多次执行活动 1 但未执行任何活动 2 的所有用户的查询。

db.coll.find({"date":"somedate","shopped":0,"danced":{$gt:1}})

小心选择单个文档可以具有连续且无限增长的模式。

例如,将所有内容存储在日期和活动数组不断增长的用户集合中就会遇到这个问题。请参阅此处突出显示的部分以对此进行解释 - 请记住,大型文档将不断进入您的工作数据集中,如果它们很大并且其中包含大量无用(旧)数据,那将损害您的性能应用程序,以及磁盘上的数据碎片。

请记住,您不必将所有数据放入一个集合中。最好有一个用户集合,其中包含该用户的一组固定属性,您可以在其中跟踪他们有多少朋友或关于他们的其他半稳定信息,并且还有一个 user_activity 集合,您可以在其中添加每个用户每天的记录什么他们所做的活动。数据的数量或规范化或非规范化与您将在其上运行的查询类型密切相关,这就是为什么弄清楚这些是我提出的第一个建议的原因。

于 2012-06-03T19:18:56.137 回答
0

现在插入变得昂贵,因为现在我必须在插入之前进行查询。

请记住,即使使用 RDBMS,当表上有索引时(即通常),插入也可能(相对)昂贵。我认为在 Mongo 中使用嵌入式文档在这方面并没有太大的不同。

对于查询,正如 Asya Kamsky 建议的那样,您可以使用$nin 运算符来查找没有去海滩的每个人。例如:

db.people.find({ 
    actions: { $nin: ["beach"] }
});

不过,在这种情况下,使用嵌入式文档可能不是最好的方法。我认为最好的方法是拥有一个“平面”活动集合,其中包含如下文档:

{
    user_id
    date
    action
}

然后你可以运行这样的查询:

var start = new Date(2012, 6, 3);
var end = new Date(2012, 5, 27);
db.activities.find({ 
    date: {$gte: start, $lt: end }, 
    action: { $in: ["beach", "shopping" ] } 
});

最后一步是在您的客户端驱动程序上,查找存在“购物”记录但不存在“海滩”活动记录的用户 ID。

于 2012-06-03T19:14:57.487 回答
0

一种可能的结构是使用嵌入的文档数组(用户集合):

{
    user_id: 1234,
    actions: [ 
        { action_type: "beach", date: "6/1/2012" },
        { action_type: "shopping", date: "6/2/2012" }
    ]
},
{ another user }

然后你可以做一个这样的查询,使用$elemMatch来查找符合特定条件的用户(在本例中,是最近三天购物的人:

var start = new Date(2012, 6, 1);
db.people.find( { 
    actions : { 
        $elemMatch : { 
            action_type : { $in: ["shopping"] }, 
            date : { $gt : start } 
        } 
    } 
});

对此进行扩展,您可以使用 $and 运算符来查找过去三天内所有去购物但没有去海滩的人:

var start = new Date(2012, 6, 1);
db.people.find( {  
    $and: [
        actions : { 
            $elemMatch : { 
                action_type : { $in: ["shopping"] }, 
                date : { $gt : start } 
            } 
        },
        actions : { 
            $not: {
                $elemMatch : { 
                    action_type : { $in: ["beach"] }, 
                    date : { $gt : start } 
                } 
            }
        }
    ]
});
于 2012-06-03T19:38:13.267 回答