Files
agent-ai-mcp-server/source/fn_busca_fonetica_produtos.sql
2025-05-13 10:14:15 -03:00

124 lines
3.7 KiB
MySQL

CREATE TABLE produtos (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
codigo VARCHAR2(50),
descricao VARCHAR2(4000)
);
CREATE INDEX idx_texto_descricao ON produtos(descricao)
INDEXTYPE IS CTXSYS.CONTEXT;
-- DROP TYPES (se existirem)
BEGIN
EXECUTE IMMEDIATE 'DROP TYPE produto_resultado_tab';
EXECUTE IMMEDIATE 'DROP TYPE produto_resultado';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
/
-- Criação de um tipo de tabela para retorno da função
CREATE OR REPLACE TYPE produto_resultado AS OBJECT (
codigo VARCHAR2(50),
descricao VARCHAR2(4000),
similaridade NUMBER
);
CREATE OR REPLACE TYPE produto_resultado_tab AS TABLE OF produto_resultado;
/
-- Função que faz busca por palavras aproximadas por fonética ou distância
-- Criação de um tipo de tabela para retorno da função
CREATE OR REPLACE TYPE produto_resultado AS OBJECT (
codigo VARCHAR2(50),
descricao VARCHAR2(4000),
similaridade NUMBER
);
CREATE OR REPLACE TYPE produto_resultado_tab AS TABLE OF produto_resultado;
/
-- Função de busca fonética e por palavras-chave
CREATE OR REPLACE FUNCTION fn_busca_avancada(p_termos IN VARCHAR2)
RETURN produto_resultado_tab PIPELINED
AS
v_termos SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
v_token VARCHAR2(1000);
v_descricao VARCHAR2(4000);
v_score NUMBER;
v_dummy NUMBER;
BEGIN
-- Dividir os termos da busca
FOR i IN 1..REGEXP_COUNT(p_termos, '\S+') LOOP
v_termos.EXTEND;
v_termos(i) := LOWER(REGEXP_SUBSTR(p_termos, '\S+', 1, i));
END LOOP;
-- Loop pelos produtos
FOR prod IN (SELECT codigo, descricao FROM produtos) LOOP
v_descricao := LOWER(prod.descricao);
v_score := 0;
-- Avaliar cada termo da busca
FOR i IN 1..v_termos.COUNT LOOP
v_token := v_termos(i);
-- 3 pontos se encontrar diretamente
IF v_descricao LIKE '%' || v_token || '%' THEN
v_score := v_score + 3;
ELSE
-- 2 pontos se foneticamente similar
BEGIN
SELECT 1 INTO v_dummy FROM dual
WHERE SOUNDEX(v_token) IN (
SELECT SOUNDEX(REGEXP_SUBSTR(v_descricao, '\w+', 1, LEVEL))
FROM dual
CONNECT BY LEVEL <= REGEXP_COUNT(v_descricao, '\w+')
);
v_score := v_score + 2;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
END;
-- 1 ponto se similar por escrita
BEGIN
SELECT 1 INTO v_dummy FROM dual
WHERE EXISTS (
SELECT 1
FROM (
SELECT REGEXP_SUBSTR(v_descricao, '\w+', 1, LEVEL) AS palavra
FROM dual
CONNECT BY LEVEL <= REGEXP_COUNT(v_descricao, '\w+')
)
WHERE UTL_MATCH.EDIT_DISTANCE(palavra, v_token) <= 2
);
v_score := v_score + 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
END;
END IF;
END LOOP;
-- Só retorna se houver ao menos algum match
IF v_score > 0 THEN
PIPE ROW(produto_resultado(prod.codigo, prod.descricao, v_score));
END IF;
END LOOP;
RETURN;
END;
/
-- Grant para execução, se necessário:
GRANT EXECUTE ON fn_busca_fonetica_por_palavra TO PUBLIC;
-- Testes
SELECT *
FROM TABLE(fn_busca_avancada('harry poter pedra'))
ORDER BY similaridade DESC;
SELECT * FROM TABLE(fn_busca_fonetica_por_palavra('velho mar'))
ORDER BY similaridade DESC;