来自 MongoDB 个键的动态频率图
Dynamic Frequency Map from MongoDB Keys
我正在通过 Meteor 使用 MiniMongo,我正在尝试根据一组动态查询创建一个频率 table。
我有两个主要字段,localHour
和 localDay
。我预计会有很多重叠,并且我想确定重叠最多的地方。我目前的做法是这样的。
if(TempStats.findOne({
localHour: hours,
localDay: day
})){//checks if there is already some entry on the same day/hour
TempStats.update({//if so, we just increment frequency
localHour: hours,
localDay: day
},{
$inc: {freq: 1}
})
} else {//if nothing exists yet, we put in a new entry
TempStats.insert({
localHour: hours,
localDay: day,
freq: 1
});
}
基本上,每当我有新数据要插入时,这段代码就会运行。目前它工作正常,因为在插入所有数据后,我可以按频率排序以找到最常出现的时间和日期集 (TempStats.find({}, {sort: {freq: -1}}).fetch()
)。
但是,我正在寻找一种按频率搜索任何键的方法。例如,搜索 day 中所有内容最常出现的日期,而不是日期和小时。按照我目前的做法,我需要有多个数据库和不同的插入方法,这有点荒谬。是否有 Mongo(特别是 MiniMongo)解决方案来根据键进行频率映射?
谢谢!
- 关于您的代码的一个小提示:作为 else 语句出现的部分并不是真正需要的,如果您将它与选项 upsert=true 结合使用,您的更新将完成完整的工作,它将插入一个新文档并且 $inc will set the freq field to 1 as desired see: here and here
- 用于计算频率的替代方法:假设您将日期存储为日期时间对象我建议使用聚合(我不确定他们是否在 minimongo 中添加了对聚合的支持)但是有 solutions 然后通过聚合,您可以使用日期时间运算符作为
$hour, $week, etc 用于过滤,$count 用于计算频率,而无需将计数保存在数据库中。
貌似miniMongo其实是不支持聚合的,这样的操作比较困难。一种解决方法是在每天结束时汇总自己并将该汇总记录插入数据库(没有小时字段或将其设置为 -1 之类的东西)。相反,您也可以在每次插入时更新该记录,这很浪费。这将允许您对两者使用相同的集合,并且在其他数据库中相当常见。
您还应该考虑@nickmilon 的第一个建议,因为将 upsert 语句与 $inc 运算符一起使用会将您的示例减少为每个数据点一个操作。
这基本上是一个简单的 map-reduce 问题。
首先,不要将派生数据分成 2 个字段。这违反了数据库最佳实践。如果数据以这种方式出现,请使用它来创建一个 Date
对象。我假设您有一堆正在订阅的集合,然后将所有这些聚合到这个临时本地集合中。这是 map-reduce 模式的映射。在这一点上,由于您的查询未知,因此 CPU (即使它是您的客户)进行聚合是一种浪费。先映射,再缩小。你应该拥有的是一个充满日期时间的集合。如果您愿意,可以将其命名为 TempMapCollection
。现在,使用 forEach()
并传入你的 reduce 函数(按天、按小时等)。
您可以缩减为另一个本地集合,或缩减为 javascript 对象。我喜欢使用集合,但如果对象很复杂,你就会在其中遇到 EJSON 错误。由于您的对象只不过是一个日期时间,所以让我们使用集合。
所以你有这样的东西:
TempMapCollection.find().forEach(function(doc) {
var date = doc.dateTime.getDate();
TempReduceCollection.upsert({timequery: hours}, {$inc: {freq: 1}});
})
现在查询你的reduce集合。这有一个额外的好处,如果你想做 2 个独特的查询,你将不必重新映射。
我正在通过 Meteor 使用 MiniMongo,我正在尝试根据一组动态查询创建一个频率 table。
我有两个主要字段,localHour
和 localDay
。我预计会有很多重叠,并且我想确定重叠最多的地方。我目前的做法是这样的。
if(TempStats.findOne({
localHour: hours,
localDay: day
})){//checks if there is already some entry on the same day/hour
TempStats.update({//if so, we just increment frequency
localHour: hours,
localDay: day
},{
$inc: {freq: 1}
})
} else {//if nothing exists yet, we put in a new entry
TempStats.insert({
localHour: hours,
localDay: day,
freq: 1
});
}
基本上,每当我有新数据要插入时,这段代码就会运行。目前它工作正常,因为在插入所有数据后,我可以按频率排序以找到最常出现的时间和日期集 (TempStats.find({}, {sort: {freq: -1}}).fetch()
)。
但是,我正在寻找一种按频率搜索任何键的方法。例如,搜索 day 中所有内容最常出现的日期,而不是日期和小时。按照我目前的做法,我需要有多个数据库和不同的插入方法,这有点荒谬。是否有 Mongo(特别是 MiniMongo)解决方案来根据键进行频率映射?
谢谢!
- 关于您的代码的一个小提示:作为 else 语句出现的部分并不是真正需要的,如果您将它与选项 upsert=true 结合使用,您的更新将完成完整的工作,它将插入一个新文档并且 $inc will set the freq field to 1 as desired see: here and here
- 用于计算频率的替代方法:假设您将日期存储为日期时间对象我建议使用聚合(我不确定他们是否在 minimongo 中添加了对聚合的支持)但是有 solutions 然后通过聚合,您可以使用日期时间运算符作为 $hour, $week, etc 用于过滤,$count 用于计算频率,而无需将计数保存在数据库中。
貌似miniMongo其实是不支持聚合的,这样的操作比较困难。一种解决方法是在每天结束时汇总自己并将该汇总记录插入数据库(没有小时字段或将其设置为 -1 之类的东西)。相反,您也可以在每次插入时更新该记录,这很浪费。这将允许您对两者使用相同的集合,并且在其他数据库中相当常见。
您还应该考虑@nickmilon 的第一个建议,因为将 upsert 语句与 $inc 运算符一起使用会将您的示例减少为每个数据点一个操作。
这基本上是一个简单的 map-reduce 问题。
首先,不要将派生数据分成 2 个字段。这违反了数据库最佳实践。如果数据以这种方式出现,请使用它来创建一个 Date
对象。我假设您有一堆正在订阅的集合,然后将所有这些聚合到这个临时本地集合中。这是 map-reduce 模式的映射。在这一点上,由于您的查询未知,因此 CPU (即使它是您的客户)进行聚合是一种浪费。先映射,再缩小。你应该拥有的是一个充满日期时间的集合。如果您愿意,可以将其命名为 TempMapCollection
。现在,使用 forEach()
并传入你的 reduce 函数(按天、按小时等)。
您可以缩减为另一个本地集合,或缩减为 javascript 对象。我喜欢使用集合,但如果对象很复杂,你就会在其中遇到 EJSON 错误。由于您的对象只不过是一个日期时间,所以让我们使用集合。
所以你有这样的东西:
TempMapCollection.find().forEach(function(doc) {
var date = doc.dateTime.getDate();
TempReduceCollection.upsert({timequery: hours}, {$inc: {freq: 1}});
})
现在查询你的reduce集合。这有一个额外的好处,如果你想做 2 个独特的查询,你将不必重新映射。