جستجو در مطالب سایت با استفاده از MySQL Full-Text

جستجو در اطلاعات پایگاه داده را می توان جزء اولین و مهمترین نیازهای برنامه های تحت وب برشمرد که تقریبا هر برنامه نویسی در طی تجربه کاری خود با آن مواجه خواهد شد، در MySQL این جستجو از دو طریق میسر است، استفاده از دستور ساده LIKE و توابع Full-Text، دستور LIKE را در آموزش های مقدماتی به صورت جداگانه و مفصل توضیح داده ایم اما یکی از قابلیت های خوب MySQL که از نسخه 3.23 به بعد به آن اضافه شده، توابع Full-Text است که می تواند جهت کسب نتایج جستجوی سریع و تقریبا دقیق مورد استفاده قرار گیرد، هرچند برخی از قابلیت های Full-Text مربوط به کلمات انگلیسی است ولی توانایی آن برای کلمات فارسی آنقدر هست که بتوانیم بدون مشکل از آن استفاده کنیم و یا لااقل یکی از متدهای جستجو را بر مبنای آن قرار دهیم و در متدهای دیگر به فرض از LIKE استفاده نمائیم، در ادامه به توضیح اینکه چرا و چگونه از این قابلیت در کدهای PHP استفاده کنیم خواهیم پرداخت.
چرا باید از Full-Text به جای LIKE یا در کنار آن استفاده کنیم؟
واقعیت این است هر کدام از روش های بالا دارای معایب و محاسنی هستند، به عنوان مثال استفاده از دستور LIKE معمولا نتایج بیشتری را برمی گرداند، شیوه نگارش و استفاده از LIKE ساده تر و به اصطلاح دم دستی تر است اما در عوض ممکن است نتایجی نامربوط یا تکراری داشته باشیم، از طرفی جستجو با متد LIKE در پایگاه داده MySQL مخصوصا در مورد جداولی که محتوای زیادی دارند در مقایسه با Full-Text زمانبرتر است و پردازش کار در این حالت به طور معمول فشار بیشتری به سرور وارد می کند که این موضوع برای سرورهای با توان سخت افزاری پائین می تواند یک مشکل اساسی باشد، علاوه بر موارد گفته شده برخی قابلیت های Full-Text با دستور LIKE بدست نمی آیند که این به ماهیت متفاوت این دو روش برمی گردد.
ایجاد قابلیت Full-Text در جداول MySQL
قابلیت جستجوی Full-Text بر روی ستون هایی از نوع CHAR ,VARCHAR و TEXT و موتور MyISAM قابل اعمال است و هم زمان با ایجاد جدول یا بعد از آن نیز می توانیم به ستون مورد نظر آن را اضافه کنیم، به طور مثال در پرس و جوی نمونه زیر هم زمان با ساخت جدول، قابلیت جستجوی Full-Text را برای ستون فرضی title در نظر گرفته ایم:
CREATE TABLE post(
title VARCHAR(255),
FULLTEXT KEY title(title)) ENGINE=MyISAM
همان طور که اشاره شد می توانیم بعد از ساخت جدول و ستونها نیز جستجوی Full-Text را اضافه کنیم، برای افزودن این قابلیت بعد از ساختن جدول و ستونها، مقادیر SQL زیر را اجرا می کنیم:ALTER TABLE post ADD FULLTEXT(title);
به این صورت این قابلیت به جدول و ستون موجود اضافه می شود.شیوه جستجو در Full-Text
شیوه جستجو در Full-Text بر اساس MATCH() ... AGAINST صورت می گیرد:
SELECT title FROM post
WHERE MATCH (title) AGAINST ('$searchword');
برای جستجو از چند ستون در مقادیر MATCH می توانیم از کاما استفاده کنیم، همچنین می توان از قابلیت امتیاز دهی (score) این تابع نیز جهت چینش نتایج استفاده کرد:SELECT *, MATCH (title) AGAINST ('$searchword') AS score FROM post
WHERE MATCH (title) AGAINST ('$searchword') ORDER BY score ASC
باید توجه کرد که Full-Text برای ایندکس کلمات محدودیت هایی اعمال می کند، از جمله اینکه اگر کلمه ای کمتر از 4 کاراکتر باشد ایندکس نمی شود، اگر کلمه ای در بیش از 50% ردیف ها تکرار شود ایندکس نمی شود، اگر کلمه ای در لیست Stopword ها باشد ایندکس نمی شود (البته این ویژگی مخصوص حروف انگلیسی است) و نهایتا اینکه برای شروع ایندکس کلمات برای جستجوی Full-Text نیاز به وجود حداقلی از اطلاعات در ردیف ها است.قابلیت های جستجو در Full-Text
قابلیت های دیگر این تابع جستجوی IN BOOLEAN MODE است که امکانات بیشتری در اختیارمان قرار می دهد (اضافه شده به MySQL از نسخه 4.0.1)، با استفاده از این قابلیت می توانید شمول ابتدا و انتهای یک کلمه را در جستجو تعیین کنید:
SELECT * FROM post WHERE MATCH (title) AGAINST ('+phpcode -htmlcode' IN BOOLEAN MODE);
در مثال بالا اگر کاربر عبارت code را جستجو کند، تیترهای حاوی عبارت phpcode در جستجو خواهند آمد اما تیتر های حاوی عبارت htmlcode در لیست نتایج ظاهر نمی شوند، از ویژگی های جستجو در حالت BOOLEAN MODE نادیده گرفتن قانون 50% است.اما قابلیت Full-Text با امکان دیگری کامل می شود که Query Expansion نام دارد، همانطور که از معنی Expansion (توسعه) برمی آید نتایج جستجوی معمولی با Full-Text به دلیل اعمال محدودیت ها و سخت گیری در ایندکس کلمات و یا استفاده از Stopword ها، ممکن است بعضا دربرگیرنده همه نتایج نباشد، با افزودن Query Expansion حداقل به لحاظ تئوری می توانیم موارد معمولی تر را نیز در لیست جستجو نشان دهیم:
SELECT title FROM post
WHERE MATCH (title)
AGAINST ('$searchword' WITH QUERY EXPANSION);

