0

I want to get total count of items with language_id other than [30, 54] and language_id: nil

LOCALES = {
  non_arabic_languages: {
    id: [30, 54]
  }
}

  scope :non_arabic_languages, -> { where.not(language_id: LOCALES[:non_arabic_languages][:id]) || where(language_id: nil) }

This example predictably returns first part, so I only get non arabic items. && works wrong as well. How may I combine it? We'll be thankful for the advice!

6
  • Very close just swap the logical or (||) for the ActiveRecord::QueryMethods#or e.g. where.not(language_id: LOCALES[:non_arabic_languages][:id]).or(where(language_id: nil)) Commented Nov 16, 2021 at 15:11
  • and for Rails 4? Commented Nov 16, 2021 at 15:14
  • 3
    where(Model.arel_table[:language_id].not_in(LOCALES[:non_arabic_languages][:id]).or(Model.arel_table[:language_id].eq(nil))) both will result in WHERE language_id NOT IN (30,54) OR language_id IS NULL Commented Nov 16, 2021 at 15:16
  • 1
    Is this double negation here? If a language is not in the list of non-arabic languages, that makes it arabic, doesn't it? Commented Nov 16, 2021 at 15:17
  • There's total list of langs, arabic_langs and non_arabic_langs(called "other_langs"). Also there's an issue when language_id = nil, items doesn't count. let's nil value will be with other_langs:) Commented Nov 16, 2021 at 15:28

1 Answer 1

2

You're falling into a common trap where you confuse logical operations in Ruby with actually creating SQL via the ActiveRecord query interface.

Using || will return the first truthy value:

where.not(language_id: LOCALES[:non_arabic_languages][:id]) || where(language_id: nil) 

Which is the ActiveRecord relation returned by where.not(language_id: LOCALES[:non_arabic_languages][:id]) since everything except false and nil are truthy in Ruby. || where(language_id: nil) is never actually evaluated.

Support for .or was added in Rails 5. In previous versions the most straight forward solution is to use Arel or a SQL string:

scope :non_arabic_languages, -> { 
  non_arabic_languages_ids = LOCALES[:non_arabic_languages].map { |h| h[:id] }
  where(arel_table[:language_id].not_in(non_arabic_languages_ids ).or(arel_table[:language_id].eq(nil)))
}

I would leave a tagging comment (like @fixme) so that you fix this after upgrading to 5.0 or consider a better solution that doesn't involve hardcoding database ids in the first place like for example using a natural key.

Sign up to request clarification or add additional context in comments.

Comments

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.