Совпадение не менее 3 слов в любом порядке из 5 слов - PullRequest
0 голосов
/ 03 июня 2018

У меня есть группа слов:

"dog", "car", "house", "work", "cat"

Мне нужно иметь возможность сопоставить хотя бы 3 из них в тексте, например:

"I always let my cat and dog at the animal nursery when I go to work by car"

Здесь я хочучтобы соответствовать регулярному выражению, потому что оно соответствует как минимум 3 словам (здесь 4 слова):

"cat", "dog", "car" and "work"

РЕДАКТИРОВАТЬ 1

Я хочу использовать его с Oracle 's regexp_like функция

РЕДАКТИРОВАТЬ 2

Мне также нужно работать с последовательными словами

Ответы [ 5 ]

0 голосов
/ 03 июня 2018

Следующее решение исключит повторные совпадения, не использует регулярные выражения (хотя вы можете, если хотите) и не использует PL / SQL.

WITH match_list ( match_word ) AS (
    SELECT 'dog' AS match_word FROM dual
     UNION ALL
    SELECT 'work' FROM dual
     UNION ALL
    SELECT 'car' FROM dual
     UNION ALL
    SELECT 'house' FROM dual
     UNION ALL
    SELECT 'cat' FROM dual
)
SELECT phrase, COUNT(*) AS unique_match_cnt, SUM(match_cnt) AS total_match_cnt
     , LISTAGG(match_word, ',') WITHIN GROUP ( ORDER BY match_word ) AS unique_matches
  FROM (
    SELECT pt.phrase, ml.match_word, COUNT(*) AS match_cnt
      FROM phrase_table pt INNER JOIN match_list ml
        ON ' ' || LOWER(pt.phrase) || ' ' LIKE '%' || ml.match_word || '%'
     GROUP BY pt.phrase, ml.match_word
) GROUP BY phrase
HAVING COUNT(*) >= 3;

Ключом является помещение словВы хотите сопоставить таблицу или общее табличное выражение / подзапрос.Если вам нравится, вы можете использовать REGEXP_LIKE() вместо LIKE, хотя я думаю, что это будет дороже.Пропустите LISTAGG(), если вы не используете Oracle 11g или выше, или если вам на самом деле не нужно знать, какие слова были сопоставлены, и пропустите LOWER(), если вы хотите регистрозависимое совпадение.

0 голосов
/ 03 июня 2018

Игнорирование вопросов, которые я задал в комментарии к исходному сообщению, представляет собой один простой способ решения проблемы с объединением и агрегированием (с использованием условия HAVING).Обратите внимание, что слово типа doghouse во входных данных будет совпадать с dog и house и т. Д. (Прочтите мой комментарий под исходным сообщением!)

В приведенном ниже запросе обе входные фразыи слова для сопоставления жестко закодированы в факторизованных подзапросах (предложение WITH).В серьезной среде оба должны быть в базовых таблицах или предоставлены в качестве входных переменных и т. Д.

Я покажу, как использовать стандартный оператор сравнения строк LIKE.Это может быть изменено на REGEXP_LIKE, но это, как правило, не нужно (и действительно плохая идея).Но если вам нужно провести различие между «собакой» и «собакой» (и «кизилом»), или вам необходимо сравнение без учета регистра и т. Д., Вы можете использовать REGEXP_LIKE.Суть этого решения в том, что вам не нужно беспокоиться о совпадении ТРИ разных слова;если вы знаете, как сопоставить ОДНО (требуется ли полное совпадение слов, капитализация имеет или не имеет значения и т. д.), то вы также можете легко сопоставить ТРИ слова по тем же правилам.

with
  inputs ( input_phrase ) as (
    select
  'I always let my cat and dog at the animal nursery when I go to work by car'
    from   dual
  ),
  words ( word_to_match) as (
    select 'dog'   from dual union all
    select 'car'   from dual union all
    select 'house' from dual union all
    select 'work'  from dual union all
    select 'cat'   from dual
  )
