2016-05-13 6 views
1

In meiner Rails 4 App habe ich ein Item-Modell und ein Flag-Modell. Artikel has_many Flaggen. Flaggen belong_to Artikel. Flag hat die Attribute item_id, user_id und reason. Ich brauche einen Weg, um ein zufälliges ausstehendes Element zu finden, das nicht markiert ist. Ich verwende enum für ausstehenden Status.Wie finde ich ein zufälliges Element, das keine Zeilen in einer Join-Tabelle hat?

Ich glaube, ich kann eine zufällige ausstehende Position finden, die mit gekennzeichnet ist:

@pending_item = Item.pending.joins(:flags).order("RANDOM()").first 

aber wie kann ich eine zufällige ausstehende Position finden, die nicht markiert ist?

Antwort

1

Verwenden where mit not exists zu bekommen Item s ohne Flag s:

@pending_item = Item.pending. 
    where("not exists (select 1 from flags where flags.item_id = items.id)"). 
    order("RANDOM()").first 

Randbemerkung: order("RANDOM()").first nicht effizient ist, wenn es viele Item s sind, die die Kriterien erfüllen. Dies wird wahrscheinlich effiziente für einen großen Tisch sein:

unflagged_pending_items = Item.pending. 
    where("not exists (select 1 from flags where flags.item_id = items.id)") 
@pending_item = unflagged_pending_items.offset(rand unflagged_pending_items.count).first 

Alternativ kann, wenn die erste Abfrage nicht zu langsam ist und Sie nicht einem anderen Zufallsgenerator jedes Mal benötigen, können Sie nur für ein das Ergebnis zwischenspeichern während.

+0

Wenn ich eine sehr große Anzahl von ausstehenden Elementen habe, ist '.order (" RANDOM() "). Zuerst eine effiziente Möglichkeit, eine zufällige zu finden? – user4584963

+1

Oh, ich wollte etwas dazu sagen. Ich habe meine Antwort hinzugefügt. –

1

Ich weiß nicht, ob es besser ist, eine Unterauswahl zu verwenden oder nicht. Ich bevorzuge Ids und Fremdschlüssel, wo ich es verwenden kann.

# initialize my instance variable to have it in the view always 
@pending_item = nil 

# get an array with all flagged item ids 
flagged_item_ids = Flag.group(:item_id).pluck(:item_id) 

# search for an unflagged item, if there are item ids in the array only, 
# because the statement could return a flagged item with an empty array in the condition 
if flagged_item_ids > 0 
    @pending_item = Item.pending.where.not(id: flagged_item_ids).order("RANDOM()").first 
end 

@pending_item 
# => an unflagged random item or nil 

Beachten Sie, dass das Array flagged_item_ids eine riesige Menge von markierten Artikel ids halten konnte. Dies kann viel Speicher belegen.

0

Lösung # 1

Item.pending.joins("LEFT OUTER JOIN (SELECT id, item_id 
             FROM flags 
            ) AS temp 
            ON temp.item_id = items.id") 
      .where('temp.id = null') 
      .order("RANDOM()") 
      .first 

Wenn wir also LEFT OUTER JOIN verwenden, wird temp.idnull gleich, wenn das Element nicht markiert ist.

Lösung # 2

Item.pending.where("id NOT IN (SELECT item_id FROM flags)") 
      .order("RAMDOM()") 
      .first 

Dieses klar ist, finden wir ein Element, das nicht markiert wurde.

Verwandte Themen