2012-06-03 31 views
1

我有两个系列 - 购物者(每天在商店中的每个人)和海滩游客(每个人在某一天的海滩上)。每天都有参赛作品,人们可以在沙滩上,或者购物或者同时做两件事,或者在任何一天都不做。我现在要做查询 - 过去7天内没有去过海滩的所有购物者。如何在mongodb中执行'不进入'操作?

我是新来蒙戈,所以它可能是我的架构设计是不适合的NoSQL数据块。我在加入时看到了类似的问题,并且在大多数情况下建议将其非规范化。所以我能想到的一个解决方案是创建集合活动,索引日期,嵌入用户行为。所以像

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

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

+0

你为什么说你必须在插入前查询?查询什么? –

+1

您是否也了解美元运营商? (“不在”) –

+0

@AsyaKamsky我认为欧普表示,要插入一个动作,首先需要查询正确的用户。 – McGarnagle

回答

3

一些建议。

图了所有的疑问,你会运行,而所有类型的数据,您将需要存储。例如,你期望在未来增加活动,还是将沙滩和商店全部?

考虑一下你有多少次写操作,哪一次写操作要快一些。

确定你的文件将如何随时间增长,以确保您的模式是在长期的可扩展性。

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

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

现在您的查询变得更简单了,无论您是否有两个或十个活动。

当新的活动进来时,你总是必须根据它更新正确的记录。 如果你想你可以只添加一条记录到您的收藏显示用户,日期,活动那么你的刀片是快很多,但是你的查询现在需要做的工作的查询为用户很多,红枣活动。

拟议架构,这里是插入/更新语句:

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

那是什么说的是:“对somedate用户名加1的逛过属性,创建它,如果它不存在又名"upsert"(这是最后的“真”的说法)。

这里是一个特殊的日子谁做了不止一次活性1以上,但没有做任何活性2的所有用户的查询。

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

请谨慎选择一个文档可以有连续和无限增长的模式。

例如,将日期和活动数组持续增长的用户集合中的所有内容都存储在此问题中。请参阅高亮显示的部分here以解释这一点 - 并记住,大型文档将不断进入您的工作数据集,并且如果它们很大并且其中有大量无用(旧)数据,那将会伤害您的性能应用程序,以及磁盘上的数据碎片。

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

+0

有超过2个动作,再加上用户本身随时间而变化,所以当我为每个动作做新记录时,我也在倾倒当前用户属性,以便能够查询是否存在任何动作的相关性用户对“他有多少朋友”这样的用户属性做了些什么。 – Shekhar

+0

用户每个日期可以有两个以上的活动计数器。你不*要*为每个动作创建一个新记录,你只需要改变(增加)那个用户在那个特定日子的记录。我将添加一个示例更新。 –

+0

嗯,我可以保留两个计数器以方便查询,然后记录每个操作,以便像shopped_at一样执行更多详细信息。 – Shekhar

0

插入现在变成昂贵的,因为现在我将插入前进行查询。

请记住,即使RDBMS,插入可以(相对)昂贵时,有指标到位放在桌子上(即通常情况下)。我不认为在Mongo中使用嵌入式文档在这方面有很大不同。

对于查询,为阿霞甘维珍建议你可以使用$nin operator找到大家谁没有去海边。例如:

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

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

+0

通常,如果我在RDBMS上这样插入数据,我会进行批量上传,因此存在成本,但是我可以根据分组更新进行优化。 另外,如果查询变得稍微复杂一点,会发生什么情况 - 查找在过去3天内只去过一次的用户。 – Shekhar

+0

@Shekhar确实变得更加复杂。我认为可能会涉及一个map-reduce查询,具体取决于您最终如何设计架构。 – McGarnagle

+0

我不认为你需要map/reduce,如果你使用一个模式,你只是在特定的一天增加一个用户/活动的计数器。 –

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

扩大这一点,你可以用$和操作员发现所有的人去逛街,但没有去海边,在过去三天

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

由于性能问题不断增长的文档,我不会推荐这种方法。最好添加文档并查询最新的文档,而不是不断增加一组文档。 –

+0

看起来像这样可能会工作,因为我还可以为每个日期操作对的用户转储属性来执行我的其他查询,“哪种用户更有可能执行此操作”。查询性能嵌入了多少文档是否重要?我可能有100万个user_ids,每天都有30个动作。 @AsyaKamsky是的,正是我的问题 - 我担心这可能是性能问题。 – Shekhar

+0

@AsyaKamsky有关嵌入式字段的索引呢? http://www.mongodb.org/display/DOCS/Indexes#Indexes-IndexingonEmbeddedFields%28%22DotNotation%22%29 – McGarnagle