chris P
chris P

Reputation: 6589

How can I query a Ruby array, like I do with Rails ActiveRecord?

I have a table, let's call it Widget.

I do some complex processing to get various type of Widgets. These end up in two different variables.

To keep things simple, let's say we have...

widgetsA = Widget.where("blah blah blah")
widgetsB = Widget.where("blah blah blah blah")

We can still perform ActiveRecord functions like .where on widgetsA and widgetsB.

Now, after retrieving the sets for A and B, I need to union them, and then perform additional ActiveRecord functions on them.

I want to do something like this...

widgetsAll = widgetsA | widgetsB
widgetsAll = widgetsAll.order("RANDOM()")
widgetsAll = widgetsAll.where(answers_count: 0).limit(10) + widgetsAll.where("answers_count > 0").limit(10)

This will take all the widgets (union) found in A & B, randomize them, and select 10 with answers and 10 without answers.

The problem is, I cannot user .order and widgetsAll is no longer an ActiveRecord object, but it's an Array because of the widgetsAll = widgetsA | widgetsB line. How do I either

A) Union/Intersect two ActiveRecord sets, into an ActiveRecord set

B) How can I order and perform a 'where' style query on an Array.

Either will solve the issue. I assume B is a bit better for performance, so I suppose that would be the better answer.

Any ideas?

Lastly, lets say the Widget table has columns id, name, description. In the end we want an ActiveRecord or Array (likely preferred) of everything.

EDIT: (Attempting to combine via SQL UNION... but not working)

w1 = Widget.where("id = 1 OR id = 2")
w2 = Widget.where("id = 2 OR id = 3")
w3 = Widget.from("(#{w1.to_sql} UNION #{w2.to_sql})")

PG::SyntaxError: ERROR:  subquery in FROM must have an alias
LINE 1: SELECT "widgets".* FROM (SELECT "widgets".* FROM "widge...

Upvotes: 2

Views: 1870

Answers (3)

spickermann
spickermann

Reputation: 106802

I see two options:

1) Do the union in SQL: Instead of widgetsA | widgetsB that return an array you can do an union in the database, so that the result is still a relation object:

Widget.from("(#{widgetA.to_sql} UNION #{widgetB.to_sql}) AS widgets")

2) Use normal array methods. Your example:

widgetsAll = widgetsAll.order("RANDOM()")
widgetsAll = widgetsAll.where(answers_count: 0).limit(10) + widgetsAll.where("answers_count > 0").limit(10)

would translate to something like this:

widgetsAll = widgetsAll.shuffle
widgetsAll = widgetsAll.select { |answer| widget.answer_count == 0 }.take(10) + 
             widgetsAll.select { |answer| widget.answers_count > 0).take(10)

Read more about Ruby arrays.

Upvotes: 2

Prakash Murthy
Prakash Murthy

Reputation: 13057

One way would be to do as follows:

widgetsAllActual = Widget.where(id: widgetsAll)

This is creating a new Widget::ActiveRecord_Relation collection containing all the elements in widgetsA and widgetsB, and allows for making further active record scoping.

Ref: https://stackoverflow.com/a/24448317/429758

Upvotes: -1

apneadiving
apneadiving

Reputation: 115511

Using the any_of gem you could do:

widgetsAll = Widget.where.any_of(widgetsA, widgetsB)

Upvotes: 1

Related Questions