select   input_phrase
from     inputs inner join words 
                on input_phrase like '%' || word_to_match || '%'
group by input_phrase
having   count(*) >= 3
;

INPUT_PHRASE                                                              
--------------------------------------------------------------------------
I always let my cat and dog at the animal nursery when I go to work by car
0 голосов
/ 03 июня 2018

Если вам не нужно сопоставлять разные слова.

(?:\b(?:dog|car|house|work|cat)\b.*?){3}

Я не знаю, работает ли это в вашей среде.

РЕДАКТИРОВАТЬ: я не видел тамдругой ответ почти такой же.

0 голосов
/ 03 июня 2018

Это решение, которое не использует регулярные выражения, исключит повторяющиеся слова, и слова для сопоставления могут быть переданы в качестве параметра связывания в коллекции:

SQL Fiddle

Настройка схемы Oracle 11g R2 :

Создание типа коллекции для хранения списка слов:

CREATE TYPE StringList IS TABLE OF VARCHAR2(50)
/

Создание функции PL / SQLчтобы разделить строку с разделителями на коллекцию:

CREATE OR REPLACE FUNCTION split_String(
  i_str    IN  VARCHAR2,
  i_delim  IN  VARCHAR2 DEFAULT ','
) RETURN StringList DETERMINISTIC
AS
  p_result       StringList := StringList();
  p_start        NUMBER(5) := 1;
  p_end          NUMBER(5);
  c_len CONSTANT NUMBER(5) := LENGTH( i_str );
  c_ld  CONSTANT NUMBER(5) := LENGTH( i_delim );
BEGIN
  IF c_len > 0 THEN
    p_end := INSTR( i_str, i_delim, p_start );
    WHILE p_end > 0 LOOP
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );
      p_start := p_end + c_ld;
      p_end := INSTR( i_str, i_delim, p_start );
    END LOOP;
    IF p_start <= c_len + 1 THEN
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );
    END IF;
  END IF;
  RETURN p_result;
END;
/

Создать тестовые данные:

CREATE TABLE test_data ( value ) AS
SELECT 'I always let my cat and dog at the animal nursery when I go to work by car' FROM DUAL UNION ALL
SELECT 'dog dog foo bar dog' FROM DUAL
/

Запрос 1 :

SELECT *
FROM   test_data
WHERE  CARDINALITY(
         split_string( value, ' ' )    -- Split the string into a collection
         MULTISET INTERSECT            -- Intersect it with the input words
         StringList( 'dog', 'car', 'house', 'work', 'cat' )
       ) >= 3                          -- Check that the size of the intersection
                                       -- is at least 3 items.

Результаты :

|                                                                      VALUE |
|----------------------------------------------------------------------------|
| I always let my cat and dog at the animal nursery when I go to work by car |
0 голосов
/ 03 июня 2018

Поскольку Oracle regexp_like не поддерживает группы без захвата и границы слов, можно использовать следующее выражение:

^((.*? )?(dog|car|house|work|cat)( |$)){3}.*$

Попробуйте это здесь .

В качестве альтернативы можно использовать более крупное, но, возможно, более чистое решение:

^(.*? )?(dog|car|house|work|cat) .*?(dog|car|house|work|cat) .*?(dog|car|house|work|cat)( .*)?$

Попробуйте это здесь .

ПРИМЕЧАНИЕ. Оба эти слова будут совпадать с одним и тем же словом, использованным нескольковремя, например "собака собака собака".

РЕДАКТИРОВАТЬ: Чтобы решить проблемы с пунктуацией, может быть сделано небольшое изменение.Это не идеально, но должно соответствовать 99% ситуаций, связанных с пунктуацией (но не будет соответствовать, например, !dog):

^((.*? )?(dog|car|house|work|cat)([ ,.!?]|$)){3}.*$

Попробуйте здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...