Oto kilka „zasad gry”, o których musisz pamiętać, aby rozwiązać ten problem. Prawdopodobnie już je znasz, ale wyraźne ich podanie może pomóc innym czytelnikom potwierdzić.
- Wszystkie indeksy w MySQL mogą odwoływać się tylko do kolumn w jednej tabeli bazowej. Nie możesz utworzyć indeksu pełnotekstowego, który indeksuje wiele tabel.
- Nie możesz definiować indeksów dla widoków, tylko tabele bazowe.
MATCH()
zapytanie względem indeksu pełnotekstowego musi być zgodne ze wszystkimi kolumnami w indeksie pełnotekstowym, w kolejności zadeklarowanej w indeksie.
Utworzyłbym trzecią tabelę do przechowywania zawartości, którą chcesz zindeksować. Nie ma potrzeby przechowywania tej zawartości nadmiarowo — przechowuj ją wyłącznie w trzeciej tabeli. To zapożycza koncepcję „powszechnej superklasy” z projektowania obiektowego (o ile możemy ją zastosować do projektowania 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;
Zwróć uwagę, że jedyna tabela z kluczem automatycznego zwiększania jest teraz Searchable
. Stoły shops
i shopitems
użyj klucza ze zgodnym typem danych, ale nie z automatycznym przyrostem. Musisz więc utworzyć wiersz w Searchable
aby wygenerować id
wartość, zanim będziesz mógł utworzyć odpowiedni wiersz w jednym z shops
lub shopitems
.
Dodałem FOREIGN KEY
deklaracje w celach ilustracyjnych, nawet jeśli MyISAM po cichu zignoruje te ograniczenia (i już wiesz, że musisz użyć MyISAM, aby mieć obsługę indeksowania pełnotekstowego).
Teraz możesz przeszukiwać zawartość tekstową obu shops
i shopitems
w jednym zapytaniu, używając jednego indeksu pełnotekstowego:
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;
Oczywiście dla danego wiersza w Searchable
tylko jedna tabela powinna pasować, sklepy lub przedmioty z zakupów, a te tabele mają różne kolumny. Więc albo sh.*
lub si.*
w wyniku będzie NULL. To do Ciebie należy sformatowanie danych wyjściowych w Twojej aplikacji.
Kilka innych odpowiedzi sugerowało użycie Sphinx Search . Jest to kolejna technologia, która uzupełnia MySQL i dodaje bardziej wyrafinowane możliwości wyszukiwania pełnotekstowego. Ma świetną wydajność w przypadku zapytań, więc niektórzy ludzie są nim całkiem zachwyceni.
Jednak tworzenie indeksów, a zwłaszcza stopniowe dodawanie do indeksu, jest kosztowne. W rzeczywistości aktualizacja indeksu Sphinx Search jest tak kosztowna, że zalecanym rozwiązaniem jest utworzenie jednego indeksu dla starszych, zarchiwizowanych danych i innego mniejszego indeksu dla najnowszych danych, które z większym prawdopodobieństwem zostaną zaktualizowane. Następnie każde wyszukiwanie musi uruchamiać dwa zapytania w dwóch oddzielnych indeksach. A jeśli Twoje dane w naturalny sposób nie pasują do wzorca, w jakim starsze dane pozostają niezmienne, i tak możesz nie być w stanie skorzystać z tej sztuczki.
Twój komentarz:Oto fragment dokumentacji Sphinx Search o aktualizacjach indeksu na żywo:
Chodzi o to, że ponieważ aktualizacja indeksu Sphinx Search jest kosztowna, ich rozwiązaniem jest sprawienie, aby aktualizowany indeks był jak najmniejszy. Aby tylko najnowsze posty na forum (w ich przykładzie), podczas gdy większa historia zarchiwizowanych postów na forum nigdy się nie zmieniała, raz tworzysz drugi, większy indeks dla tej kolekcji. Oczywiście, jeśli chcesz przeprowadzić wyszukiwanie, musisz zapytać o oba indeksy.
Od czasu do czasu, powiedzmy raz w tygodniu, „ostatnie” wiadomości na forum będą uważane za „zarchiwizowane” i trzeba będzie scalić bieżący indeks ostatnich postów z indeksem zarchiwizowanym i zacząć od początku mniejszy indeks. Zwracają uwagę, że połączenie dwóch indeksów Sphinx Search jest bardziej wydajne niż ponowne indeksowanie po aktualizacji danych.
Ale chodzi mi o to, że nie każdy zestaw danych naturalnie wpisuje się w schemat zarchiwizowanego zestawu danych, który nigdy się nie zmienia, w porównaniu z ostatnimi danymi, które często się aktualizują.
Weźmy na przykład swoją bazę danych:masz sklepy i przedmioty na zakupy. Jak możesz podzielić je na rzędy, które nigdy się nie zmieniają, w porównaniu z nowymi rzędami? Wszelkie sklepy lub produkty znajdujące się w katalogu powinny mieć możliwość aktualizacji ich opisu. Ale ponieważ wymagałoby to przebudowy całego indeksu Sphinx Search za każdym razem, gdy dokonujesz zmiany, staje się to bardzo kosztowną operacją. Być może umieściłbyś zmiany w kolejce i stosowałbyś je partiami, odbudowując indeks raz w tygodniu. Spróbuj jednak wyjaśnić sprzedawcom, dlaczego niewielka zmiana w opisie ich sklepu wejdzie w życie dopiero w niedzielę wieczorem.