Как искать в массиве с оператором LIKE - PullRequest
0 голосов
/ 02 апреля 2019
 id |   name   |          ipAddress           
----+----------+-------------------------
  1 | testname | {192.168.1.60,192.168.1.65}

Я хочу найти ipAddress с LIKE.Я попытался:

{'$mac_ip_addresses.ip_address$': { [OP.contains]: [searchItem]}},

Этот также:

{'$mac_ip_addresses.ip_address$': { [OP.Like] : { [OP.any]: [searchItem]}}},

Тип данных ipAddress равен text[].Я хочу искать в ipAddress с LIKE.searchItem содержит IP, который нужно искать в поле ipAddress, поэтому я хочу искать в массиве с помощью LIKE.

Ответы [ 2 ]

1 голос
/ 03 апреля 2019

что вы спросили

~~ - это оператор, используемый для реализации SQL LIKE. Для него нет коммутатора - нет оператора, который работает с переключенным левым и правым операндами. Это то, что вам нужно для вашей попытки использовать ANY конструкцию с шаблоном к слева . Связанный:

Вы можете создать оператор , и все довольно просто:

CREATE OR REPLACE FUNCTION reverse_like (text, text)
  RETURNS boolean LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
'SELECT $2 LIKE $1';

CREATE OPERATOR <~~ (function = reverse_like, leftarg = text, rightarg = text);

Вдохновленный идеей Джеффа Джейнса здесь:

Тогда ваш запрос может иметь шаблон для слева оператора:

SELECT *
FROM   mac_ip_addresses
WHERE  '192.168.2%.255' <~~ ANY (ipaddress);

Простой, но значительно медленнее , чем EXISTS выражение , продемонстрированное filiprem .

Опять же, любой запрос мучительно медленен для больших таблиц , поскольку ни один из них не может использовать index . Нормализованный дизайн БД с таблицей n: 1, содержащей по одному IP, позволил бы это. Это также заняло бы в несколько раз больше места на диске. Тем не менее, гораздо более чистая реализация ...

Пока вы застряли в своем текущем дизайне, есть еще способ: создать индекс GIN триграммы в текстовом представлении массива и добавить избыточный предикат «sargable» к запросу дополнительно. Смущенный? Вот рецепт:

Во-первых, индексы триграмм? Прочтите это, если вы не знакомы:

Ни приведенные от text[] до text, ни array_to_string() не являются неизменными . Но нам это нужно для индекса выражения. Короче говоря, подделайте его с помощью неизменной функции-обертки:

CREATE OR REPLACE FUNCTION f_textarr2text(text[]) 
  RETURNS text LANGUAGE sql IMMUTABLE AS $$SELECT array_to_string($1, ',')$$;

CREATE INDEX iparr_trigram_idx ON iparr
USING gin (f_textarr2text(iparr) gin_trgm_ops);

Связанный ответ с длинной историей (и почему это безопасно):

Тогда ваш запрос может быть:

SELECT *
FROM   mac_ip_addresses
WHERE  NOT ('192.168.9%.255' <~~ ANY (ipaddress))
AND    f_textarr2text(ipaddress) LIKE '192.168.9%.255';  -- logically redundant

Добавленный предикат логически избыточен, но может задействовать всю мощь индекса триграммы.
Намного быстрее для больших столов. Еще немного быстрее, но:

SELECT *
FROM   mac_ip_addresses
WHERE  EXISTS (SELECT FROM unnest(ipaddress) ip WHERE ip LIKE '192.168.9%.255')
AND    f_textarr2text(ipaddress) LIKE '192.168.9%.255';

Но теперь это незначительно.

дБ <> скрипка здесь

Я ответил на заданный вопрос, когда проявил интерес. Может представлять интерес для широкой публики. Скорее всего, не то, что вам нужно , хотя.

Что вам нужно нужно

Я хочу искать в ipAddress с LIKE. searchItem содержит IP, который нужно искать в поле ipAddress, поэтому я хочу искать в массиве с помощью LIKE.

Это, вероятно, следует читать:

"Я хочу найти заданный IP-адрес (searchItem) в массиве ipAddress. Моя первая идея - использовать LIKE ..."

Ну, LIKE для сопоставления с образцом. Чтобы найти полный IP-адрес в массиве, это неправильный инструмент. Второй запрос filiprem с операторами массива - это путь. Наверное, достаточно хорошо.

Было бы лучше использовать встроенный тип данных cidr вместо text. И тип данных ip4 дополнительного модуля ip4r был бы гораздо лучше. Все в сочетании со стандартными операторами массива, как показано.

Наконец, преобразование адресов IPv4 в integer и использование его с дополнительным модулем inrarray должно быть звездным - что касается производительности.

1 голос
/ 02 апреля 2019

Я не знаю Sequelize, но могу ответить со стороны postgres.

В PostgreSQL нет короткого синтаксиса для поиска шаблона внутри массива.

Если вы хотите проверить шаблон для каждого элемента массива в отдельности, то вам нужно развернуть массив, используя unnest:

SELECT id, name, ipaddress
FROM testing
WHERE EXISTS (
  SELECT 1 FROM unnest(ipaddress) AS ip
  WHERE ip LIKE '8.8.8.%'
);

Если массив часто ищется таким образом, лучше хранить данные в нормализованном виде.

Однако существует короткий синтаксис (плюс поддержка индекса GIN) для поиска на основе равенства (см. @> и другие операторы здесь ).

SELECT id, name, ipaddress
FROM testing
WHERE ipaddress @> ARRAY['8.8.8.8'];
...