• 0 رای - 0 میانگین
  • 1
  • 2
  • 3
  • 4
  • 5
full text در innodb
#1
بالاخره innodb با اومدن mysql 5.6 به fulltext مجهز شد
http://blogs.innodb.com/wp/2011/12/innod...sql-5-6-4/
  پاسخ
تشکر شده توسط : Y.P.Y Na3r phpveteran zoghal cyletech Reza
#2
گرچه به طور کلی استفاده از Fulltext در محیط پروداکشن برای برنامه هایی که حجم داده بالایی رو در دیتابیس دارن تا به حال تست نکردم ولی همیشه از این موضوع ترس داشتم کسی هست تجربه ران کرردن اون رو داشته باشه؟ رو چند رکورد تست کرده و زمان اجرا چقدر بوده.
من همیشه ترجیح دادم خودم رشته های موجود رو اکسپلود کنم، وزن دهی کنم و ذخیره کنم نهایتاً می تونستم از یه فول تکست سرچ خیلی سریع برخوردار بشم که نگرانی از اورلود شدن در محیط پروداکشن نداشتم.
براتون شماتیک پایگاه داده رو می ذارم


فایل‌های پیوست تصاویر بندانگشتی
   
  پاسخ
تشکر شده توسط :
#3
ایول وحید مرسی. چقدر من سر آخرین پروژم با این موضوع درگیر شدم. آخر سرم مجبور به استفاده از myisam شدم. خیلی خوبه این اتفاق افتاده. توی بنچمارکایی که دیدم تقریبا میتونم بگم innodb خیلی بهتر از myisam عمل میکنه ولی خصوصیت هایی که myisam داره باعث میشه (یا میشد) که برای مصارف خاص سراغ innodb بریم.
منبع بنچمارک ها http://www.mysqlperformanceblog.com/ هست.
غایب
  پاسخ
تشکر شده توسط :
#4
(۱۳۹۱ تير ۱۱, ۰۸:۰۳ ب.ظ)ramram نوشته: گرچه به طور کلی استفاده از Fulltext در محیط پروداکشن برای برنامه هایی که حجم داده بالایی رو در دیتابیس دارن تا به حال تست نکردم ولی همیشه از این موضوع ترس داشتم کسی هست تجربه ران کرردن اون رو داشته باشه؟ رو چند رکورد تست کرده و زمان اجرا چقدر بوده.
من همیشه ترجیح دادم خودم رشته های موجود رو اکسپلود کنم، وزن دهی کنم و ذخیره کنم نهایتاً می تونستم از یه فول تکست سرچ خیلی سریع برخوردار بشم که نگرانی از اورلود شدن در محیط پروداکشن نداشتم.
براتون شماتیک پایگاه داده رو می ذارم

myisam که در این زمینه ضعیفه. احتمال زیاد innodb بتونه بهتر جواب بده. چیزهایی که اوراکل و innodb گفتن اینه که در بهینه سازی های آیندش خیلی کار می کنن. ولی من تا حدود ۱ میلیون رکورد روی محصول پراداکشن داشتم مشکلی پیش نیومده (myisam). سرعتش خیلی خوبه. روی دیتاهای بالاتر هم وقتی میرفت بالای ۱۰ میلیون رکورد سرعت همینطور پایین می اومد. به همین خاطر مجبور شدم از sphinx استفاده کنم که دیگه در زمینه فول تکس و سرعت پردازش واقعا هیچ حرفی رو برای گفتن نذاشته.

راستی رامین دیتایی هم داری بدی ببینم چه جوری ذخیره سازی می کنی؟ راستی چرا از lucene استفاده نکردی؟ از زند فریم ورک هم فقط قسمتlucene رو می تونستی استفاده کنی. الگوریتم full text یه مقدار پیچیده تر از این حرفها هست.


اوراکل خیلی روی پرفورمنس innodb زوم کرده. تقریبا هر نسخه ای داره از 5.6 میده نصفش مربوط به پرفورمنس innodb هست
  پاسخ
تشکر شده توسط : zoghal
#5
از Zend_Lucene استفاده کردم ضعیفتر از چیزی که فکرش رو بکنی به لحاظ پرفورمنس می گم ها یعنی اصلا به درد نمی خوره برای یک وبلاگ کم بازدید خوبه Smile اگه بخوای از lucene استفاده کنی باید با جاوا کار کنی دلم نمی خواست پروژه هام وابسته به چیز دیگه ای باشن.
راستش این پروسه ای که ایجاد کردم برای خودم واقعا عالی جواب داده اما این دیتایی که می خوای رو نمی دونم چجوری برات بفرستم اگه سورس تولید و استخراج واژگان رو می خوای می تونم اینجا بذارم

