Маркиране и изтриване на дублирано съдържание php/laravel

bobbydigital

Active Member
Предполагам, че много колеги сте се сблъсквали с проблема дублирано съдържание, особенно когато в сайта има съдържание което е попъплва от потребители и особенно ако става въпрос за сайтове за обяви да кажем. Предлагам едно решение което може да реши този проблем.

Теорията е следната когато се публикува нещо му се създава хаш, после се пуска скрипт който открива редове от базата с еднакъв хаш и ги изтрива, така се елиминират дубликатите.

Това са две функции който генерират уникален хаш на даден текст.
За да може винаги хаша да еднакъв дори и потребителя да слага точки или спейсове или тирета или нещо си, за да заблуди системата функцията прави следното:
1. прави целия текст с малки букви
2. заменя \r\n (нов ред) с <br />
3. премахнва всички тагове от текста
4. премахва всички символи които не са буква или цифра
5. сортира текста

по този начин си осигуряване винаги еднакъв хаш
това е кода на двете функции

Код:
function generateDescriptionHash($_description)
{
    $_description = mb_strtolower($_description);
    $_description = nl2br($_description);
    $_description = strip_tags($_description);
    $_description = preg_replace('/[^\pL\pN]+/u', '', $_description);
    $_description = sortString($_description);
    return md5($_description);
}

function sortString($_string)
{
    $sort = [];
    for($i=0;$i<strlen($_string);$i++){
        $sort[$i] = mb_substr($_string,$i , 1, 'UTF-8');
    }
    sort($sort);
    return implode($sort);
}

разбира се функциите могат да се сложат в някакъв util клас за да е по удобно.

После при публикуване на дадено съдържание, в базата се записва този хаш.
След това се пуска крон, който на определено време изтрива съдържанието с еднакъв хаш

В моят случай на laravel в сайт за обяви кода изглежда така:
Код:
$doubleAds = Ad::select('ad_id')
    ->groupBy('ad_description_hash')
    ->havingRaw('count(ad_id) >= 2')
    ->get()
    ->toArray();
if(!empty($doubleAds)){
    $inArray = [];
    foreach($doubleAds as $k => $v){
        $inArray[] = $v['ad_id'];
    }
    Ad::whereIn('ad_id', $inArray)->delete();
}

Като цяло идеята е ясна, прави се хаш, изтриват се дубликатите.
Поздрави :)
 
Според мен правилно би било да се прави по уникални думи сравнение. Така да се открива сходство на текста в проценти. Но това са тежки операции, а ти го правиш при събмит на форма май.
 
Интересно ми е, защо допускаш да има дублирано съдържание? Режи ги още на ниво публикуване, вместо да си товариш машината с този крон.

Относно кода, защо толкова много операции?
Код:
function sortString($_string)
{
   $sort = preg_split('//u', $_string);
   sort($sort);
   return implode($sort);
}

Данни: 3081 знака от произволня обавя в olx
Твоят sortString: ~ 0.018 секунди
Моят вариант: ~ 0.0017 секунди

Защо ти е nl2br() след като премахваш всички знаци които не са букви или числа?

@hristonev дали ще е по букви както го прави сега или по думи е еднакво сложно.

Рано или късно обаче хората ще се усетят и ще ползват вемсто българско Е, английско E ... така че дали ще са букви или думи все тая.
 
Интересно ми е, защо допускаш да има дублирано съдържание? Режи ги още на ниво публикуване, вместо да си товариш машината с този крон.

Относно кода, защо толкова много операции?
Код:
function sortString($_string)
{
   $sort = preg_split('//u', $_string);
   sort($sort);
   return implode($sort);
}

Данни: 3081 знака от произволня обавя в olx
Твоят sortString: ~ 0.018 секунди
Моят вариант: ~ 0.0017 секунди

Защо ти е nl2br() след като премахваш всички знаци които не са букви или числа?

@hristonev дали ще е по букви както го прави сега или по думи е еднакво сложно.

Рано или късно обаче хората ще се усетят и ще ползват вемсто българско Е, английско E ... така че дали ще са букви или думи все тая.

Да видях го това решение с регекса в стака и в документацията, просто не ми допада на мен.
nl2br за да махне \r\n не съм сигурен дали няма да остане само rn после.
съгласен съм че може да се пипне повече. не го правя веднага, защото потребителя не е добре да се тормози много.
 