هوشمند سازی پنل ورود و خروج سایت با PHP و MySQL
تعویض کد امنیتی Captcha با Ajax و MySQL
ساخت فید آر اس اس (RSS Feed) با استفاده از PHP و MySQL
نحوه رسم چارت و نمودار آماری با PHP و MySQL
صفحه بندی مطالب و محتوا با PHP و MySQL


با وجودی که رکورد وجود داره ولی با php و sql این دستور شمارو که اجرا می کنم هیچ رکوردی رو پیدا نمی کنه!
مشکل از چی هست؟
سپاسگزارم سوال دیگه ای دارم اگه بخوام با preg_match دنبال اسم هایی بگردم که a-z باشند چطوری تابع رو بنویسم؟
مثلا همین کد:
SELECT * FROM `card` WHERE name != ' ' and name like'%a-z%' ORDER BY id DESC LIMIT 100
من گذاشتم کاربران اسم خودشون رو فارسی بذارن ولی بعضی ها انگلیسی می نویسن من می خوام با این تابع این اسم هارو پیدا کنمبا تشکر فراوان
SELECT * FROM `card` WHERE name != ' ' and name REGEXP '[a-z]' ORDER BY id DESC LIMIT 100
در صورتی که بخواهیم جستجو به صورت حساس به حروف بزرگ و کوچک انگلیسی باشد باید پارامتر BINARY را هم اضافه کنیم:SELECT * FROM `card` WHERE name != ' ' and name REGEXP BINARY '[a-z]' ORDER BY id DESC LIMIT 100
من برای پیدا کردن خریدهایی با شماره های تکراری از این کوئری توی php استفاده می کنم
SELECT * FROM `card` where phone != '' group by phone having count(phone) > 1 and status != 1 order by id desc limit 100
که میگرده خریدهایی که شماره تکراری دارن رو برام پیدا می کنهstatus!=1
یعنی خرید موفق نباشه الان این کد زمانی کار می کنه که خرید اول ناموفق باشه و اگه خرید اول موفق و خرید دوم ناموفق باشه نشون نمیده شماره تکراری وجود داره! ولی اگه خرید اول ناموفق باشه و خرید دوم موفق باشه اونو پیدا می کنه! و اگه کلا خریدها ناموفق باشن نشون میده من میخوام کلا تکراری هایی با خرید ناموفق رو برام پیدا کنه حالا چه خرید موفق داشته باشن و چه خرید ناموفقبا تشکر از شما
SELECT * FROM `card` WHERE phone != '' GROUP BY phone HAVING COUNT(phone) > 1 AND phone IN (SELECT phone FROM `card` WHERE status = '0') ORDER BY id DESC LIMIT 100
من دیتابیس از قبل دارم که می خوام ایمپورت کنم شناسه های قبلی پر شده اند یعنی اگه شناسه مطلب دیتابیسی که دارم 10 و 12 و 14 باشه این شناسه ها الان توی جدول گرفته شده اند حجم داده ها هم زیاده الان می خوام در صورت ایمپورت کردن دیتابیس قبل شناسه های جدید بگیرن یعنی اگه آخرین مطلب کنونی 1200 باشه دیتابیسی که می خوام ایمپورتش کنم از شناسه 1201 شروع بشه!
آیا امکان داره؟
با تشکر
نکته مهم: لطفا قبل از اعمال هر گونه تغییرات و جهت جلوگیری از اشکالات پیش بینی نشده از اطلاعات هر دو جدول پشتیبان کامل داشته باشید.
امکانش هست که نمونه سورس ساده با توجه به کدهام که ارسال کردم که در حالت ساده با زدن یک باتن فقط بیاد ساعت ها رو از دیتابیس فراخوانی کنه بیان کنین و بنویسین. ممنون.
- کدهای زیر را در فایل index.html درج کنید:
<!DOCTYPE html>
<html lang="fa">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>وبگو | بررسی نتایج دیتابیس با PHP و JavaScript</title>
<!-- Webgoo.ir -->
<style>
body{
font-family:Tahoma, Geneva, sans-serif;
font-size:12px;
direction:rtl;
}
.green {
background: #98fe98;
}
.red {
background: #fe9898;
}
</style>
<script>
function ajaxRequest(id) {
var xmlHttp;
var respons;
var block = document.getElementById(id);
var value = block.innerHTML;
var result = '';
try {
xmlHttp = new XMLHttpRequest();
} catch (e) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("متاسفانه مرورگر شما از ای جکس پشتیبانی نمی کند");
return false;
}
}
}
xmlHttp.onreadystatechange = function () {
if(xmlHttp.readyState == 4 && xmlHttp.status == 200) {
respons = xmlHttp.responseText;
if(respons == value){
block.className = 'green';
} else {
block.className = 'red';
}
document.getElementById("ajax-wait").innerHTML = 'نتیجه از سرور دریافت شد.';
} else {
document.getElementById("ajax-wait").innerHTML = 'لطفا چند لحظه صبر کنید...';
}
}
xmlHttp.open("GET", "response.php?time=" + value, true);
xmlHttp.send(null);
}
</script>
</head>
<body>
<noscript>جاوا اسکریپت در مرورگر شما پشتیبانی نمی شود یا غیرفعال است!</noscript>
<button id="button-1" type="button" onclick="ajaxRequest('12-33');">ارسال درخواست ای جکس</button>
<div id="12-33">12:33</div>
<div id="ajax-wait"></div>
</body>
</html>
- این کدها را هم در فایل response.php قرار دهید:<?php
/*
//اتصال به دیتابیس
$conn = mysqli_connect("localhost", "dbuser", "dbpass", "dbname");
if(!$conn) {
echo "PHP & MySQL Connection: Error! " . mysqli_connect_errno() . ' - ' . mysqli_connect_error();
exit;
} else {
//نام جدول
$tbl_name = "tblname";
//دریافت امن زمان از سمت کاربر
$time = mysqli_real_escape_string($conn, $_GET['time']);
//استخراج ردیف ها
$query = mysqli_query($conn, "SELECT * FROM $tbl_name WHERE time = $time LIMIT 1");
if(!$query){
echo "Selecting From Table $tbl_name: Error! " . mysqli_error($conn) . '<br>';
} else{
$db_row = null;
while($row = mysqli_fetch_array($query)){
$db_row = $row['time'];
}
}
//چاپ زمان
echo $db_row;
}
//پایان اتصال
mysqli_close($conn);
*/
//جهت تست بدون اتصال دیتابیس
$time = '12:33';
echo $time;
?>
اکنون با فراخوانی فایل index.html در مرورگر از طریق لوکال هاست نمونه کد قابل تست و بررسی است (در صورت عدم آشنایی با این مقوله می توانید در وب در خصوص نحوه کار لوکال هاست و برنامه WampServer جستجو کنید)، مشابه این رویه باید در مورد کدهای اصلی شما اعمال شود که البته در حالت کاربردی پیچیده گی های بیشتری هم دارد.یک سوال داشتم از خدمتتان و اون اینکه من چند باتن دارم مثلا باتنی به نام 25 یا 28 یا 30 میخوام با زدن هر باتن بیاد اطلاعات رو از دیتابیس فراخوانی کنه و ساعت های اون تاریخ که تو دیتابیس ذخیره شده را بیاد برام نمایش بده . مثلا با زدن باتن تاریخ 25ام بیاد ساعت های که مربوط به تاریخ 25 هست را برام نمایش بده .
سوال دومم هم اینکه حالا ساعت هایی که اومده نمایش داده شده را با ساعت هایی را که تو دایوم دارم مقایسه کنه و اگر بین اونا بود بیاد مثلا بگراندش رو سبز کنه این نمونه سورس هام هست با این نمونه سورس ها دقیقا میاد به تاریخ روز تمامی ساعت های ذخیره شده را به درستی فراخوانی می کنه فقط من میخوام با زدن باتن هر تاریخ پایین صفحه این کار انجام بشه . خیلی ممنون و تشکر می کنم که همیشه کمک حال من و سایر دوستان هستین.
حذف شد
متغیر تعریف کردم چون کدها رو کامل ندادم فقط این بخشش رو مشکل دارم باز اگر میشه لطفا راهنمایی کنید و کد صحیح رو جهت جستجو و چرا متغیر کپی نمیشه
SELECT * FROM table WHERE col_1 = 'value_1' AND col_2 = 'value_2'
SELECT * FROM table WHERE col_1 = 'value_1' OR col_2 = 'value_2'
کد من اینه لطفا طبق معمول راهنمایی کنید
<form action="profile.php" method="post">
<input type="text" name="query" size=50 placeholder="نام کاربر مورد نظر را در اینجا تایپ کنید ...">
<input type="submit" name="search" size=40 value="جستجو کن">
</form><br>
if(isset($_POST['search'])){
$searchQuery = $_POST['query'];
$getUsers = mysqli_query($db, "SELECT * FROM users WHERE u_username LIKE '%$searchQuery%'");
$profile_user = $row['u_username'];
while($row = mysqli_fetch_array($getUsers)){
echo '<a href="profile.aspx?user='.$user_username.'" target="_blank">' ;
echo $row['u_username'] . "<br>";
echo '</a>' ;
}
}
?>
لینک صفحه مورد نظر https://afra.ory.ir/profile.aspx?user=admin
میخوم یوزرهایی که نمایش میده رو لینک دار کنم که بشه روش کلیک کرد بری پروفایل که متاسفانه هر جور متغیر مینویسم خطا میده...از صبح درگیرم گاهی واقعا نمیتونم پروژه رو تنهایی پیش ببرم... ضمنا به رسم ادب و قدر دانی لینکتون کردم و توی درباره ما هم ازتون تشکر کردم...
تکه کدی که درج کرده اید چند مشکل دارد:
- قسمت مربوط به مقداردهی متغیر profile_user باید داخل حلقه while باشد، آرایه row در حلقه مقداردهی می شود و قبل از آن تعریف نشده است!
- متغیر user_username که در حلقه استفاده شده ظاهرا تعریف نشده است، متغیر profile_user را خارج از حلقه تعریف کرده اید اما در حلقه از متغیر دیگری استفاده کرده اید.
درک ساختار PHP از ویژال بیسیک معمولا ساده تر است!
در خصوص لینک و درباره ما نظر لطف شما است، هر زمان صلاح بود می توانید این موارد را حذف کنید، هیچ اجباری به تشکر و لینک نیست، خوشحال می شویم کمکی در حد توان انجام دهیم.
به این کلمات دقت کنید:
قابلمه، غابلمه
پیچگوشتی، پیچ گوشتی
mohamad, mohammad
hosein, hossein
و ازین قبیل ...
در مورد چنین خواص جست و جو هوشمند حتما باید دیتابیسی کامل از کلمات آلترناتیو ایجاد کنیم و یا روشی از طریق فقط کدنویسی وجود داره؟ این مطلبی که شما گذاشتید بسیار مبتدی بوده و برای جست و جوهای حرفه ای چندان نتایج مطلوبی نخواهد داشت. لطفا در مورد جست و جوی هوشمند کامل توضیح دهید. سپاس
چجوری میشه کوئری رو ست کرد که نزدیکترین کلمه جستجو رو نمایش بده تو خروجی . یعنی کاربر با غلط املایی سرچ کرد نزدیک ترین کلمه رو نشون بده . به این صورت مثلا :
مثلا وقتی من میخوام کلمه abc رو سرچ کنم اشتباهی مینویسم axc یا abccc در حالت عادی نمیاره میخوام رو حساسیت کلمه کار کنم که حتی اگه دو تا حرف درست بود نزدیک ترین کلمه رو جستجو کنه بیاره یا صحیح همون کلمه رو جستجو کنه و پیشنهاد بده به کاربر برای جستجو
خداخیرتون بده...
خداقوت
از کمکی که در مساله دوباره نویسی آدرسها بهم داشتید، متشکرم... من دارم یک فیلترینگ درست میکنم که کاربرها مطلبی رو که آپلود کردن اول بره تو لیست سیاه بگرده اگر کلمات عنوان و متن، در لیست سیاه نیست، مطلب اضافه بشه.
برای کوئری گرفتن از دستور like استفاده میکنم.
مشکلی که هست اینه که باید حتما کلمات، دقیقاً شبیه به کلماتی باشن که من توی جدول نوشتم.
مثلا اگه کلمه در متن ارسالی فاصله ای داشته باشه کوئری نمیگیره...
راهکاری دارید؟
متشکرم
آشنایی بیشتر با بحث REGEXP در MySQL:
https://www.tutorialspoint.com/mysql/mysql-regexps.htm
$query="SELECT * FROM balatarin WHERE MATCH (title) AGAINST ('$value')";
این به درستی کار میکند و همچنین:$query="SELECT * FROM balatarin WHERE MATCH (describe) AGAINST ('$value')";
هم به درستی کار میکند ولی حالت ترکیبی که در مثال شما هم هست کار نمیکند:کد PHP:
$query="SELECT * FROM balatarin WHERE MATCH (title,describe) AGAINST ('$value')";
مشکل از چیه؟MATCH (title) AGAINST ('$value') OR MATCH (describe) AGAINST ('$value')
مشکل استفاده از چند ستون در MATCH ممکن است به دلیل استفاده از موتور InnoDB باشد.بنده میخام اطلاعات یک جدول بالای 100 هزار تا سطر را بصورت PAGINATION در اپلیکیشن اندروید لیست کنم.
جدول من 12 تا ستون داره شامل اسم و فامیل و لینک و بازدید و بیوگرافی و تاریخ و غیره ...
در لیست کردن اطلاعات در لیست سطر ها در اندروید فقط نیاز به لینک عکس و آی دی و نام و فامیل نیازه و کافیه.
بوقتی اپ در اندروید باز میشه 20 عدد سطر از دیتابیس انتخاب و لیست میشه (در این انتخاب فقط همون ستون های مورد Select میشه)
بعد از اسکرول کردن همینجور بصورت pagination بیست تا دیگه و .. و ..)
بعدش اگه یوزر سطری رو انتخاب کرد با آی دی سطر میرم سمت سرور تا ستون های دیگر آن را بیارم.
حال میخام بدونم تا چه حد تفاوت وجود داره بین اینکه اگر من از همان ابتدا تمام ستون های سطر را انتخاب کنم که دیگه برای دریافت ستون های دیگر نرم سمت سرور؟
چقدر تفاوت وجود داره بین انتخاب 5 ستون و 8 یا 10 ستون؟ اینکه من تعداد درخواستها رو کم کنم بهتره یا اینکه تعداد ستون ها رو کمتر کنم بهینه تره؟
(بحث سر کارایی و بهینه بودن حافظه در سرور است)
همه جا میگن از Select * پرهیز کنید. ولی میخام بدونم تفاوت بین 5 ستون تا 8 ستون چقدره؟
ممنون
- به سوالات کلی، زمانبر، مبهم و مشکلاتی که تلاشی برای رفع آنها نکرده باشید پاسخ مختصر داده شده یا به بخش برنامه نویسی اختصاصی ارجاع داده می شوند.
- کدها و اسکریپت های طولانی را ترجیحا در یک صفحه وب آنلاین یا به صورت حساب موقت و آزمایشی قرار دهید تا امکان بررسی دقیق مشکل و خطایابی میسر باشد.
- تمام دیدگاه های ارسالی خوانده شده و برای هر کاربر مدت زمان لازم جهت پاسخگویی در نظر گرفته می شود، لطفا از طرح سوالات متعدد در بازه زمانی کوتاه خودداری کنید.