Slozitejsi select s databaze s ref a related
- Muhahe
- Člen | 79
Zdravim,
snazim se smontovat slozitejsi select dat nad databazi, ktery potrebuji provest nad vice tabulkami. Povedlo se mi ho uvest ± do chodu, ale spise me zajima, jestli to nejde udelat „lepe“ (optimalneji, rychleji).
V databazi ukladam domenu (informace o indexovane lokaci), linky (v podstate soubory a adresare + jejich obsah), klicova slova (veskera nalezena klicova slova v nazvu linku a v obsahu) a potom mam 16 tabulek, ktere predstavuji vazby mezi klicovimy slovy a linky. Tech tabulek mam 16, protoze predstavuji neco jako hashtable a podle hashe klicoveho slova potom volim konkretni tabulku, kde hledat, ke kterym linkum dane klicove slovo nalezi.
Timto kodem se snazim z DB dostat vysledky pro hledane(a) klicove(a) slovo(a).
private function searchKeywords($selectedDomains) {
//get stored search value
$searchValues = $this->searchValue;
//replace astering and exclamation mark (counted as characters for regular expression) and replace them by their mysql equivalent
$searchValues = str_replace("*", "%", $searchValues);
$searchValues = str_replace("!", "_", $searchValues);
// empty result array to prevent previous results to interfere
$this->resultData = array();
//searched phrase can be multiple keywords, so split it by space and get results for each keyword
foreach (explode(" ", $searchValues) as $keywordName) {
//set default link result weight to -1 (default value)
$weight = -1;
//select all keywords, which match searched keyword (or its regular expression)
$recordExists = $this->database->table('keywords')->where('keyword LIKE ?', $keywordName);
//count keywords
$recordExistsCount = $recordExists->count();
//if there are some keywords, continue
if ($recordExistsCount != 0) {
//fetch all keywords from database
$keywords = $recordExists->fetchAll();
//loop all matched keywords
foreach ($keywords as $keyword) {
$currentKeyword = $keyword->keyword;
//count keyword md5 sum to determine which table should be use to match it links
$md5 = md5($currentKeyword);
//get all link ids from linkkeyword relation table
$keywordJoinLink = $keyword->related('linkkeyword' . $md5[0]);
//fetch all keywordLinkIds
$keywordJoinLink->fetchAll();
//loop found links
foreach ($keywordJoinLink as $link) {
//store link weight, for later result sort
$weight = $link->weight;
//get link ID
$linkId = $link->linkId;
//check if link already exists in results, to prevent duplicity
$keyExists = array_key_exists($linkId, $this->resultData);
//if link already exists in result set, just update its weight and insert matching keyword for later keyword tag specification
if ($keyExists) {
$this->resultData[$linkId]->updateWeight($weight);
$this->resultData[$linkId]->addKeyword($currentKeyword);
//if link isnt in result yet, insert it
} else {
//get link reference
$linkData = $link->ref('link', 'linkId');
//get information about domain, to which link belongs (location, flagPath,...)
$domainData = $linkData->ref('domain', 'domainId');
//if is domain searchable and was selected before search, add link to result set. Otherwise ignore it
if ($domainData->searchable == 1 && in_array($domainData->id, $selectedDomains)) {
//create new link instance
$linkClass = new search\linkClass($linkData, $domainData);
//insert matching keyword to links keyword set
$linkClass->addKeyword($currentKeyword);
//set links weight
$linkClass->updateWeight($weight);
//insert link into result set
$this->resultData[$linkId] = $linkClass;
}
}
}
}
}
}
}
V prvni fazi, se snazim vybrat veskera slova, ktere odpovidaji hledanemu vyrazu. Pote pro jednotliva slova hledam v relacni tabulce odpovidajici linky a pokud link jeste neni ve vysledne datove sade, tak vytvorim instanci jeho objektu a vlozim ho do vyslednych dat.
Je to timto zpusobem ± spravne? Nebo na to jdu uplne ze spatne strany? Samotna konstrukce dotazu mi prijde tezkopadna, ale nemuzu prijit na to, jak to zjednodusit.
Hlavni dira mi asi prijde kolem toho spojovani. Podle toho co jsem cetl v navodu k database selection a activerow bych chtel delat select nad tabulkou link, kterou bych spojil prez relacni tabulky na tabulku keywords, ale neumim si poradit jak do toho zakomponovat spojeni prez relacni tabulky. A jestli je to dobre reseni…
(klicovych „slov“ mam v databazi neco kolem 400 000 a odkazu asi 15 000)
Editoval Muhahe (13. 9. 2016 9:54)
- Muhahe
- Člen | 79
CZechBoY napsal(a):
To schéma je trochu šílený :-)
Proč tam máš X stejných tabulek prolinkkeyword
? Nešlo by to dát do jedný a označit nějakým stavem nebo tak něco?
Ano schema je trochu prasarna, ale inspiroval jsem se u sphideru. Pokud to spravne chapu, melo by tam jit o to, ze nejvetsi pocet bude radku o informaci jake linky maji jaka klicova slova, proto to ukladam do neceho jako je hashtable. Pri hledani daneho klicoveho slova si vytvorim jeho md5 a z prvniho znaku v md5 vim v ktere tabulce tyto zaznamy hledat. Tudiz potom nemusim projizdet tak velke mnozstvi dat.
Pro nejakych 15 000 linku s cca 400 000 klicovimy slovy je to uz cca 2 000 000 zaznamu linkkeyword. Timto je to rozlozene do 16 tabulek s cca 150 000 ~ 200 000 radku pro kazdou tabulku
Editoval Muhahe (13. 9. 2016 11:02)
- CZechBoY
- Člen | 3608
Tak pokud musíš takhle dynamicky sestavit název tabulky tak si myslím, že asi moc algoritmus hledání nezměníš.
Jen pro ten ->count()
bych použil nějakou databázi
funkci – takhle se fetchnou všechny řádky a pak se volá count($rows).
Zkus to úplně bez toho count (stejně s nim pak nepracuješ) a prostě projdi
foreachem všechny keywordy.
- Muhahe
- Člen | 79
CZechBoY napsal(a):
Tak pokud musíš takhle dynamicky sestavit název tabulky tak si myslím, že asi moc algoritmus hledání nezměníš.
Jen pro ten
->count()
bych použil nějakou databázi funkci – takhle se fetchnou všechny řádky a pak se volá count($rows).
Zkus to úplně bez toho count (stejně s nim pak nepracuješ) a prostě projdi foreachem všechny keywordy.
Trosku se v tom hrabu a dostal jsem se do teto faze, ktera mi prijde o neco prehlednejsi. Jeste tam par drobnosti chybi ale asi to bude to co jsem hledal. SQL dotazy z debug baru se mi libi daleko vic, nez u predchozi varianty a je to takove prehlednejsi bez tech vsech foru atp… ale cesta k cily bude jeste daleko
private function searchKeywords($selectedDomains) {
$searchValues = $this->searchValue;
$searchValues = str_replace("*", "%", $searchValues);
$searchValues = str_replace("!", "_", $searchValues);
$this->resultData = array();
foreach (explode(" ", $searchValues) as $keywordName) {
$keywordMd5 = md5($keywordName);
$selection = $this->database->table('link');
$results = $selection->where('domain.id', $selectedDomains)->where('domain.searchable = ?', '1')->where(':linkkeyword' . $keywordMd5[0] . '.keyword.keyword LIKE ?', $keywordName)->fetchAll();
//$keywords = $results->ref('domain');
foreach($results as $result){
$domain = $result->ref('domain');
$linkClass = new search\linkClass($result, $domain);
$weight = 0;
//$keyword = array();
$weight = $result->related('linkkeyword' . $keywordMd5[0],'linkId');
debugger::bardump($weight);
/*
foreach ($result->related('linkkeyword' . $keywordMd5[0]) as $linkKeyword){
$weight += $linkKeyword->weight;
$keyword = $linkKeyword->ref('keyword')->keyword;
$linkClass->addKeyword($keyword);
}*/
//$keyExists = array_key_exists($linkId, $this->resultData);
//$linkClass->updateWeight($weight);
debugger::bardump($linkClass);
}
}
}