Hier zijn een paar "spelregels" die u in gedachten moet houden om dit probleem op te lossen. Je kent deze waarschijnlijk al, maar als je ze duidelijk vermeldt, kan dit voor andere lezers helpen bevestigen.
- Alle indexen in MySQL kunnen alleen verwijzen naar kolommen in een enkele basistabel. Je kunt geen fulltext index maken die indexeert over meerdere tabellen.
- U kunt geen indexen definiëren voor weergaven, alleen basistabellen.
- Een
MATCH()
zoekopdracht tegen een volledige tekstindex moet overeenkomen met alle kolommen in de volledige tekstindex, in de volgorde die in de index is aangegeven.
Ik zou een derde tabel maken om de inhoud op te slaan die u wilt indexeren. Het is niet nodig om deze inhoud redundant op te slaan -- sla het alleen op in de derde tabel. Dit ontleent een concept van een "gewone superklasse" aan objectgeoriënteerd ontwerp (voor zover we het kunnen toepassen op RDBMS-ontwerp).
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;
Merk op dat de enige tabel met een auto-increment-sleutel nu Searchable
is . De tafels shops
en shopitems
gebruik een sleutel met een compatibel gegevenstype, maar niet automatisch verhogen. U moet dus een rij maken in Searchable
om de id
te genereren waarde, voordat u de corresponderende rij kunt maken in een van beide shops
of shopitems
.
Ik heb FOREIGN KEY
toegevoegd verklaringen ter illustratie, hoewel MyISAM deze beperkingen stilzwijgend negeert (en u weet al dat u MyISAM moet gebruiken om ondersteuning voor fulltext-indexering te hebben).
Nu kunt u zoeken in de tekstuele inhoud van beide shops
en shopitems
in een enkele zoekopdracht, met behulp van een enkele volledige tekstindex:
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;
Natuurlijk, voor een bepaalde rij in Searchable
er mag slechts één tabel overeenkomen, ofwel winkels ofwel winkelitems, en deze tabellen hebben verschillende kolommen. Dus ofwel sh.*
of si.*
zal NULL zijn in het resultaat. Het is aan jou om de uitvoer in je applicatie te formatteren.
Een paar andere antwoorden hebben voorgesteld om Sphinx Search te gebruiken . Dit is een andere technologie die MySQL aanvult en geavanceerdere full-text zoekmogelijkheden toevoegt. Het heeft geweldige prestaties voor zoekopdrachten, dus sommige mensen zijn er behoorlijk door gecharmeerd.
Maar indexen maken en vooral incrementeel toevoegen aan een index is duur. Het bijwerken van een Sphinx-zoekindex is zelfs zo kostbaar dat de aanbevolen oplossing is om één index te maken voor oudere, gearchiveerde gegevens en een andere kleinere index voor recente gegevens waarvan de kans groter is dat ze worden bijgewerkt. Vervolgens moet elke zoekopdracht twee zoekopdrachten uitvoeren, tegen de twee afzonderlijke indexen. En als uw gegevens zich niet van nature lenen voor het onveranderlijke patroon van oudere gegevens, kunt u deze truc misschien toch niet benutten.
Over uw opmerking:hier is een fragment uit de Sphinx Search-documentatie over live updates van een index:
Het idee is dat, aangezien het duur is om een Sphinx Search-index bij te werken, hun oplossing is om de index die u bijwerkt zo klein mogelijk te maken. Zodat alleen de meest recente forumberichten (in hun voorbeeld), terwijl de grotere geschiedenis van gearchiveerde forumberichten nooit verandert, dus je bouwt één keer een tweede, grotere index voor die verzameling. Als je wilt zoeken, moet je natuurlijk beide indexen doorzoeken.
Periodiek, bijvoorbeeld een keer per week, worden de "recente" forumberichten als "gearchiveerd" beschouwd en moet u de huidige index voor recente berichten samenvoegen met de gearchiveerde index en de kleinere index opnieuw beginnen. Ze maken wel duidelijk dat het samenvoegen van twee Sphinx Search-indexen efficiënter is dan opnieuw indexeren na een update van de gegevens.
Maar mijn punt is dat niet elke dataset van nature valt in het patroon van het hebben van een gearchiveerde set data die nooit verandert, versus recente data die regelmatig wordt bijgewerkt.
Neem bijvoorbeeld je database:je hebt winkels en shopitems. Hoe kun je deze scheiden in rijen die nooit veranderen, versus nieuwe rijen? Alle winkels of producten in de catalogus moeten de mogelijkheid hebben om hun beschrijving bij te werken. Maar aangezien dat elke keer dat u een wijziging aanbrengt de hele Sphinx Search-index opnieuw zou moeten worden opgebouwd, wordt het een erg dure operatie. Misschien zou u wijzigingen in de wachtrij plaatsen en ze in een batch toepassen, waarbij u de index eenmaal per week opnieuw opbouwt. Maar probeer de winkelverkopers uit te leggen waarom een kleine wijziging in hun winkelbeschrijving pas zondagavond van kracht wordt.