Aqui estão algumas "regras do jogo" que você deve ter em mente para resolver este problema. Você provavelmente já os conhece, mas declará-los claramente pode ajudar a confirmar para outros leitores.
- Todos os índices no MySQL podem referenciar apenas colunas em uma única tabela base. Você não pode criar um índice de texto completo que indexe em várias tabelas.
- Você não pode definir índices para visualizações, apenas tabelas base.
- Um
MATCH()
a consulta em um índice de texto completo deve corresponder a todas as colunas no índice de texto completo, na ordem declarada no índice.
Eu criaria uma terceira tabela para armazenar o conteúdo que você deseja indexar. Não há necessidade de armazenar este conteúdo de forma redundante - armazene-o apenas na terceira tabela. Isso toma emprestado um conceito de "superclasse comum" do design orientado a objetos (na medida em que podemos aplicá-lo ao design RDBMS).
CREATE TABLE Searchable (
`id` SERIAL PRIMARY KEY,
`title` varchar(100) default NULL,
`description` text,
`keywords` text,
`url` varchar(255) default '',
FULLTEXT KEY `TitleDescFullText` (`keywords`,`title`,`description`,`url`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shopitems` (
`id` INT UNSIGNED NOT NULL,
`ShopID` INT UNSIGNED NOT NULL,
`ImageID` INT UNSIGNED NOT NULL,
`pricing` varchar(45) NOT NULL,
`datetime_created` datetime NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shops` (
`id` INT UNSIGNED NOT NULL,
`owner_id` varchar(255) default NULL,
`datetime_created` datetime default NULL,
`created_by` varchar(255) default NULL,
`datetime_modified` datetime default NULL,
`modified_by` varchar(255) default NULL,
`overall_rating_avg` decimal(4,2) default '0.00',
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Observe que a única tabela com uma chave de incremento automático agora é
Searchable
. As mesas shops
e shopitems
use uma chave com um tipo de dados compatível, mas não com incremento automático. Então você deve criar uma linha em Searchable
para gerar o id
value, antes de criar a linha correspondente em shops
ou shopitems
. Adicionei
FOREIGN KEY
declarações para fins de ilustração, mesmo que o MyISAM ignore silenciosamente essas restrições (e você já sabe que deve usar o MyISAM para ter suporte para indexação de texto completo). Agora você pode pesquisar o conteúdo textual de ambas as
shops
e shopitems
em uma única consulta, usando um único índice de texto completo:SELECT S.*, sh.*, si.*,
MATCH(keywords, title, description, url) AGAINST('dummy') As score
FROM Searchable S
LEFT OUTER JOIN shops sh ON (S.id = sh.id)
LEFT OUTER JOIN shopitems si ON (S.id = si.id)
WHERE MATCH(keywords, title, description, url) AGAINST('dummy')
ORDER BY score DESC;
Claro, para uma determinada linha em
Searchable
apenas uma tabela deve corresponder, lojas ou itens de loja, e essas tabelas têm colunas diferentes. Portanto, sh.*
ou si.*
será NULL no resultado. Cabe a você formatar a saída em seu aplicativo. Algumas outras respostas sugeriram o uso da Pesquisa Sphinx . Essa é outra tecnologia que complementa o MySQL e adiciona recursos de pesquisa de texto completo mais sofisticados. Ele tem um ótimo desempenho para consultas, então algumas pessoas ficaram bastante encantadas com ele.
Mas criar índices e especialmente adicionar a um índice de forma incremental é caro. Na verdade, atualizar um índice do Sphinx Search é tão caro que a solução recomendada é criar um índice para dados arquivados mais antigos e outro índice menor para dados recentes com maior probabilidade de serem atualizados. Em seguida, cada pesquisa deve executar duas consultas, em relação aos dois índices separados. E se seus dados não se prestam naturalmente ao padrão de dados mais antigos serem imutáveis, talvez você não consiga aproveitar esse truque de qualquer maneira.
Re seu comentário:Aqui está um trecho da documentação do Sphinx Search sobre atualizações ao vivo para um índice:
A ideia é que, como é caro atualizar um índice do Sphinx Search, a solução deles é tornar o índice atualizado o menor possível. Para que apenas as postagens mais recentes do fórum (no exemplo deles), enquanto o histórico maior de postagens arquivadas do fórum nunca mude, você cria um segundo índice maior para essa coleção uma vez. Claro que se você quiser fazer uma pesquisa, você tem que consultar os dois índices.
Periodicamente, digamos uma vez por semana, as mensagens "recentes" do fórum seriam consideradas "arquivadas" e você teria que mesclar o índice atual de postagens recentes ao índice arquivado e iniciar o índice menor novamente. Eles afirmam que mesclar dois índices do Sphinx Search é mais eficiente do que reindexar após uma atualização dos dados.
Mas meu ponto é que nem todo conjunto de dados naturalmente se enquadra no padrão de ter um conjunto de dados arquivados que nunca muda, em comparação com dados recentes que são atualizados com frequência.
Pegue seu banco de dados, por exemplo:você tem lojas e itens de loja. Como você pode separá-los em linhas que nunca mudam, versus novas linhas? Quaisquer lojas ou produtos no catálogo devem ter permissão para atualizar sua descrição. Mas como isso exigiria a reconstrução de todo o índice Sphinx Search toda vez que você fizer uma alteração, torna-se uma operação muito cara. Talvez você enfileirasse as alterações e as aplicasse em um lote, reconstruindo o índice uma vez por semana. Mas tente explicar aos vendedores da loja por que uma pequena alteração na descrição da loja não entrará em vigor até domingo à noite.