I have a Rails app and a column that contains an array like ["manager", "engineer"]
etc. and a where statement like this:
where("? = ANY roles", query)
which works find if I pass a single value for query. I want to be able to pass multiple values. I did some Googling and an simple solution was found:
where("? && roles", query )
except that if I pass something in like "['admin', 'guest']"
I get this error:
PG::InvalidTextRepresentation: ERROR: malformed array literal: "['admin', 'guest']" LINE 1: .... $1 AND ('[''admin'... ^ DETAIL: "[" must introduce explicitly-specified array dimensions.
I suspect that there is some weird quote escaping issue but I can't figure it out. Those error messages results in a bunch of JSON Q&As but nothing that jumps out with a solution.
UPDATE
I always seem to find a clue after posting a question - I tried:
where("'{guest, admin}'::text[] && roles", query )
and it works - I still don't really know why but it does. Now I can't see how to get the ?
back in there now so I can search on it.
UPDATE 2
I took the first answer below and did a bit of refactoring to get what I think is a simple elegant solution:
where("'{#{roles}}'::text[] && roles")
This way I can pass a simple text string to my application helper that this where clause sits. It handles single and multiple queries.
You're almost always better off using the array constructor syntax for arrays. From the fine manual:
4.2.12. Array Constructors
An array constructor is an expression that builds an array value using values for its member elements. A simple array constructor consists of the key word ARRAY
, a left square bracket [
, a list of expressions (separated by commas) for the array element values, and finally a right square bracket ]
. For example:
SELECT ARRAY[1,2,3+4]; array --------- {1,2,7} (1 row)
ActiveRecord will expand an array value for a placeholder into a comma delimited list and that's exactly what the array[...]
syntax wants between the brackets. So you'd say:
where('array[?] && roles', query)
This even does the right thing if query
is a single value.
As far as your UPDATE goes, this:
'{guest, admin}'::text[]
is a string literal ('{guest, admin}'
) followed by a type cast (::
) to an array-of-text (text[]
). The '{...}'
syntax inside the string is another form of an array that is easy to read but a hassle to properly build; the fine manual also covers this form:
8.15.2. Array Value Input
To write an array value as a literal constant, enclose the element values within curly braces and separate them by commas.
I use the array[...]
version exclusively because it is easier to work with and more explicit as to what type the array elements are.
Edit
DO NOT do what this answer suggests since it is dangerous and fragile. It was one of those things I really did not think through and regret posting in public
You want your array literal to be formatted like '{"admin", "guest"}'
Response to edited OP
query_terms = ["admin", "guest"] query = "{#{ query_terms.map {|term| %Q("#{ term }") }.join(",") }}" where("? && roles", query)