Unraveling the Mysteries of where.not in Rails

During a recent project, I encountered an intriguing aspect of where.not and of negative conditions in general, that's definitely worth highlighting. My task was simple: we aimed to exclude certain invoices by applying two negative conditions using the where.not method. We already had in place a where.not clause.

The existing query was something like: Invoice.where.not(my_data_errors: nil), and I had to add just one more not clause for correlated_invoice_marks: [] to be excluded.

Consider the following two ActiveRecord queries:
Invoice.where.not(my_data_errors: nil, correlated_invoice_marks: [])
Invoice.where.not(my_data_errors: nil).where.not(correlated_invoice_marks: [])

What would you choose?

At a glance, both seem as though they'd return the same set of records. In a similar situation without the .not condition, you'd typically add the condition into the parentheses of .where without much thought. However, while in the common .where adding a condition makes it more restrictive, doing the same inside the parentheses of .not actually makes it less restrictive!

To explain, the first query fuses the conditions, effectively searching for records where both conditions hold true. Conversely, the second query, by segregating the conditions, fetches records that satisfy either of the conditions.

To illustrate, imagine our database has these entries:
Invoice A: my_data_errors is not nil, and correlated_invoice_marks is empty.
Invoice B: my_data_errors is nil, but correlated_invoice_marks is not empty.
Invoice C: both my_data_errors and correlated_invoice_marks are not nil.
The first query returns only Invoice C since it's the sole invoice that meets both conditions. Meanwhile, the second query fetches both Invoice A and C since each matches at least one condition.

My takeaway? Exercise caution when crafting negative conditions in Rails (or in any language/framework, for that matter). A seemingly minor syntax variation can yield significantly different results. Always aim to keep negative conditions clear and distinct for both clarity and predictable behavior. What's your take on this?

Max one mail per week.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.