 <p>@Saurabh Nanda: Similar to what you posted, you can also create a simple function to convert your varchar array to lowercase as follows:</p> <pre><code>CREATE OR REPLACE FUNCTION array_lowercase(varchar[]) RETURNS varchar[] AS $BODY$ SELECT array_agg(q.tag) FROM ( SELECT btrim(lower(unnest($1)))::varchar AS tag ) AS q; $BODY$ language sql IMMUTABLE; </code></pre> <p>Note that I'm also trimming the tags of spaces. This might not be necessary for you but I usually do for consistency.</p> <p>Testing:</p> <pre><code>SELECT array_lowercase(array['Hello','WOrLD']); array_lowercase ----------------- {hello,world} (1 row) </code></pre> <p>As noted by Saurabh, you can then create a GIN index:</p> <pre><code>CREATE INDEX ix_tags ON tagtable USING GIN(array_lowercase(tags)); </code></pre> <p>And query:</p> <pre><code>SELECT * FROM tagtable WHERE ARRAY['mytag'::varchar] &amp;&amp; array_lowercase(tags); </code></pre> <p><strong>UPDATE:</strong> Performance of <code>WHILE</code> vs array_agg/unnest</p> <p>I created table of 100K 10 element <code>text[]</code> arrays (12 character random mixed case strings) and tested each function.</p> <p>The array_agg/unnest function returned:</p> <pre><code>EXPLAIN ANALYZE VERBOSE SELECT array_lowercase(data) FROM test; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Seq Scan on public.test (cost=0.00..28703.00 rows=100000 width=184) (actual time=0.320..3041.292 rows=100000 loops=1) Output: array_lowercase((data)::character varying[]) Total runtime: 3174.690 ms (3 rows) </code></pre> <p>The WHILE function returned:</p> <pre><code>EXPLAIN ANALYZE VERBOSE SELECT array_lowercase_while(data) FROM test; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Seq Scan on public.test (cost=0.00..28703.00 rows=100000 width=184) (actual time=5.128..4356.647 rows=100000 loops=1) Output: array_lowercase_while((data)::character varying[]) Total runtime: 4485.226 ms (3 rows) </code></pre> <p><strong>UPDATE 2:</strong> <code>FOREACH</code> vs. <code>WHILE</code> As a final experiment, I changed the WHILE function to use FOREACH:</p> <pre><code>CREATE OR REPLACE FUNCTION array_lowercase_foreach(p_input varchar[]) RETURNS varchar[] AS $BODY$ DECLARE el text; r varchar[]; BEGIN FOREACH el IN ARRAY p_input LOOP r := r || btrim(lower(el))::varchar; END LOOP; RETURN r; END; $BODY$ language 'plpgsql' </code></pre> <p>Results appeared to be similar to <code>WHILE</code>:</p> <pre><code>EXPLAIN ANALYZE VERBOSE SELECT array_lowercase_foreach(data) FROM test; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Seq Scan on public.test (cost=0.00..28703.00 rows=100000 width=184) (actual time=0.707..4106.867 rows=100000 loops=1) Output: array_lowercase_foreach((data)::character varying[]) Total runtime: 4239.958 ms (3 rows) </code></pre> <p>Though my tests are not by any means rigorous, I did run each version a number of times and found the numbers to be representative, suggesting that the SQL method (array_agg/unnest) is the fastest.</p>