繁体   English   中英

在 MongoDB 中使用聚合管道对前 N 个结果进行分桶

[英]Bucketing top N results with an aggregation pipeline in MongoDB

我正在使用 pymongo 对 MongoDB 进行一些分析。

在 MongoDB 中,有 480000 个 json 对象代表 2020 年 3 月至 4 月之间关于 Covid-19 病毒的推文。

特别是,这些对象包含两个字段:

1)“created_at”,表示推文的创建时间戳(例如 created_at: 2020-03-20T10:57:57.000+00:00 ),日期类型;

2)“retweet_count”,表示这条推文被转发了多少次(例如“retweet_count:30”);

我会创建一个聚合管道,每天接收前 5000 个具有最高 retweet_count 值的 json 对象。

问题是,如果我必须使用组子句、匹配子句或项目子句(我是新手),我也不明白。

这里有一个我做过的尝试:

import pymongo
from datetime import datetime, tzinfo, timezone
from pymongo import MongoClient

client['Covid19']['tweets'].aggregate([
    {
        '$match' : {
            "created_at": { '$gte': datetime(2020, 3, 20), '$lt':  datetime(2020, 3, 21) } 
        }

    },
    {
        '$merge': {
            'into': 'tweets_filtered'
        }
    }
])

print(client['Covid19']['tweets_filtered'].count_documents({}))

该管道提供了从 3 月 20 日到 3 月 21 日发布的推文,但我会概括该过程并每天获取前 5000 条 retweet_count 值最高的推文。

您可以通过编程方式生成所需的边界并使用 $bucket。

  1. $sort 输入以实现所需的排序(最先转发)。
  2. $bucket 按天拆分集合。
  3. $push 在相应的日期下移动每个文档。
  4. $project 和 $slice 以获得前 X 个结果。

Ruby 示例使用时间、计数和消息字段:

require 'mongo'

Mongo::Logger.logger.level = Logger::WARN

client = Mongo::Client.new(['localhost:14420'])
c = client['foo']


c.delete_many

10.times do |i|
  day_time = Time.now - i*86400
  100.times do |j|
    time = day_time + j*100
    count = rand*1000
    message = "message #{count}"

    c.insert_one(time: time, count: count, message: message)
  end
end


days = (-1..10).map { |i| Time.now - i*86400 }.reverse
pp c.aggregate([
  {'$sort' => {count: -1}},
  {'$bucket' => {groupBy: '$time', boundaries: days,
    output: {messages: {'$push' => '$$ROOT'}},
  }},
  {'$project' => {top_messages: {'$slice' => ['$messages', 5]}}},
]).to_a

Pymongo 使用 pandas 回答:

from pymongo import MongoClient
from datetime import datetime
import pandas as pd

TOP_N_PER_DAY = 5000

# Perform the find with a filter; strip out the _id
tweets = db.tweets.find({ 'created_at': {'$gte': datetime(2020, 3, 20), '$lt':  datetime(2020, 3, 22) }}, {'_id': 0})

# Create a dataframe from the find
df = pd.DataFrame(list(tweets))

# Convert the datetime to a date only timeseries
df['date'] = df['created_at'].dt.date

# Group by date and sort by retweet count
df = df.groupby('date').apply(lambda x: x.sort_values('retweet_count', ascending = False)).reset_index(drop=True)

# Take the top n per day
df = df.groupby('date').head(TOP_N_PER_DAY)

# Convert the pandas timeseries back to a datetime
df['date'] = pd.to_datetime(df['date'])

# Convert the dataframe into a list of dicts
records = df.to_dict('records')

# Insert the filtered tweets into a new collection
db.tweets_filtered.insert_many(records)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM