2

だから私はソーシャルブックマーク風のセットアップをここに持っています。4 つのテーブル: 投稿、ユーザー、コメント、および投票。ホームページは単に投稿モデルのインデックス ページを指し、Post.all配列に対してループを実行し、いくつかの基本的な情報を表示します: title, upvotes- downvotes(私は ti を投稿の「スコア」と呼びます)。

今、私がする必要があるのは、モデルでこれらの投稿を (私は推測していますが) 相対的な作成時間 (ページの読み込みからこの投稿が作成されてから何秒か?) で割ったスコアで順序付けすることです。Railscreated_atでは、投稿オブジェクトに列が表示され、次のような投票の表があります。

投稿モデル

class Post < ActiveRecord::Base
  attr_accessible  :id, :title, :url, :user_id, :comment_count, :agreement
  has_many :votes
  has_many :comments
  belongs_to :user
end

投票モデル

class Vote < ActiveRecord::Base
  attr_accessible :direction, :post_id, :vote_type, :user_id
  belongs_to :user
  belongs_to :post
  belongs_to :comment
end

post モデルの ID は、vote モデルの post_id に対応します。

:directionブール値です。0賛成票と反対票に1関連付けられています。Post_ID は、投票が行われた投稿の ID です。

ホームページでの位置が作成時間と賛成票と反対票の両方によって決定されるように、投稿をどのように並べ替える必要がありますか?

def index
    @posts = Post.join(:votes).order(<???>)
end

ETA: @shweta は正しい軌道に乗っており、投稿と投票のテーブルに参加しています。ただし、彼の回答の選択部分は機能しません。

4

3 に答える 3

4

免責事項:私は Ruby も Rails も知りません。SQL だけです。

shwetaの答えは本当に近いように見えるので、SQLハックアンドスラッシュで終わらせようとします:

賛成票の順に並べてから、created_at

@posts = Post.joins(:votes).select("posts.id,sum(if(direction = 0, 1, -1)) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

また、投票数が 0 の投稿を含めるには、左結合を行う必要があります。

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select("posts.id,sum(if(direction = 0, 1, -1)) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

更新:投票されていない投稿の順序を修正するには:

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select("posts.id,sum(if(direction = 0, 1, if(direction is null, 0, -1))) as score,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("score DESC,posts.created_at")

更新 2:ランキングの改善

古い項目を除外すると、リストが空の状態になる可能性があります (静かな日など)。次のように、ランキング用に別の列を追加することをお勧めします。

@posts = Post.joins("LEFT JOIN votes ON posts.id = votes.post_id").select(
    "posts.id," +
    "sum(if(direction = 0, 1, if(direction is null, 0, -1))) as score," +
    "(sum(if(direction = 0, 1, if(direction is null, 0, -1))) * " +
    " if(unix_timestamp() - unix_timestamp(posts.created_at) < 7200, 3, 1)) as rank," +
    "posts.created_at,OTHER_ATTRS_YOU_NEED"
).group("posts.id").order("rank DESC,posts.created_at")

ランキングの目的で、これは 2 時間未満の投稿のスコアを 3 倍にします。

于 2013-01-16T09:06:48.527 に答える
1

賛成票で注文してからcreated_at

@posts = Post.joins(:votes).select("posts.id,count(votes.id) AS vote_count,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("vote_count DESC,posts.created_at")

created_atで注文してから賛成

@posts = Post.joins(:votes).select("posts.id,count(votes.id) AS vote_count,posts.created_at,OTHER_ATTRS_YOU_NEED").group("posts.id").order("posts.created_at, vote_count DESC")
于 2013-01-11T07:49:14.600 に答える
1

だからあなたはで注文したいですrating/(time elapsed since creation)。これは、sort_byを使用して純粋なRubyで実行できますが、生のSQLほど効率的ではありません。

つまり...DBでそれを行うには、Post#creation以降に経過した時間を計算する必要があります。Mysqlには、その種の計算用のTIMEDIFF(Postres / Oracleなどでは異なる場合があります)があります。SQLには多かれ少なかれ(ライト/擬似コードで)含まれます:

class Post
  def self.order_by_popularity
    score        = "sum(if(direction = 0, 1, -1)) as score"
    votes_count  = "count(votes.id) as votes_count"
    time_elapsed = "ABS(Timediff(#{Time.now}, created_at)) as time_elapsed"

    select("posts.*, votes.id, #{score}, #{votes_count}, #{time_elapsed}").
    join(:votes).group("votes.id").
    order "ABS(votes_count/time_elapsed)"
  end
end

更新:上記で得られたすべての提案をまとめます(BTWjoin(:votes)はデフォルトで左結合です)。落とし穴はあるのかしら…。

以前のコメントが示唆しているように、vote_countが投稿のキャッシュ列またはjoin+groupから来る場合。

補足:私はこのタイプのクエリをうまく使用したので、正しい方向に向けることができれば幸いですが、率直に言って、これには通常のスタックオーバーフローヘルプが提供するものよりも多くのテスト/試行が必要です。

于 2013-01-15T22:39:52.493 に答える