Как така не ти допада? Това превръща стринга в масив, това което правиш чрез for цикъла. Едно и също е :), просто едното е в пъти по-бързо и даже не ти трябват гадните и тромави mb_* функции.
\r и \n са в групата на space-овете, които ти премахваш, така че няма как да остане само rn ;). Все едно сега остава \t което е табулацията :)

Как го тормозиш потребителя ако имаш UNIQUE индекс върху полето за hash-а (+ други ако е нужно)?
Просто при опит за INSERT ще получиш грешка за дублиране и ще информираш потребителя.
 
Как така не ти допада? Това превръща стринга в масив, това което правиш чрез for цикъла. Едно и също е :), просто едното е в пъти по-бързо и даже не ти трябват гадните и тромави mb_* функции.
\r и \n са в групата на space-овете, които ти премахваш, така че няма как да остане само rn ;). Все едно сега остава \t което е табулацията :)

Как го тормозиш потребителя ако имаш UNIQUE индекс върху полето за hash-а (+ други ако е нужно)?
Просто при опит за INSERT ще получиш грешка за дублиране и ще информираш потребителя.
Еми така не ми допада :), ясно е какво прави :), не трябва да се тормози потребителя
 
По-бързото решение не ти допада? Интересно мислене :)
 
Не трябва ли да има някаква проверка, да се минават само текстове >= да кажем n символа? От чиста UX гледна точка, потребител 1 качва текст от няколко думи: "n produkt" много изгодно. В добро състояние". След време друг потребител пише същия кратък текст и му гърми и му казва, да си въведе нов текст, понеже се дублира. И понеже потребителя е мързелив, най-много да удари по клавиатурата и да добави " сдгафргасдфгасдфтъврдгас" след обявата.
 
Здравейте.
Сигурно има и по-елегантно решение но ми се наложи да чистя една база с вицове, повечето от които добавяни от потребители. Естествено около 30 % бяха дублаж и подходих по следният начин.
- премахнах всички "не думи" в това число формат, кавички, запетайки точки и т.н.
- всички интервали и нови редове сведох до 1 интервал. Там където бяха повече от един на куп - също.
- два вложени цикъка и проверка със http://php.net/manual/en/function.similar-text.php . Всичко което е с над 90% съвпадение - в коша.
Естествено няма как дасе хване ако заменят "Иванчо" с "Гарабет" но метода свърши прилична работа.
Поздрави.
 
Здравейте.
Сигурно има и по-елегантно решение но ми се наложи да чистя една база с вицове, повечето от които добавяни от потребители. Естествено около 30 % бяха дублаж и подходих по следният начин.
- премахнах всички "не думи" в това число формат, кавички, запетайки точки и т.н.
- всички интервали и нови редове сведох до 1 интервал. Там където бяха повече от един на куп - също.
- два вложени цикъка и проверка със http://php.net/manual/en/function.similar-text.php . Всичко което е с над 90% съвпадение - в коша.
Естествено няма как дасе хване ако заменят "Иванчо" с "Гарабет" но метода свърши прилична работа.
Поздрави.
уау супер, не я знаех тази функция. с кирилица (utf-8) добре ли се справя?
 
@bobbydigital защо не ти допада? Какво против regex-а имаш, че даже го и ползваш.

Внимавай със similar-text, че току виж се изненадаш колко е "ефективна".https://www.predpriemach.com/members/bobbydigital.3946/

@ReminD то затова първо се мисли. Не случайно съм споменал и допълнителни условия като потребител. Да не говорим, че може просто да му покажеш текста за обявата ви е добавена и тя да не е.

Просто не разбирам, защо трябва да се позволява действие, което "не е позволено и трябва да се изтрие".


 
При similar_text са показани 2 алгоритърма за текст, които са много ефективни. Аз съм ползвал Levenstein за едно приложение, което търсеше съвпадение на футболни отбори от 2 различни бази от данни. Soundex се представя по-лошо, но е и по-бърз.
 
Здравейте,
similar_text работи с кирилица и uft8. Забравих да пиша, че е чувствителна към регистъра - да се има предвид при писането на скрипта. Алгоритамът е относително бавен, но не е нещо, което да натовари съвременен компютър.
Поздрави.
 

Горе