2

I'd like to fetch all the Active Relations whose ID's are NOT in an array. Like this:

    @widgets = Widget.where("id not in (?)", [1, 2, 3])

This works fine. It returns the full Widget table except the those records excluded by the filter array. However, if the filter array is empty, then it doesn't work.

    @widgets = Widget.where("id not in (?)", [])

returns "[]", when really I would like the equivalent of Widget.all

I have worked around this by testing first if the filter array is empty, and modifying the query. But the workaround seems like a kludge. Is there a way to get express this 'where' clause so that it returns the entire table if the filter array is empty?

2 Answers 2

2

You can add a scope to the Widget model:

scope :excluding_ids, ->(ids) { where('id NOT IN (?)', ids) if ids.any? }

Then it's just a matter of doing this wherever you need it:

@widgets = Widgets.excluding_ids([1,2,3])
Sign up to request clarification or add additional context in comments.

3 Comments

Tried this, it works! It does not fix the underlying issue (why it's not ok to pass [] in the filter array), but adding the scope sheds light on the issue. I'm torn though, because Ross' code is simpler, and yet masking the issue with a filter array of '[0]' doesn't feel quite right.
ActiveRecord converts [] to NULL in SQL. So, you're basically running SELECT * FROM widgets WHERE id NOT IN (NULL), which causes the database to return no records.
Alternatively, if you don't want to do this in a scope: @widgets = ids.any? ? Widget.where("id NOT IN (?)", ids) : Widget.all Although I don't like that because it's rather hard to read (lots of question marks).
1

I'm assuming your IDs start at 1, so you won't have a Widget with id=0. You could add a zero to the array, so it would never be empty, and it would return all widgets since none have id=0.

@widgets = Widget.where("id not in (?)", my_array + [0])

This is kind of a hack, but it should work!

5 Comments

That is exactly the workaround I did :-) But as you say, it's a hack.
@Monty it is a hack, but this is not really a problem of rails. This bug exist at database level bugs.mysql.com/bug.php?id=12474
Thanks @maximus that sheds some light. I am using postgres, but the mysql explanation in that link seems to be based on the ANSI SQL standard for passing 'IN()'. Basically he is saying 'dont't query the db if you know the answer' - it's a waste of resources.
@Ross, I went with Tim's answer, but yours should get 'honorable mention'. I was using "filter_array = ids.empty? ? [0] : ids" and just tacking on " + [0]" is a "cleaner" hack :-)
@Monty to be honest I'd go with Tim's answer as well. If I had to do this in multiple places it would definitely be more convenient to use a scope.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.