کد پی‌اچ‌پی:
<?php

define
('SEARCH_MAX_WORD_LENGTH'15);

/* Copied from Drupal search module, except for \x{0}-\x{2f} that has been replaced by \x{0}-\x{2c}\x{2e}-\x{2f} in order to keep the char '-' */
define('PREG_CLASS_SEARCH_EXCLUDE',
    
'\x{0}-\x{2c}\x{2e}-\x{2f}\x{3a}-\x{40}\x{5b}-\x{60}\x{7b}-\x{bf}\x{d7}\x{f7}\x{2b0}-'.
    
'\x{385}\x{387}\x{3f6}\x{482}-\x{489}\x{559}-\x{55f}\x{589}-\x{5c7}\x{5f3}-'.
    
'\x{61f}\x{640}\x{64b}-\x{65e}\x{66a}-\x{66d}\x{670}\x{6d4}\x{6d6}-\x{6ed}'.
    
'\x{6fd}\x{6fe}\x{700}-\x{70f}\x{711}\x{730}-\x{74a}\x{7a6}-\x{7b0}\x{901}-'.
    
'\x{903}\x{93c}\x{93e}-\x{94d}\x{951}-\x{954}\x{962}-\x{965}\x{970}\x{981}-'.
    
'\x{983}\x{9bc}\x{9be}-\x{9cd}\x{9d7}\x{9e2}\x{9e3}\x{9f2}-\x{a03}\x{a3c}-'.
    
'\x{a4d}\x{a70}\x{a71}\x{a81}-\x{a83}\x{abc}\x{abe}-\x{acd}\x{ae2}\x{ae3}'.
    
'\x{af1}-\x{b03}\x{b3c}\x{b3e}-\x{b57}\x{b70}\x{b82}\x{bbe}-\x{bd7}\x{bf0}-'.
    
'\x{c03}\x{c3e}-\x{c56}\x{c82}\x{c83}\x{cbc}\x{cbe}-\x{cd6}\x{d02}\x{d03}'.
    
'\x{d3e}-\x{d57}\x{d82}\x{d83}\x{dca}-\x{df4}\x{e31}\x{e34}-\x{e3f}\x{e46}-'.
    
'\x{e4f}\x{e5a}\x{e5b}\x{eb1}\x{eb4}-\x{ebc}\x{ec6}-\x{ecd}\x{f01}-\x{f1f}'.
    
'\x{f2a}-\x{f3f}\x{f71}-\x{f87}\x{f90}-\x{fd1}\x{102c}-\x{1039}\x{104a}-'.
    
'\x{104f}\x{1056}-\x{1059}\x{10fb}\x{10fc}\x{135f}-\x{137c}\x{1390}-\x{1399}'.
    
'\x{166d}\x{166e}\x{1680}\x{169b}\x{169c}\x{16eb}-\x{16f0}\x{1712}-\x{1714}'.
    
'\x{1732}-\x{1736}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17db}\x{17dd}'.
    
'\x{17f0}-\x{180e}\x{1843}\x{18a9}\x{1920}-\x{1945}\x{19b0}-\x{19c0}\x{19c8}'.
    
'\x{19c9}\x{19de}-\x{19ff}\x{1a17}-\x{1a1f}\x{1d2c}-\x{1d61}\x{1d78}\x{1d9b}-'.
    
'\x{1dc3}\x{1fbd}\x{1fbf}-\x{1fc1}\x{1fcd}-\x{1fcf}\x{1fdd}-\x{1fdf}\x{1fed}-'.
    
'\x{1fef}\x{1ffd}-\x{2070}\x{2074}-\x{207e}\x{2080}-\x{2101}\x{2103}-\x{2106}'.
    
'\x{2108}\x{2109}\x{2114}\x{2116}-\x{2118}\x{211e}-\x{2123}\x{2125}\x{2127}'.
    
'\x{2129}\x{212e}\x{2132}\x{213a}\x{213b}\x{2140}-\x{2144}\x{214a}-\x{2b13}'.
    
'\x{2ce5}-\x{2cff}\x{2d6f}\x{2e00}-\x{3005}\x{3007}-\x{303b}\x{303d}-\x{303f}'.
    
'\x{3099}-\x{309e}\x{30a0}\x{30fb}\x{30fd}\x{30fe}\x{3190}-\x{319f}\x{31c0}-'.
    
'\x{31cf}\x{3200}-\x{33ff}\x{4dc0}-\x{4dff}\x{a015}\x{a490}-\x{a716}\x{a802}'.
    
'\x{a806}\x{a80b}\x{a823}-\x{a82b}\x{d800}-\x{f8ff}\x{fb1e}\x{fb29}\x{fd3e}'.
    
'\x{fd3f}\x{fdfc}-\x{fe6b}\x{feff}-\x{ff0f}\x{ff1a}-\x{ff20}\x{ff3b}-\x{ff40}'.
    
'\x{ff5b}-\x{ff65}\x{ff70}\x{ff9e}\x{ff9f}\x{ffe0}-\x{fffd}');

