Ето няколко „правила на играта“, които трябва да имате предвид, за да разрешите този проблем. Вероятно вече ги знаете, но ясното им посочване може да помогне за потвърждаването на други читатели.
- Всички индекси в MySQL могат да препращат само към колони в една базова таблица. Не можете да направите пълнотекстов индекс, който да индексира в множество таблици.
- Не можете да дефинирате индекси за изгледи, а само основни таблици.
- А
MATCH()
заявката към пълнотекстов индекс трябва да съвпада с всички колони в пълнотекстовия индекс в реда, деклариран в индекса.
Бих създал трета таблица за съхраняване на съдържанието, което искате да индексирате. Няма нужда да съхранявате това съдържание излишно - съхранявайте го единствено в третата таблица. Това заимства концепцията за "общ суперклас" от обектно-ориентирания дизайн (доколкото можем да го приложим към дизайна на 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;
Забележете, че единствената таблица с ключ за автоматично увеличение вече е Searchable
. Таблиците shops
и shopitems
използвайте ключ със съвместим тип данни, но не и автоматично увеличение. Така че трябва да създадете ред в Searchable
за генериране на id
стойност, преди да можете да създадете съответния ред в shops
или shopitems
.
Добавих FOREIGN KEY
декларации за илюстративни цели, въпреки че MyISAM тихо ще игнорира тези ограничения (и вече знаете, че трябва да използвате MyISAM, за да имате поддръжка за пълнотекстово индексиране).
Сега можете да търсите в текстовото съдържание и на двата shops
и shopitems
в една заявка, като се използва един индекс на пълен текст:
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;
Разбира се, за даден ред в Searchable
само една таблица трябва да съвпада, или магазини, или shopitems, и тези таблици имат различни колони. Така че или sh.*
или si.*
ще бъде NULL в резултата. От вас зависи да форматирате изхода във вашето приложение.
Няколко други отговора предлагат използването на Sphinx Search . Това е друга технология, която допълва MySQL и добавя по-сложни възможности за търсене в пълен текст. Той има страхотна производителност за заявки, така че някои хора са се очаровали от него.
Но създаването на индекси и особено постепенното добавяне към индекс е скъпо. Всъщност актуализирането на индекс за търсене на Sphinx е толкова скъпо, че препоръчителното решение е да се създаде един индекс за по-стари, архивирани данни и друг по-малък индекс за скорошни данни, който е по-вероятно да бъде актуализиран. Тогава всяко търсене трябва да изпълни две заявки срещу двата отделни индекса. И ако вашите данни не се поддават естествено на модела на по-стари данни да не се променят, тогава може да не сте в състояние да се възползвате от този трик така или иначе.
Относно вашия коментар:Ето откъс от документацията за търсене на Sphinx относно актуализациите на живо на индекс:
Идеята е, че тъй като е скъпо да актуализирате индекс за търсене на Sphinx, тяхното решение е да направят индекса, който актуализирате, възможно най-малък. Така че само най-новите публикации във форума (в техния пример), докато по-голямата история на архивираните форумни публикации никога не се променя, така че изграждате втори, по-голям индекс за тази колекция веднъж. Разбира се, ако искате да направите търсене, трябва да направите заявка и в двата индекса.
Периодично, да речем веднъж седмично, "последните" съобщения във форума ще се считат за "архивирани" и ще трябва да обедините текущия индекс за последните публикации с архивирания индекс и да започнете по-малкия индекс отначало. Те подчертават, че обединяването на два индекса на Sphinx Search е по-ефективно от повторното индексиране след актуализация на данните.
Но моята гледна точка е, че не всеки набор от данни естествено попада в модела на архивиран набор от данни, който никога не се променя, в сравнение с последните данни, които се актуализират често.
Вземете например вашата база данни:Имате магазини и shopitems. Как можете да ги разделите на редове, които никога не се променят, спрямо нови редове? На всички магазини или продукти в каталога трябва да бъде разрешено да актуализират описанието си. Но тъй като това ще изисква повторно изграждане на целия индекс на Sphinx Search всеки път, когато правите промяна, това се превръща в много скъпа операция. Може би ще поставите промените на опашка и ще ги приложите в пакет, като възстановявате индекса веднъж седмично. Но опитайте да обясните на продавачите на магазини защо малка промяна в описанието на магазина им няма да влезе в сила до неделя вечерта.