Best way to search in postgres by a group of keyword - postgresql

right now I have a keyword array like:
['key1', 'key2', 'key3'.......] , the keyword can be number or character.
If I want to search in my table (postgres database), and find out all record contain any of keyword in that array, how can I do that?
For example:
I got a table which has a column called name and a column called description
I need find all record that either name or description contains any keywords in that array.
thanks

Maybe this example will be useful:
CREATE TABLE TEST(
FIELD_KEY TEXT);
INSERT INTO TEST VALUES('this is hello');
INSERT INTO TEST VALUES('hello');
INSERT INTO TEST VALUES('this');
INSERT INTO TEST VALUES('other message');
SELECT *
FROM TEST
WHERE FIELD_KEY LIKE ANY (array['%this%', '%hel%']);
This will return:
this is hello
hello
this
Here other example:
SELECT *
FROM TEST
WHERE FIELD_KEY ~* 'this|HEL';
~* is case insensitive, ~ is case sensitive
You can try this example here.

select *
from t
where
array[name] <# my_array
or array[description] <# my_array
Couple the like operator with the any subquery expression:
select *
from t
where name like any (values ('%John%'), ('%Mary%'))
Or the array syntax:
where name like any (array['%John%', '%Mary%'])

Related

postgressql query matching a field containing domainname