define('PREG_CLASS_NUMBERS',
    
'\x{30}-\x{39}\x{b2}\x{b3}\x{b9}\x{bc}-\x{be}\x{660}-\x{669}\x{6f0}-\x{6f9}'.
    
'\x{966}-\x{96f}\x{9e6}-\x{9ef}\x{9f4}-\x{9f9}\x{a66}-\x{a6f}\x{ae6}-\x{aef}'.
    
'\x{b66}-\x{b6f}\x{be7}-\x{bf2}\x{c66}-\x{c6f}\x{ce6}-\x{cef}\x{d66}-\x{d6f}'.
    
'\x{e50}-\x{e59}\x{ed0}-\x{ed9}\x{f20}-\x{f33}\x{1040}-\x{1049}\x{1369}-'.
    
'\x{137c}\x{16ee}-\x{16f0}\x{17e0}-\x{17e9}\x{17f0}-\x{17f9}\x{1810}-\x{1819}'.
    
'\x{1946}-\x{194f}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2153}-\x{2183}'.
    
'\x{2460}-\x{249b}\x{24ea}-\x{24ff}\x{2776}-\x{2793}\x{3007}\x{3021}-\x{3029}'.
    
'\x{3038}-\x{303a}\x{3192}-\x{3195}\x{3220}-\x{3229}\x{3251}-\x{325f}\x{3280}-'.
    
'\x{3289}\x{32b1}-\x{32bf}\x{ff10}-\x{ff19}');

define('PREG_CLASS_PUNCTUATION',
    
'\x{21}-\x{23}\x{25}-\x{2a}\x{2c}-\x{2f}\x{3a}\x{3b}\x{3f}\x{40}\x{5b}-\x{5d}'.
    
'\x{5f}\x{7b}\x{7d}\x{a1}\x{ab}\x{b7}\x{bb}\x{bf}\x{37e}\x{387}\x{55a}-\x{55f}'.
    
'\x{589}\x{58a}\x{5be}\x{5c0}\x{5c3}\x{5f3}\x{5f4}\x{60c}\x{60d}\x{61b}\x{61f}'.
    
'\x{66a}-\x{66d}\x{6d4}\x{700}-\x{70d}\x{964}\x{965}\x{970}\x{df4}\x{e4f}'.
    
'\x{e5a}\x{e5b}\x{f04}-\x{f12}\x{f3a}-\x{f3d}\x{f85}\x{104a}-\x{104f}\x{10fb}'.
    
'\x{1361}-\x{1368}\x{166d}\x{166e}\x{169b}\x{169c}\x{16eb}-\x{16ed}\x{1735}'.
    
'\x{1736}\x{17d4}-\x{17d6}\x{17d8}-\x{17da}\x{1800}-\x{180a}\x{1944}\x{1945}'.
    
'\x{2010}-\x{2027}\x{2030}-\x{2043}\x{2045}-\x{2051}\x{2053}\x{2054}\x{2057}'.
    
'\x{207d}\x{207e}\x{208d}\x{208e}\x{2329}\x{232a}\x{23b4}-\x{23b6}\x{2768}-'.
    
'\x{2775}\x{27e6}-\x{27eb}\x{2983}-\x{2998}\x{29d8}-\x{29db}\x{29fc}\x{29fd}'.
    
'\x{3001}-\x{3003}\x{3008}-\x{3011}\x{3014}-\x{301f}\x{3030}\x{303d}\x{30a0}'.
    
'\x{30fb}\x{fd3e}\x{fd3f}\x{fe30}-\x{fe52}\x{fe54}-\x{fe61}\x{fe63}\x{fe68}'.
    
'\x{fe6a}\x{fe6b}\x{ff01}-\x{ff03}\x{ff05}-\x{ff0a}\x{ff0c}-\x{ff0f}\x{ff1a}'.
    
'\x{ff1b}\x{ff1f}\x{ff20}\x{ff3b}-\x{ff3d}\x{ff3f}\x{ff5b}\x{ff5d}\x{ff5f}-'.
    
'\x{ff65}');

