我该怎么办';不在';mongodb的运作?

我该怎么办';不在';mongodb的运作?,mongodb,database-schema,schema-design,Mongodb,Database Schema,Schema Design,我有两个收藏品——购物者(某一天商店里的每个人)和海滩游客(某一天海滩上的每个人)。每天都有条目,每个人可以在海滩上,或者购物,或者两者都做,或者在任何一天都不做。我现在想做一个查询——所有在过去7天里没有去海滩的购物者 我是Mongo的新手,所以我的模式设计可能不适合nosql DBs。我在join周围看到了类似的问题,在大多数情况下,有人建议取消规范化。所以,我能想到的一个解决方案是创建集合活动、日期索引、嵌入用户操作。大概是 { user_id date actions

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

我是Mongo的新手,所以我的模式设计可能不适合nosql DBs。我在join周围看到了类似的问题,在大多数情况下,有人建议取消规范化。所以,我能想到的一个解决方案是创建集合活动、日期索引、嵌入用户操作。大概是

{
   user_id
   date
   actions {
      [action_type, ..]
   }
}
现在插入变得很昂贵,因为现在我必须在插入之前进行查询

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

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

对于查询,正如Asya Kamsky所建议的那样,您可以使用来查找所有没有去海滩的人。例如:

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" ] } 
});
然后可以运行如下查询:

{
    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,其中存在“购物”记录,但不存在“海滩”活动的记录。

一些建议

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

考虑一下您将有多少写操作与读操作,以及哪些操作必须更快

确定文档将如何随时间增长,以确保您的模式在长期内是可伸缩的

这里有一种可能的方法,如果你只参加这两项活动的话。每个用户每天一条记录

{ user: "user1",
  date: "2012-12-01",
  shopped: 0,
  beached: 1
}
现在,无论您有两个或十个活动,查询都变得更加简单

当新活动出现时,您必须始终根据它更新正确的记录。 如果您认为可以在集合中添加一条指示用户、日期和活动的记录,那么插入的速度要快得多,但是您的查询现在必须同时查询用户、日期和活动

对于建议的模式,以下是insert/update语句:

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

它的意思是:“对于somedate上的用户名,将其shopped属性增加1,如果它不存在,则创建它(这是最后一个'true'参数)

以下是针对某一天中多次执行activity1但未执行任何activity2的所有用户的查询

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

在选择一个模式时要小心,因为一个文档可能会有连续和无限的增长

例如,将所有内容存储在日期和活动不断增加的用户集合中会遇到此问题。请参阅突出显示的部分以了解此问题的解释-请记住,大型文档将不断进入您的工作数据集中,如果它们很大且有很多无用的(旧的)文档它们中的数据会影响应用程序的性能,磁盘上的数据碎片也会影响应用程序的性能


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

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

然后您可以执行如下查询,使用查找符合特定条件的用户(在本例中,是指过去三天内购物的用户:

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 } 
                } 
            }
        }
    ]
});
在此基础上进行扩展,您可以使用$and运算符查找所有在过去三天内没有去海滩购物的人:

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

为什么说在插入之前必须查询?查询什么?您还知道$nin运算符吗?(“不在”)@AsyaKamsky我认为Op的意思是,要插入一个动作,首先需要查询正确的用户。如果我看到一个新动作说“在海滩上”,我需要找到用户id的记录,以及将该动作嵌入现有数据的日期。如果要将该动作插入正确的用户文档,这将是一个好主意:)但似乎这是不可避免的通常如果我在RDBMS上做这样的插入,我会进行批量上传,所以成本是存在的,但我可以根据一组更新进行优化。此外,如果查询变得稍微复杂,会发生什么?找到过去3天只去海滩一次的用户。@Shekhar,这确实会变得更复杂。我认为一个可能涉及map reduce查询,这取决于您最终如何设计模式。如果您使用的模式只是在特定的一天为用户/活动增加一个计数器,那么我认为您不需要map/reduce。有两个以上的操作,加上用户本身随着时间的推移而变化,所以当我为每个操作做新记录时,我是一个lso转储当前用户属性,以便能够查询用户所做的操作是否与用户属性相关,如“他有多少朋友”。用户每个日期可以有两个以上的活动计数器。您不希望每个操作都有一个新记录,只需更改(增量)用户的记录即可