Need help with postsql query field matching a certain domain names in the end as per below in a particular FIELD.
1234.abc.xyz.com;
0971.abc.xyz.com
WHERE CAST (domain_name AS text) LIKE '%\d{4}.abc.xyz.com%'
#where domain_name is the FIELD name
~ is used for regular expression matching, LIKE for simple matching. Read more about them here: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-SIMILARTO-REGEXP
If you just want to find domain_name that end in a particular text, the simple matching works fine (don't know if you really need the cast):
select * from tbl_test where domain_name LIKE '%.abc.xyz.com'
This will not work correctly:
select * from tbl_test where domain_name ~ '\d\d\d\d.abc.xyz.com'
The dot (.) is "any character" in a regular expression so this domain would be selected: abcd.abcxxyzdcom. You need to escape the dot in the string for it to be treated literally like this: '\d\d\d\d\.abc\.xyz\.com'
Underscore is a wildcard for "any character" in the simple LIKE.
use ~ followed by your search pattern as regular expression:
where domain_name ~ '\d\d\d\d\.abc\.xyz\.com'
playground:
https://dbfiddle.uk/O0Q_Ctmo
create table tbl_test (
domain_name varchar
);
insert into tbl_test VALUES
('1234.abc.xyz.com'),
('0971.abc.xyz.com'),
('0971.abc.xyz.bam'),
('1234.xxx.xyz.com'),
('123.xxx.xyz.com'),
('aaaa.xxx.xyz.com')
CREATE TABLE
INSERT 0 6
select * from tbl_test
where domain_name ~ '\d\d\d\d\.abc\.xyz\.com'
domain_name
1234.abc.xyz.com
0971.abc.xyz.com
SELECT 2
fiddle

Postgres: ANY function does not work on varchar array

I have a "product" table with a varchar[] column to keep excluded companies.
When I select the array for a specific product, I get it like this:
SELECT excluded_company_codes FROM product WHERE code = '123'
excluded_company_codes
----------
{'10'}
However, oddly enough, when I try to check if company code exists in the array with ANY function, it doesn't work:
SELECT '10'=ANY(product.excluded_company_codes) where code = '123'
?column?
----------
false
What am I doing wrong here?
The string in the array contains two single quotes. If you want to find that, you have to
SELECT '''10''' = ANY(product.excluded_company_codes)
FROM product
WHERE code = '123';
If you want to avoid doubling the quotes, you can use dollar quoting: $$'10'$$.

Cast a PostgreSQL column to stored type

I am creating a viewer for PostgreSQL. My SQL needs to sort on the type that is normal for that column. Take for example:
Table:
CREATE TABLE contacts (id serial primary key, name varchar)
SQL:
SELECT id::text FROM contacts ORDER BY id;
Gives:
1
10
100
2
Ok, so I change the SQL to:
SELECT id::text FROM contacts ORDER BY id::regtype;
Which reults in:
1
2
10
100
Nice! But now I try:
SELECT name::text FROM contacts ORDER BY name::regtype;
Which results in:
invalid type name "my first string"
Google is no help. Any ideas? Thanks
Repeat: the error is not my problem. My problem is that I need to convert each column to text, but order by the normal type for that column.
regtype is a object identifier type and there is no reason to use it when you are not referring to system objects (types in this case).
You should cast the column to integer in the first query:
SELECT id::text
FROM contacts
ORDER BY id::integer;
You can use qualified column names in the order by clause. This will work with any sortable type of column.
SELECT id::text
FROM contacts
ORDER BY contacts.id;
So, I found two ways to accomplish this. The first is the solution #klin provided by querying the table and then constructing my own query based on the data. An untested psycopg2 example:
c = conn.cursor()
c.execute("SELECT * FROM contacts LIMIT 1")
select_sql = "SELECT "
for row in c.description:
if row.name == "my_sort_column":
if row.type_code == 23:
sort_by_sql = row.name + "::integer "
else:
sort_by_sql = row.name + "::text "
c.execute("SELECT * FROM contacts " + sort_by_sql)
A more elegant way would be like this:
SELECT id::text AS _id, name::text AS _name AS n FROM contacts ORDER BY id
This uses aliases so that ORDER BY still picks up the original data. The last option is more readable if nothing else.

Pattern matching with identical wildcards

I'm working with PostgreSQL and want to know whether you can have a wildcard retain its value.
So for example say I had
select * from tableOne where field like ‘_DEF_’;
Is there a way to get the first and last wildcard to be the exact same character?
So an example matching result could be: ADEFA or ZDEFZ.
You can use a regular expression with a back-reference:
select *
from some_table
where some_column ~* '^(.)DEF(\1)$'
^(.)DEF(\1)$ means: some character at the beginning followed DEF followed by the first character must occur at the end of the string.
The () defines a group and the \1 references the first group (which is the first character in the input sequence in this example)
SQLFiddle example: http://sqlfiddle.com/#!15/d4c4d/1
Use regular expression:
with test as (
select 'xABa' as foo
union select 'xABx'
union select 'xJBx'
)
select * from test
where foo ~* E'^(.)AB\\1$'
Outputs:
foo
------
xABx
(1 row)

Test for item membership in Postgres json array

Let's say I have a table in Postgres that looks like this - note the zips field is json.
cities
name (text) | zips (json)
San Francisco | [94100, 94101, ...]
Washington DC | [20000, 20001, ...]
Now I want to do something like select * from cities where zip=94101, in other words, testing membership.
I tried using WHERE zips ? '94101' and got operator does not exist: json ? unknown.
I tried using WHERE zips->'94101' but was not sure what to put there, as Postgres complained argument of WHERE must by type boolean, not type json.
What do I want here? How would I solve this for 9.3 and 9.4?
edit Yes, I know I should be using the native array type... the database adapter we are using doesn't support this.
In PostgreSQL 9.4+, you can use #> operator with jsonb type:
create table test (city text, zips jsonb);
insert into test values ('A', '[1, 2, 3]'), ('B', '[4, 5, 6]');
select * from test where zips #> '[1]';
An additional advantage of such approach is 9.4's new GIN indexes that speed up such queries on big tables.
For PostgreSQL 9.4+, you should use json[b]_array_elements_text():
(the containment operator ? does something similar, but for a JSON array, it can only find exact matches, which could only occur, if your array contains strings, not numbers)
create table cities (
city text,
zips jsonb
);
insert into cities (city, zips) values
('Test1', '[123, 234]'),
('Test2', '[234, 345]'),
('Test3', '[345, 456]'),
('Test4', '[456, 123]'),
('Test5', '["123", "note the quotes!"]'),
('Test6', '"123"'), -- this is a string in json(b)
('Test7', '{"123": "this is an object, not an array!"}');
-- select * from cities where zips ? '123';
-- would yield 'Test5', 'Test6' & 'Test7', but none of you want
-- this is a safe solution:
select cities.*
from cities
join jsonb_array_elements_text(
case jsonb_typeof(zips)
when 'array' then zips
else '[]'
end
) zip on zip = '123';
-- but you can use this simplified query, if you are sure,
-- your "zips" column only contains JSON arrays:
select cities.*
from cities
join jsonb_array_elements_text(zips) zip on zip = '123';
For 9.3, you can use json_array_elements() (& convert zips manually to text):
select cities.*
from cities
join json_array_elements(zips) zip on zip::text = '123';
Note: for 9.3, you can't make your query safe (at least easily), you need to store only JSON arrays in the zips column. Also, the query above won't find any string matches, your array elements need to be numbers.
Note 2: for 9.4+ you can use the safe solution with json too (not just with jsonb, but you must call json_typeof(zips) instead of jsonb_typeof(zips)).
Edit: actually, the #> operator is better in PostgreSQL 9.4+, as #Ainar-G mentioned (because it's indexable). A little side-note: it only finds rows, if your column and query both use JSON numbers (or JSON strings, but not mixed).
For 9.3, you can use json_array_elements(). I can't test with jsonb in version 9.4 right now.
create table test (
city varchar(35) primary key,
zips json not null
);
insert into test values
('San Francisco', '[94101, 94102]');
select *
from (
select *, json_array_elements(zips)::text as zip from test
) x
where zip = '94101';