/**
 * Matches all CJK characters that are candidates for auto-splitting
 * (Chinese, Japanese, Korean).
 * Contains kana and BMP ideographs.
 */
define('PREG_CLASS_CJK''\x{3041}-\x{30ff}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fbb}\x{f900}-\x{fad9}');

class 
Search
{
    public static function 
sanitize($string$indexation false)
    {
        
$string Yii::app()->func->strtolower(strip_tags($string));
        
$string html_entity_decode($stringENT_NOQUOTES'utf-8');

        
$string preg_replace('/(['.PREG_CLASS_NUMBERS.']+)['.PREG_CLASS_PUNCTUATION.']+(?=['.PREG_CLASS_NUMBERS.'])/u''\1'$string);
        
$string preg_replace('/['.PREG_CLASS_SEARCH_EXCLUDE.']+/u'' '$string);

        if (
$indexation)
            
$string preg_replace('/[._-]+/'''$string);
        else
        {
            
$string preg_replace('/[._]+/'''$string);
            
$string ltrim(preg_replace('/([^ ])-/''$1'' '.$string));
            
$string preg_replace('/[._]+/'''$string);
            
$string preg_replace('/[^\s]-+/'''$string);
        }

        
/*
        $blacklist = Configuration::get('PS_SEARCH_BLACKLIST', (int)$id_lang);
        if (!empty($blacklist))
        {
            $string = preg_replace('/(?<=\s)('.$blacklist.')(?=\s)/Su', '', $string);
            $string = preg_replace('/^('.$blacklist.')(?=\s)/Su', '', $string);
            $string = preg_replace('/(?<=\s)('.$blacklist.')$/Su', '', $string);
            $string = preg_replace('/^('.$blacklist.')$/Su', '', $string);
        }

        */

        
if ($indexation)
        {
            
$minWordLen 3;
            if (
$minWordLen 1)
            {
                
$minWordLen -= 1;
                
$string preg_replace('/(?<=\s)[^\s]{1,'.$minWordLen.'}(?=\s)/Su'' '$string);
                
$string preg_replace('/^[^\s]{1,'.$minWordLen.'}(?=\s)/Su'''$string);
                
$string preg_replace('/(?<=\s)[^\s]{1,'.$minWordLen.'}$/Su'''$string);
                
$string preg_replace('/^[^\s]{1,'.$minWordLen.'}$/Su'''$string);
            }
        }

        
$string trim(preg_replace('/\s+/'' '$string));
        return 
$string;
    }

    public static function 
searchProduct($expr$language null)
    {
        if(
is_null($language))
            
$language Yii::app()->language;

        
$intersectArray = array();
        
$scoreArray = array();
        
$words explode(' 'Search::sanitize($expr));

        foreach (
$words AS $key => $word)
            if (!empty(
$word) AND strlen($word) >= 3) {
                
$word str_replace('%''\\%'$word);
                
$word str_replace('_''\\_'$word);
                
$searchWord = ($word[0] == '-') ? Yii::app()->func->substr($word1SEARCH_MAX_WORD_LENGTH) : Yii::app()->func->substr($word0SEARCH_MAX_WORD_LENGTH);
                
$intersectArray[] = Yii::app()->db->createCommand()
                    ->
select('product_id')
                    ->
from('search_word sw')
                    ->
leftJoin('search_product_index si''sw.id = si.search_word_id')
                    ->
where('sw.language = :language AND sw.word LIKE :searchWord',array(
                        
':language' => $language,
                        
':searchWord' => "{$searchWord}%"))->queryAll();
                if (
$word[0] != '-')
                    
$scoreArray[] = 'sw.word LIKE \''.$searchWord.'%\'';
            } else
                unset(
$words[$key]);

        if (!
sizeof($words))
            return array(
'total' => 0'result' => array());

        
$score '';
        if (
sizeof($scoreArray))
            
$score ',(
                SELECT SUM(weight)
                FROM search_word sw
                LEFT JOIN search_product_index si ON sw.id = si.search_word_id
                WHERE sw.language = \''
.$language.'\' AND si.product_id = p.id AND ('.implode(' OR '$scoreArray).')) position';

        
$productPool '';
        foreach (
$intersectArray as $result)
        {
            foreach(
$result as $row)
                
$productPool .= (int)$row['product_id'].',';
        }
        if (empty(
$productPool))
            return array(
'total' => 0'result' => array());
        
$productPool = ((strpos($productPool',') === false) ? (' = '.(int)$productPool.' ') : (' IN ('.rtrim($productPool',').') '));

        
$dependency = new CDbCacheDependency('SELECT MAX(`id`) FROM `product`');
        
$result Yii::app()->db->cache(10800$dependency)->createCommand()
            ->
select(' p.*')
            ->
from('product p')
            ->
where('p.`id` '.$productPool)
            ->
queryAll();

        
$total count($result);

        return array(
'total' => $total,'result' => $result);
    }

    public static function 
getFeatures($productId$language null)
    {
        
$features '';
        
$productId = (int)$productId;
        if (
$language === null)
            
$language Yii::app()->language;
        if (
$language !== 'en'){
            
$featuresArray Yii::app()->db->createCommand()
                ->
select('fv.value')
                ->
from('feature_product fp')
                ->
leftJoin('feature_value_translation fv''fv.id=fp.feature_value_id AND fv.language=:language',array(':language' => $language))
                ->
where('fp.product_id=:productId', array(':productId' => $productId,))
                ->
queryAll();
        }else{
            
$featuresArray Yii::app()->db->createCommand()
                ->
select('fv.value')
                ->
from('feature_product fp')
                ->
leftJoin('feature_value fv''fv.id=fp.feature_value_id')
                ->
where('fp.product_id=:productId', array(':productId' => $productId,))
                ->
queryAll();
        }

        foreach (
$featuresArray AS $feature)
            
$features .= $feature['value'].' ';
        return 
$features;
    }

    protected static function 
getProductsToIndex($limit 50$language null)
    {
        if (
$language === null)
            
$language Yii::app()->language;
        if (
$language !== 'en'){
            
$results Yii::app()->db->createCommand()
                ->
select('p.id, pt.language, pt.name pname, pt.information, pt.description, pt.keyword, ct.name cname, m.name mname')
                ->
from('product p')
                ->
leftJoin('product_translation pt''pt.product_id=p.id')
                ->
leftJoin('category_translation ct''ct.category_id=p.category_id')
                ->
leftJoin('company_translation mt''mt.company_id=p.company_id')
                ->
where('p.indexed=0')
                ->
limit((int)$limit)
                ->
queryAll();
        }else{
            
$results Yii::app()->db->createCommand()
                ->
select('p.id, p.name pname, p.information, p.description, p.keyword, c.name cname, m.name mname')
                ->
from('product p')
                ->
leftJoin('category c''c.id=p.category_id')
                ->
leftJoin('company m''m.id=p.company_id')
                ->
where('p.indexed=0')
                ->
limit((int)$limit)
                ->
queryAll();
            foreach(
$results as $key => $value)
                
$results[$key]['language'] = 'en';

        }
        return 
$results;
    }

    public static function 
indexation($full false$productId false$language null)
    {
        if (
$productId)
            
$full false;

        if (
$full){
            
Yii::app()->db->createCommand('SET FOREIGN_KEY_CHECKS = 0')->execute();
            
Yii::app()->db->createCommand('TRUNCATE search_word')->execute();
            
Yii::app()->db->createCommand('TRUNCATE search_product_index')->execute();
            
Yii::app()->db->createCommand('TRUNCATE search_company_index')->execute();
            
Yii::app()->db->createCommand('TRUNCATE search_offer_index')->execute();
            
Yii::app()->db->createCommand('UPDATE product SET indexed = 0')->execute();
            
Yii::app()->db->createCommand('SET FOREIGN_KEY_CHECKS = 1')->execute();
        }else {
            if(
$productId)
                
$products Yii::app()->db->createCommand()
                    ->
select('product')
                    ->
from('product')
                    ->
where('product.id=:productId', array(':productId' => $productId,))
                    ->
queryRow();
            else
                
$products Yii::app()->db->createCommand()
                    ->
select('product')
                    ->
from('product')
                    ->
where('indexed = 0')
                    ->
queryAll();

            
$ids = array();
            if (!empty(
$products))
                foreach(
$products AS $product)
                    
$ids[] = (int)$product['id'];
            if (
sizeof($ids))
                
Yii::app()->db->createCommand('DELETE FROM search_index WHERE id_product IN ('.implode(','$ids).')')->execute();
        }

        
// Every fields are weighted according to the configuration in the backend
        
$weightArray = array(
            
'pname' => 6,
            
'keyword' => 4,
            
'description' => 1,
            
'information' => 1,
            
'trademark' => 2,
            
'cname' => 3,
            
'mname' => 3,
            
'features' => 2
        
);

        
// Those are kind of global variables required to save the processed data in the database every X occurences, in order to avoid overloading MySQL
        
$countWords 0;
        
$countProducts 0;
        
$countCompanies 0;
        
$countOffers 0;
        
$queryArray3 = array();
        
$productsArray = array();
        
$companiesArray = array();
        
$offersArray = array();

        
// Every indexed words are cached into a PHP array
        
$wordIdsByWord = array();
        
$wordIds Yii::app()->db->createCommand()
            ->
select('sw.id, sw.word, sw.language')
            ->
from('search_word sw')
            ->
queryAll();

        foreach (
$wordIds as $wordId) {
            if (!isset(
$wordIdsByWord[$wordId['language']]))
                
$wordIdsByWord[$wordId['language']] = array();
            
$wordIdsByWord[$wordId['language']]['_'.$wordId['word']] = (int)$wordId['id'];
        }

        
// Products are processed 50 by 50 in order to avoid overloading MySQL
        
while ($products Search::getProductsToIndex(50) AND count($products) > 0) {
            
// Now each non-indexed product is processed one by one, langage by langage
            
foreach ($products as $product)
            {
                
$product['features'] = Search::getFeatures((int)$product['id'], $product['language']);

                
// Data must be cleaned of html, bad characters, spaces and anything, then if the resulting words
                // are long enough, they're added to the array
                
$pArray = array();
                foreach (
$product AS $key => $value){
                    
$column substr($key,-3,3);
                    
$isIdColumn = ($column==='_id' || $column==='.id') ? ture false;
                    if (!
$isIdColumn) {
                        
$words explode(' 'Search::sanitize($valuetrue));
                        foreach (
$words AS $word)
                            if (!empty(
$word)) {
                                
$word Yii::app()->func->substr($word0SEARCH_MAX_WORD_LENGTH);
                                
// Remove accents
                                
$word Yii::app()->func->convertAccents($word);
                                
// Remove Stop Character
                                
$word Yii::app()->func->removeStopCharacters($word);

                                if (!isset(
$pArray[$word]))
                                    
$pArray[$word] = 0;
                                
$pArray[$word] += $weightArray[$key];
                            }
                    }

                }
                
// If we find words that need to be indexed, they're added to the word table in the database
                
if (sizeof($pArray))
                {
                    
$queryArray = array();
                    
$queryArray2 = array();
                    foreach (
$pArray AS $word => $weight)
                        if (
$weight AND !isset($wordIdsByWord['_'.$word])) {
                            
$queryArray[$word] = '(\''.$product['language'].'\',\''.$word.'\')';
                            
$queryArray2[] = '\''.$word.'\'';
                            
$wordIdsByWord[$product['language']]['_'.$word] = 0;
                        }

                    
$existingWords Yii::app()->db->createCommand()
                        ->
select('word')
                        ->
from('search_word')
                        ->
where('word IN ('.implode(','$queryArray2).') AND language=:language',array(':language'=>$product['language']))
                        ->
queryAll();

                    foreach (
$existingWords as $data){
                        unset(
$queryArray[Yii::app()->func->convertAccents($data['word'])]);
                    }


                    if (
count($queryArray))
                    {
                        
// The words are inserted...
                        
$sql 'INSERT IGNORE INTO search_word (language, word) VALUES '.implode(','$queryArray);
                        
Yii::app()->db->createCommand($sql)->execute();
                    }
                    if (
count($queryArray2))
                    {
                        
// ...then their IDs are retrieved and added to the cache
                        
$addedWords Yii::app()->db->createCommand()
                            ->
select('sw.id, sw.word')
                            ->
from('search_word sw')
                            ->
where('sw.word IN ('.implode(','$queryArray2).') AND language=:language',array(':language'=>$product['language']))
                            ->
limit(count($queryArray2))
                            ->
queryAll();

                        
// replace accents from the retrieved words so that words without accents or with differents accents can still be linked
                        
foreach ($addedWords AS $wordId)
                            
$wordIdsByWord[$product['language']]['_'.Yii::app()->func->convertAccents($wordId['word'])] = (int)$wordId['id'];
                    }
                }

                foreach (
$pArray AS $word => $weight)
                {
                    if (!
$weight)
                        continue;
                    if (!isset(
$wordIdsByWord[$product['language']]['_'.$word]))
                        continue;
                    if (!
$wordIdsByWord[$product['language']]['_'.$word])
                        continue;
                    
$queryArray3[] = '('.(int)$product['id'].','.(int)$wordIdsByWord[$product['language']]['_'.$word].','.(int)$weight.')';

                    
// Force save every 200 words in order to avoid overloading MySQL
                    
if (++$countWords 200 == 0)
                        
Search::saveIndex($queryArray3,'product');
                }

                if (!
in_array($product['id'], $productsArray))
                    
$productsArray[] = (int)$product['id'];
            }
            
Search::setProductsAsIndexed($productsArray);
            
// One last save is done at the end in order to save what's left
            
Search::saveIndex($queryArray3,'product');
        }
        return 
true;
    }

    protected static function 
setProductsAsIndexed(&$productsArray)
    {
        if (
$i count($productsArray)){
            
$sql 'UPDATE product SET indexed = 1 WHERE id IN (:productsArray) LIMIT '.(int)$i;
            
Yii::app()->db->createCommand($sql)->execute(array(':productsArray'=>implode(','$productsArray)));
        }
        
$productsArray = array();
    }

    
// $queryArray3 is automatically emptied in order to be reused immediately
    
protected static function saveIndex(&$queryArray3,$table)
    {
        if (
count($queryArray3)){
            
$sql 'INSERT INTO search_'.$table.'_index (product_id, search_word_id, weight) VALUES '.implode(','$queryArray3).' ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)';
            
Yii::app()->db->createCommand($sql)->execute();
        }
        
$queryArray3 = array();
    }



البته این کد متعلق به آخرین پروژه ای هست که دارم روش کار میکنم و با فریم ورک yii داره کار می شه ولی الگوریتم کار واضحه

در ضمن این کد من برای هر کالا بسته به حجم توضیحات حدود 150 واژه استخراج می کنه که اگه از واژگان مشابه بگذریم میانگین 500 واژه برای ده کالا استخراج می شه یعنی 1 میلیون رکورد برای 20 هزار کالا که خیلی کم هست برای پروژه ای که دارم کار می کنم برای همین ترجیحا همچون گذشته از خیر فول تکست مای اس کیو ال بگذرم می تونی Sphinx ات رو به اشتراک بذاری؟
تا یادم نرفته اینو هم بگم مشکل Zend_Lucene و کلا بحث کندی اون بخصوص در تولید ایندکس ها هست بیشتر که باز هم برای من خیلی گرون تموم میشه چون واقعا کند

می گم وحید انقدر ستاره هات زیاد شدن داره جِر می خوره اون DIV Big Grin

به این مسئله هم توجه کنید که واقعا تو خیلی از موارد قابلیت های Fulltext واقعا اون چیزی نیست که ما می خوایم اما اون الگوریتم من برای کاری که من می خوام خیلی خوب جواب می ده واسه همین ترجیح می دم اون SELECT ساده رو روی فیلد (word VARCHAR(15 ایندکس گذاری شده تا یه فولتکست سرچ خرکی که فشار زیادی رو سرورم میاره اونم تو تریت و کان کارنسی بالایی که پیش بینی می شه سر kingsera با اون سرورها هیولاش الان مشکل داریم.

یه عکسی رو ضمیمه کردم شاید به کارت بیاد


فایل‌های پیوست تصاویر بندانگشتی
   
  پاسخ
تشکر شده توسط : admin
#6
روی Zend_Lucene من روی دیتای ۱۰ گیگ تست کردم. خوب جواب میداده. ولی به شدت به APC احتیاج داره. نمی دونم چه جوری بوده که کند بوده. البته دیتای بالاتر از این رو گند می زنه بهش Big Grin
ولی کدت جالب بود. البته زیاد هم مشخص نیست یه ذره کامنت میذاشتی بد نبود Big Grin
ولی شبیه سرچ دروپاله . اونم همینطوریه.
  پاسخ
تشکر شده توسط : zoghal
#7
(۱۳۹۱ تير ۱۱, ۱۱:۲۹ ب.ظ)admin نوشته: روی Zend_Lucene من روی دیتای ۱۰ گیگ تست کردم. خوب جواب میداده. ولی به شدت به APC احتیاج داره. نمی دونم چه جوری بوده که کند بوده. البته دیتای بالاتر از این رو گند می زنه بهش Big Grin
ولی کدت جالب بود. البته زیاد هم مشخص نیست یه ذره کامنت میذاشتی بد نبود Big Grin
ولی شبیه سرچ دروپاله . اونم همینطوریه.
Smile کامنت رو حق داری انقدر وقتم سر کد زنی کم هست که واقعا همش رو دارم بدون کامنت می رم جلو البته کدها خیلی بلوکه شدن و بجز همین یک فایل استثنا باقیشون خیلی خوانا هستن
سرچ دروپال رو که ندیدم اما به نظرم این روش رو خیلی از پروژه ها پیاده شده باشه و فکر هم می کنم روشی باشه که امتحانش رو پس داده و گزینه خاص دیگه ای فعلا وجود نداره اصولا فول تکست بزرگترین مشکل خیلی از پروژه ها بوده و هست.
در مورد Zend_Lucene چون نمی تونستم از APC استفاده کنم احتمالا دلیل کندی همون بوده اما به هر شکل واقعا کند نمی شه برای تریت بالا ازش استفاده کرد ضمن اینکه برای ایندکس هم خیلی پروسز می گیره لامذهب
  پاسخ
تشکر شده توسط :
#8
چیه Sphinx رو به اشتراک بزارم؟
http://sphinxsearch.com/
این سایتش هست
اینم ویژگی هاش هست
http://sphinxsearch.com/about/

آره بعضی وقتها از یک سری ویژگی های نمی خواییم استفاده کنیم. ولی بعضی وقتها هم نیاز هست. خیلی وقتها ما نتیجه مطلوب می خواییم. نتیجه مطلوب توی رکوردهای بسیار بالا اونجا معنی پیدا می کنه. نه برای مثلا ۱۰۰۰ رکورد. کاربر توی رکوردهای بالا می خواد که سریعا به نتیجه مطلوبش برسه.
  پاسخ
تشکر شده توسط :
#9
(۱۳۹۱ تير ۱۱, ۱۱:۳۵ ب.ظ)admin نوشته: چیه Sphinx رو به اشتراک بزارم؟
http://sphinxsearch.com/
این سایتش هست
اینم ویژگی هاش هست
http://sphinxsearch.com/about/

آره بعضی وقتها از یک سری ویژگی های نمی خواییم استفاده کنیم. ولی بعضی وقتها هم نیاز هست. خیلی وقتها ما نتیجه مطلوب می خواییم. نتیجه مطلوب توی رکوردهای بسیار بالا اونجا معنی پیدا می کنه. نه برای مثلا ۱۰۰۰ رکورد. کاربر توی رکوردهای بالا می خواد که سریعا به نتیجه مطلوبش برسه.
اونو نگفتم به اشتراک بذار گفتم تجربیاتت رو به اشتراک بذار یعنی پروژه و جایگاهی که از این استفاده کردی حجم کار و کان کارنسی و تعداد رکورد نتایج این و تفاوتش با نتایج فول تکست بیلت این خود مای اس کیو ال این چیزا مدنظر بود گل پسر
  پاسخ
تشکر شده توسط : zoghal
#10
راستی رامین جان شاید فول ایندکس می کردی که کند بوده. باید live index باشه. این یه ویژگی خیلی مهم هست. یه چیز دیگه هم اینکه روی ویندوز به شدت کنده. به خاطر IO ضعیف php روی ویندوز .
البته از دلایلی که من از Lucene استفاد ه می کردم این بود که همچین داکیومنتی رو داشتم (rira ) . استانداری هست که فایلهای ایجاد شدش توسط بقیه خونده میشه.

راستی یه بار هم از sqlite استفاده کردم برای اینکار. واقعا سرعتش خیلی بالا بود. ولی نیاز به کامپایل مجدد داره. FTS4 واقعا فوق العاده بود
  پاسخ
تشکر شده توسط :


پرش به انجمن:


کاربران در حال بازدید این موضوع: 1 مهمان