CouchDB - 从列表中筛选每个日志实例的最新日志

3 投票
1 回答
810 浏览
提问于 2025-04-16 13:01

我需要一些帮助,想从一个CouchDB的视图中过滤出不同的值。我有一个数据库,用来存储关于电脑的日志信息。定期会有新的日志写入这个数据库。

简单来说,我存储的条目大致是这样的:

{
   "name": "NAS",
   "os": "Linux",
   "timestamp": "2011-03-03T16:26:39Z",
}
{
   "name": "Server1",
   "os": "Windows",
   "timestamp": "2011-02-03T19:31:31Z",
}
{
   "name": "NAS",
   "os": "Linux",
   "timestamp": "2011-02-03T18:21:29Z",
}

到目前为止,我在过滤这个列表时遇到了一些困难。我想要得到的是每个设备的最新日志文件。

我有一个这样的视图:

function(doc) {
    emit([doc.timestamp,doc.name], doc);
}

我用Python(couchdbkit)查询这个视图,目前我想到的最佳解决方案是这样的:

def get_latest_logs(cls):
    unique = []
    for log in cls.view("logs/timestamp", descending=True):
        if log.name not in unique_names:
            unique.append(log)
    return unique

好的……这个方法可以用。但我有种强烈的感觉,这不是最好的解决方案,因为Python需要遍历整个日志文件列表(这个列表可能会很长)。

我想我需要一个reduce函数,但我找不到可以适应我问题的例子或解释。

所以,我想要的是一个(纯CouchDB的)视图,只输出给定设备的最新日志。

1 个回答

6

这是我所做的事情。虽然这有点过分使用CouchDB,但我取得了很好的效果。

通常,reduce会计算一个总和、计数或者类似的东西。不过,可以把reduce想象成一个淘汰赛。很多值进入,只有一个值出来。这就是“减少”!不断重复这个过程,你就能找到最终的赢家(再一次减少)。在这种情况下,最新的时间戳的日志就是赢家。

当然,轻量级选手不能和重量级选手打比赛。必须有不同的联赛和体重级别。只有文档才能和相似的文档进行对抗。这正是reduce的group参数的作用。它会确保只有实力相当的选手进入我们的“血腥竞技场”。(咖啡开始发挥作用了。)

首先,按设备发出所有日志。发出的value只是文档的一个副本。

function(doc) {
    emit(doc.name, doc);
}

接下来,写一个reduce函数,返回所有给定值中最新的时间戳。如果你发现两个来自不同联赛的选手在打架(来自不同系统的两条日志),就停止比赛!这说明出现了问题(有人查询时没有使用正确的group值)。

function(keys, vals, re) {
    var challenger, winner = null;
    for(var a = 0; a < vals.length; a++) {
        challenger = vals[a];
        if(!winner) {
            // The title is unchallenged. This value is the winner.
            winner = challenger;
        } else {
            // Fight!
            if(winner.name !== challenger.name) {
                // Stop the fight! He's gonna kill him!
                return null; // With a grouping query, this will never happen.
            } else if(winner.timestamp > challenger.timestamp) {
                // The champ wins! (Nothing to do.)
            } else {
                // The challenger wins!
                winner = challenger;
            }
        }
    }

    // Today's champion lives to fight another day.
    return winner;
}

(注意,时间戳的比较可能是错误的。你可能需要转换成Date格式。)

现在,当你用?group=true查询视图时,CouchDB只会在相同key(也就是你的机器名)之间进行减少(找出赢家)。

(你也可以把数组作为key,这样会更灵活。你可以用emit([doc.name, doc.timestamp], doc)来发出。这样你可以通过像?reduce=false&startkey=["NAS", null]&endkey=["NAS", {}]这样的查询查看所有系统的日志,或者用?group_level=1查看每个系统的最新日志。

最后,“停止比赛”的部分是可选的。你可以简单地返回最新时间戳的文档。不过,我更喜欢保留这个,因为在类似的情况下,我想看看我是否在map-reduce过程中出错了,而空的reduce输出就是我的重要线索。

撰写回答