加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 计算在Rails中发布的连续天数

发布时间:2020-12-17 02:54:50 所属栏目:百科 来源:网络整理
导读:在一个简单的 Ruby on Rails应用程序中,我正在尝试计算用户发布的连续天数.例如,如果我在过去4天中发布了每一天,我想在我的个人资料中说“你目前的帖子连续4天,继续保持
在一个简单的 Ruby on Rails应用程序中,我正在尝试计算用户发布的连续天数.例如,如果我在过去4天中发布了每一天,我想在我的个人资料中说“你目前的帖子连续4天,继续保持!”或类似的东西.

我应该跟踪我的某个模型中的“条纹”,还是应该在其他地方计算它们?不知道我应该在哪里做,或者如何正确地做到这一点,所以任何建议都会很精彩.

我很高兴包含您认为有用的任何代码,请告诉我.

解决方法

我不确定它是否是最好的方法,但这是在SQL中实现它的一种方法.首先,看看以下查询.

SELECT
  series_date,COUNT(posts.id) AS num_posts_on_date
FROM generate_series(
       '2014-12-01'::timestamp,'2014-12-17'::timestamp,'1 day'
     ) AS series_date
LEFT OUTER JOIN posts ON posts.created_at::date = series_date
GROUP BY series_date
ORDER BY series_date DESC;

我们使用generate_series生成从2014-12-01开始到2014-12-17(今天)结束的日期范围.然后我们用posts表进行LEFT OUTER JOIN.这为我们在范围内的每一天提供了一行,当天在num_posts_on_date列中有帖子数.结果看起来像这样(SQL Fiddle here):

series_date                     | num_posts_on_date
---------------------------------+-------------------
 December,17 2014 00:00:00+0000 |                 1
 December,16 2014 00:00:00+0000 |                 1
 December,15 2014 00:00:00+0000 |                 2
 December,14 2014 00:00:00+0000 |                 1
 December,13 2014 00:00:00+0000 |                 0
 December,12 2014 00:00:00+0000 |                 0
 ...                             |               ...
 December,01 2014 00:00:00+0000 |                 0

现在我们知道从12月14日到17日每天都有一个帖子,所以如果今天的12月17日我们知道当前的“连胜”是4天.我们可以做更多的SQL来获得最长的连胜纪录,如described in this article,但由于我们只对“当前”连胜的长度感兴趣,所以只需稍作改动.我们所要做的就是更改查询以仅获取num_posts_on_date为0的第一个日期(SQL Fiddle):

SELECT series_date
FROM generate_series(
       '2014-12-01'::timestamp,'1 day'
     ) AS series_date
LEFT OUTER JOIN posts ON posts.created_at::date = series_date
GROUP BY series_date
HAVING COUNT(posts.id) = 0
ORDER BY series_date DESC
LIMIT 1;

结果如下:

series_date
---------------------------------
 December,13 2014 00:00:00+0000

但是因为我们实际上想要从最后一天起没有帖子的天数,我们也可以在SQL中做到这一点(SQL Fiddle):

SELECT ('2014-12-17'::date - series_date::date) AS days
FROM generate_series(
       '2014-12-01'::timestamp,'1 day'
     ) AS series_date
LEFT OUTER JOIN posts ON posts.created_at::date = series_date
GROUP BY series_date
HAVING COUNT(posts.id) = 0
ORDER BY series_date DESC
LIMIT 1;

结果:

days
------
    4

你去!

现在,如何将它应用于我们的Rails代码?像这样的东西:

qry = <<-SQL
  SELECT (CURRENT_DATE - series_date::date) AS days
  FROM generate_series(
         ( SELECT created_at::date FROM posts
           WHERE posts.user_id = :user_id
           ORDER BY created_at
           ASC LIMIT 1
         ),CURRENT_DATE,'1 day'
       ) AS series_date
  LEFT OUTER JOIN posts ON posts.user_id = :user_id AND
                           posts.created_at::date = series_date
  GROUP BY series_date
  HAVING COUNT(posts.id) = 0
  ORDER BY series_date DESC
  LIMIT 1
SQL

Post.find_by_sql([ qry,{ user_id: some_user.id } ]).first.days # => 4

正如您所看到的,我们添加了一个条件来限制user_id的结果,并用一个查询替换我们的硬编码日期,该查询获取用户的第一个帖子的日期(generate_series函数内的子选择)作为范围的开头和CURRENT_DATE为范围的结尾.

最后一行有点搞笑,因为find_by_sql将返回一个Post实例数组,因此您必须在数组中的第一个上调用days来获取值.或者,您可以这样做:

sql = Post.send(:sanitize_sql,[ qry,{ user_id: some_user.id } ])
result_value = Post.connection.select_value(sql)
streak_days = Integer(result_value) rescue nil # => 4

在ActiveRecord中,它可以变得更干净:

class Post < ActiveRecord::Base
  USER_STREAK_DAYS_SQL = <<-SQL
    SELECT (CURRENT_DATE - series_date::date) AS days
    FROM generate_series(
          ( SELECT created_at::date FROM posts
            WHERE posts.user_id = :user_id
            ORDER BY created_at ASC
            LIMIT 1
          ),'1 day'
        ) AS series_date
    LEFT OUTER JOIN posts ON posts.user_id = :user_id AND
                             posts.created_at::date = series_date
    GROUP BY series_date
    HAVING COUNT(posts.id) = 0
    ORDER BY series_date DESC
    LIMIT 1
  SQL

  def self.user_streak_days(user_id)
    sql = sanitize_sql [ USER_STREAK_DAYS_SQL,{ user_id: user_id } ]
    result_value = connection.select_value(sql)
    Integer(result_value) rescue nil
  end
end

class User < ActiveRecord::Base
  def post_streak_days
    Post.user_streak_days(self)
  end
end

# And then...
u = User.find(123)
u.post_streak_days # => 4

以上是未经测试的,因此可能需要一些摆弄才能使其正常工作,但我希望它至少能指出正确的方向.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读