Primeiro, você deve perceber que o suporte RDBMS para indexação de texto completo é um hack para forçar uma tecnologia projetada para permitir acesso eficiente a dados estruturados para lidar com texto não estruturado. (Sim, isso é apenas minha opinião. Se necessário, posso defendê-lo, pois entendo extremamente bem as duas tecnologias.;)
Então, o que pode ser feito para melhorar o desempenho da pesquisa?
Opção um - "A melhor ferramenta para a tarefa"
A melhor maneira de lidar com a pesquisa de texto completo em um corpus de documentos é usar tecnologia projetada especificamente para isso, como SOLR (Lucene) do Apache ou Sphinx de err, Esfinge.
Por razões que ficarão claras a seguir, recomendo fortemente essa abordagem.
Opção 2 - Pré-carregue seus resultados
Ao construir soluções de pesquisa baseadas em texto, a abordagem usual é indexar todos os documentos em um único índice pesquisável e, embora isso possa ser o mais conveniente, não é a única abordagem.
Supondo que o que você está procurando possa ser facilmente quantificado em um conjunto de regras conhecidas, você pode oferecer mais um estilo de pesquisa "guiado" do que simplesmente texto completo não qualificado. O que quero dizer com isso é que, se o seu aplicativo puder se beneficiar da associação de usuários aos resultados, você poderá pré-carregar vários conjuntos de resultados com base em um conjunto conhecido de regras em suas próprias tabelas e, assim, reduzir o volume de dados a serem pesquisados.
Se você espera que a maioria de seus usuários se beneficie de um conjunto conhecido de termos de pesquisa em uma ordem conhecida, você pode construir sua interface de pesquisa para favorecer esses termos.
Portanto, supondo que a maioria dos usuários esteja procurando por uma variedade de automóveis, você pode oferecer pesquisas predefinidas com base em modelo, ano, condição etc. Sua interface de pesquisa seria criada como uma série de menus suspensos para "guiar" os usuários a resultados específicos.
Ou se a maioria das pesquisas for para um tópico principal específico (digamos, 'automóveis'), você pode predefinir uma tabela apenas dos registros que você identificou anteriormente como relacionados a automóveis.
Ambas as abordagens reduziriam o número de registros a serem pesquisados e, assim, aumentariam os tempos de resposta.
Opção três - "Faça você mesmo"
Se você não puder integrar uma tecnologia de pesquisa externa ao seu projeto e o pré-carregamento não for uma opção, ainda há maneiras de melhorar muito os tempos de resposta das consultas de pesquisa, mas eles diferem com base no que você precisa realizar e como espera que as pesquisas sejam realizadas .
Se você espera que os usuários pesquisem usando palavras-chave ou frases únicas e relações booleanas entre elas, considere construir seu próprio 'índice invertido ' do seu corpus. (Isto é o que a Pesquisa Booleana de Texto Completo do MySQL já faz, mas fazer você mesmo permite maior controle sobre a velocidade e a precisão da pesquisa.)
Para construir um índice invertido a partir de seus dados existentes:
Etapa 1. Crie três tabelas
// dict - a dictionary containing one row per unique word in corpus create table dict ( id int primary key, word varchar ) // invert - an inverted_index to map words to records in corpus create table invert ( id int primary key, rec_id int, word_id int ) // stopwords - to contain words to ignore when indexing (like a, an, the, etc) create table stopwords ( id int primary key, word varchar )
Nota:Este é apenas um esboço. Você vai querer adicionar índices e restrições, etc. quando você realmente criar essas tabelas.
A tabela de palavras irrelevantes é usada para reduzir o tamanho do seu índice apenas para aquelas palavras que importam para as consultas esperadas dos usuários. Por exemplo, raramente é útil indexar artigos em inglês, como 'a', 'an', 'the', pois eles não contribuem com significado útil para pesquisas de palavras-chave.
Normalmente, você precisará de uma lista de palavras irrelevantes criada especificamente às necessidades da sua aplicação. Se você nunca espera que os usuários incluam os termos 'vermelho', 'branco' ou 'azul' em suas consultas ou se esses termos aparecem em todas registro pesquisável, você gostaria de adicioná-los à sua lista de palavras irrelevantes.
Veja a nota no final desta mensagem para instruções sobre como usar sua própria lista de palavras irrelevantes no MySQL.
Veja também:
-
A lista atual de palavras irrelevantes suportadas no MySQL
-
Uma boa lista de palavras irrelevantes em inglês
Etapa 2. Construir o índice invertido
Para construir um índice invertido de seus registros existentes, você precisará (pseudo-código):
foreach( word(w) in record(r) ) { if(w is not in stopwords) { if( w does not exist in dictionary) { insert w to dictionary at w.id } insert (r.id, w.id) into inverted_index } }Mais sobre palavras irrelevantes:
Em vez de usar uma lista específica de palavras irrelevantes, o teste 'if(w não está em palavras irrelevantes)' pode tomar outras decisões em vez de ou como um complemento à sua lista de palavras inaceitáveis.
Seu aplicativo pode querer filtrar todas as palavras com menos de 4 caracteres ou apenas incluir palavras de um conjunto predefinido.
Ao criar seu próprio índice invertido, você obtém um controle muito maior e mais refinado sobre a pesquisa.
Etapa 3. Consultar o índice invertido usando SQL
Essa etapa realmente depende de como você espera que as consultas sejam enviadas ao seu índice.
Se as consultas devem ser 'codificadas', você pode simplesmente criar a instrução select ou, se precisar dar suporte a consultas inseridas pelo usuário, precisará converter qualquer linguagem de consulta escolhida em uma instrução SQL (geralmente feita usando um analisador simples).
Supondo que você deseja recuperar todos os documentos correspondentes à consulta lógica '(word1 AND word2) OR word3', uma abordagem possível pode ser:
CREATE TEMPORARY TABLE temp_results ( rec_id int, count int ) AS
( SELECT rec_id, COUNT(rec_id) AS count
FROM invert AS I, dict AS D
WHERE I.word_id=D.id AND (D.word='word1' OR D.word='word2')
GROUP BY I.rec_id
HAVING count=2
)
UNION (
SELECT rec_id, 1 AS count
FROM invert AS I, dict AS D
WHERE I.word_id=D.id AND D.word='word3'
);
SELECT DISTINCT rec_id FROM temp_results;
DROP TABLE temp_results;
NOTA:Esta é apenas uma primeira passagem em cima da minha cabeça. Estou confiante de que existem maneiras mais eficientes de converter uma expressão de consulta booleana em uma instrução SQL eficiente e aceito todas e quaisquer sugestões de melhoria.
Para pesquisar frases, você precisará adicionar um campo ao índice invertido para representar a posição em que a palavra apareceu em seu registro e fatorar isso em seu SELECT.
E, finalmente, você precisará atualizar seu índice invertido à medida que adiciona novos registros ou exclui os antigos.
Palavra Final
"Full text search" se enquadra em uma área muito grande de pesquisa conhecida como "Information Retrieval" ou RI e há muitos livros sobre o assunto, incluindo
-
Recuperação de informações:implementação e avaliação de mecanismos de pesquisa por Stefan Büttcher, Charles L. A. Clarke e Gordon V. Cormack (23 de julho de 2010)
-
Mecanismos de pesquisa:recuperação de informações na prática por Bruce Croft, Donald Metzler e Trevor Strohman (16 de fevereiro de 2009)
-
Criando aplicativos de pesquisa:Lucene, LingPipe e Gate por Manu Konchady (junho de 2008)
Verifique a Amazon para mais.
Observações
Como usar sua própria lista de palavras irrelevantes no MySQL
Para usar sua própria lista de palavras irrelevantes no MySQL:
- Crie sua própria lista de palavras irrelevantes, uma palavra por linha, e salve-a em um local conhecido em seu servidor, por exemplo:/usr/local/lib/IR/stopwords.txt
- Edite my.cnf para adicionar ou atualizar as seguintes linhas:
[mysqld] ft_min_word_len=1 ft_max_word_len=40 ft_stopword_file=/usr/local/lib/IR/stopwords.txt
que irá definir o comprimento mínimo e máximo das palavras legais para 1 e 40, respectivamente, e informará ao mysqld onde encontrar sua lista personalizada de palavras irrelevantes.
(Nota:o padrão ft_max_word_len é 84, o que acredito ser muito excessivo e pode fazer com que execuções de strings que não sejam palavras reais sejam indexadas.)
- Reiniciar o mysqld
- Solte e recrie todos os índices relacionados ao texto completo