ComBioLaw.De » Blog » เขียนโปรแกรม » โปรแกรมค้นหาข้อมูลสำหรับ CMS

โปรแกรมค้นหาข้อมูลสำหรับ CMS

imageการเขียนโปรแกรมค้นหาข้อมูลบนเวบไซท์นั้น เป็นเรื่องที่ยากและซับซ้อนมาก ลำพังเพียงแค่หาข้อมูลให้เจออย่างเดียวนั้น ไม่ยากครับ แต่ศาสตร์และศิลป์ของโปรแกรมค้นหาข้อมูลบนหน้าเวบไซท์นั้น อยู่ที่การจัดลำดับความสำคัญของข้อมูลที่หาเจอมากกว่า นี่เป็นเหตุผลหนึ่ง ที่ทำให้ Google ได้รับความนิยม เพราะการจัดลำดับความสำคัญข้อมูลของ Google ดี ทำให้ผู้ใช้เจอข้อมูลที่ต้องการเร็วขึ้น

มีอยู่ช่วงหนึ่ง BioLawCom ของเราจึงตัดสินใจใช้ Google เป็นเครื่องมือในการหาข้อมูลบนหน้าเวบไซท์ ในระยะแรกใช้ได้ผลดีทีเดียว เพราะช่วงนั้น Google เข้ามาเก็บข้อมูลของพวกเราค่อนข้างเยอะ และบ่อย แต่สองสามอาทิตย์ที่ผ่านมา ผมหาอะไรบน BioLawCom ผ่าน Google ไม่เคยเจอ ส่วนมาก Google จะส่งข้อมูลเก่าเก็บมาให้ ผมจึงเดาว่า Google มีการจัดข้อมูลใหม่ ข้อมูลใหม่ที่เคยเก็บไปหายเกลี้ยง

ทำให้ผมรู้สึกว่า การหาข้อมูลบนหน้าเวบไซท์ตัวเองผ่าน Search Engine ยอดนิยม ดีและง่ายก็จริง แต่เราก็ต้องไปอิงกับบริษัทเหล่านี้มากเกินไป วันไหนเขาเลิกสนใจเรา เราก็หาอะไรบนหน้าเวบตัวเองไม่เจอ ผมก็เลยต้องแก้ปัญหา ที่หาทางแก้ไม่ได้มานาน (เพราะดันเขียน CMS ใช้เอง) นั่นคือ เขียนโปรแกรมค้นหาข้อมูลเอง ...

เขียนโปรแกรม เขียนโปรแกรม

bow_der_kleine bow_der_kleine

โจทย์สำคัญของการเขียนโปรแกรมค้นหาคือ จะใช้เงื่อนไขใดในการเรียงลำดับความสำคัญของข้อมูล ? สำหรับ Search Engine ระดับ Hi-End อย่าง Google หรือ Yahoo ก็จะมีเงื่อนไขต่าง ๆ กันเช่น

  • จำนวนคำที่ค้นหาในปรากฏในหน้าเวบไซท์ ยิ่งทีคำที่ค้นมาก ความสำคัญก็จะมากตามมา
  • มีเวบไซท์อื่นลิงก์มายังหน้าเวบไซท์นี้มากน้อยเพียงใด
  • ความสดใหม่ของข้อมูล
  • จำนวนคนดูของหน้าเวบไซท์นั้น ๆ
ซึ่งโปรแกรม CMS ปกติ ไม่สามารถใช้เงื่อนไขดังกล่าวในการเรียงลำดับข้อมูลได้ เพราะซับซ้อน และต้องใช้พลังการประมวลผลของ Server มากเกินความจำเป็น วิธีการค้นหาข้อมูลของโปรแกรม CMS ส่วนมากจึงมีสองแบบคือ

1 . ค้นหาโดยใช้ SQL โดยใช้ WHERE และ ORDER BY ยกตัวอย่างเช่น

SELECT id, topic, content FROM content WHERE topic LIKE '%something%' OR content LIKE '%something%' ORDER BY update_time
plain code

เป็นวิธีที่ CMS ส่วนมากเลือกใช้ เช่น Wordpress, Joomla ข้อดีคือ ง่าย และ ใช้พลังการประมวลผลของ Server ต่ำ แต่ข้อเสียคือ การเรียงลำดับข้อมูลทำได้ไม่ดี นอกจากนี้ข้อมูลที่อยู่ใน HTML-Tag ยังได้รับการค้นหา เหมือนข้อมูลปกติ ผลที่ได้จึงไม่เป็นที่น่าพอใจ

ข้อเสียอีกอย่างของวิธีนี้คือ หากมีข้อมูลที่เก็บไว้หลายตาราง การเรียงลำดับข้อมูลก็จะยุ่งยากไปกันใหญ่

2. ค้นหาผ่านตาราง Keywords ตัว CMS ต้องสร้างตาราง Keywords ค้นหาว่าหน้าไหนมี Keywords คำว่าอะไรบ้าง จำนวนกี่คำ เมื่อมีการค้นหาข้อมูลเกิดขึ้น โปรแกรมจะเทียบข้อมูลที่ค้นหากับตาราง Keywords เรียงลำดับความสำคัญตามตาราง Keywords แล้วสร้างลิ้งก์ไปยังข้อมูลที่อยู่ในตาราง Keywords วิธีนี้ได้ผลการค้นหาเป็นที่น่าพอใจมาก แต่ปัญหาอยู่ที่การสร้างตาราง Keywords นี่แหละครับ เพราะเราไม่รู้ว่าคำใดควรอยู่ในตารางบ้าง เราควรอัพเดดตารางบ่อยเพียงใด ซึ่งการสร้างตาราง Keywords เป็นเรื่องที่ซับซ้อนและใช้พลังการประมวลผลของ Server ที่สูงมาก Drupal เป็น CMS ตัวหนึ่งครับ ที่ใช้วิธีการดังกล่าว

เดิมทีผมคิดจะใช้วิธีที่สองในการเขียนโปรแกรมค้นหาให้ X-BLC แต่มันเป็นวิธีที่ยุ่งยากเกินไป และอาจจะไม่คุ้ม หากมีการสร้างตาราง Keywords ที่ดีมาก ๆ แต่ได้รับการใช้งานสองสัปดาห์ครั้ง กลายเป็นเรื่องที่ได้ไม่คุ้มเสียไป ผมเลยต้องหาวิธี ที่อยู่ตรงกลางระหว่างสองวิธี กล่าวคือ ผลจากการค้นหาไม่ต้องดีมาก แต่อยู่ในระดับที่รับได้ และเป็นที่น่าพอใจ และไม่ใช้ทรัพยากรของ Server มากเกินไป

วิธีที่ผมใช้คือ แยกการค้นหาออกเป็นสามระดับตามความสำคัญ ระดับแรกคือค้นหาจากชื่อเรื่อง หากเรื่องใดมีชื่อเรื่องตรงกัคำที่ค้นหา แสดงว่ามีความเป็นไปได้สูง ที่เรื่องนั้นตรงกับความต้องการของการค้นหา ระดับที่สองคือ ค้นหาจาก tags และระดับสุดท้ายคือ ค้นหาจากเนื้อเรื่อง

นอกจากนี้ในแต่ระดับยังมีการเรียงลำดับตามเวลาที่แก้ไขข้อมูล อันที่จริงเงื่อนไขนี้ไม่ดีเท่าที่ควร การเรียงลำดับตามจำนวน Keywords เป็นวิธีการที่ดีกว่า แต่ปัญหาก็คือ การจะหาว่า การเรียงลำดับตามจำนวน Keywords ได้นั้น เราต้องเรียกข้อมูลทั้งหมดที่มี Keywords ที่ต้องการออกมาจากฐานข้อมูลก่อน จากนั้นจึงหาว่าข้อมูลชุดใด มีจำนวน Keywords ที่ต้องการหาจำนวนเท่าใด แล้วจึงสามารถเรียงลำดับข้อมูบตามจำนวน Keywords ได้ ซึ่งเป็นวิธีที่ใช้ทรัพยากร Server เยอะมาก แต่การเรียงลำดับตามเวลาการแก้ไขนั้น ทำได้ง่ายโดยผ่านคำสั่ง SQL

ซึ่งโปรแกรมการค้นหาข้อมูลที่ว่า หน้าตาประมาณนี้ครับ (ยาวเชียว)

<?php
function search($search_tables){
	if(strlen($_GET[search])){
		$search_perpage = 10;
		$result_count = 0 ;
		$begin_page = abs($_GET[page])*abs($search_perpage);
		$end_page   = (1+abs($_GET[page]))*abs($search_perpage);
		$modes = array('topic', 'tag', 'content');
		if(count(split('+',$_GET[search])) > 1){
			$plus = true;
			$words = split('+',$_GET[search]);
		}
		else{
			$plus = false;
			$words = split(' ',$_GET[search]);
		}
 
		foreach($modes as $mode){
			foreach($search_table as $table){
				if(isset($ids[$table[table]])) $id = $ids[$table[table]];
				$query  = "SELECT id,$table[topic],$table[content] FROM $table[table] ";
				if ($plus) $logical = 'AND';
				else $logical = 'OR ';
				$where = '';
				foreach($words as $wordplus){
					$where .= $table[$mode]." LIKE '%$wordplus%' $logical ";
				}
				$query .= "WHERE ( ".substr($where,0,-4)." )";
				$query .= " $id ORDER BY $table[update] DESC";
				$result = db_array($query);
				foreach($result as $row){
					$ids[$table[table]] = $ids[$table[table]].' AND id != '.$row[id];
					if($result_count >= $begin_page AND $result_count < $end_page){
						$content_result = textsearch($row[$table[content]], $words);
						if($content_result[0]){
							$search_result[] = Array(url => $table[url].'/'.$row[id],
							topic => $row[$table[topic]],
							content => "<b>($content_result[0] keywords)</b><br>".$content_result[1]);
						}else{
							$result_count = $result_count-1;
						}
					}
					$result_count = $result_count+1;
				}
				unset($result);
			}
		}
	}
	$title = $lang[search].' : '.$_GET[search].' ('.$result_count.' results )';
	do_something_to_render_page($title,$search_result);
}
 
function textsearch($text, $searchs){
	$text = str_replace("<br />","n",$text);
	$content_text  = strip_tags($text);
	$content_split = split($searchs[0],$content_text);
	if(count($content_split) > 1){
		$pre_content = split("n",$content_split[0]);
		$content_result .= $pre_content[count($pre_content)-2].' ';
		$content_result .= $pre_content[count($pre_content)-1].' ';
		$content_result .= $searchs[0];
		$sub_content = split("n",$content_split[1]);
		$content_result .= $sub_content[0].' ';
		$content_result .= $sub_content[1].' ';
		$content_result = '... '.$content_result.' ...';
		foreach($searchs as $search){
			$keywords += count(split($search,$content_text));
			$content_result = str_replace($search,"<b class="keyword">$search</b>",$content_result);
		}
	}else{
		$keywords = 0;
		$content_result .= '' ;
	}
	return array($keywords, $content_result);
}
 
function db_array($query){
	$state = db_query($query);
	$data = array();
	while($row=mysql_fetch_assoc($state)){
		$data[]=$row;
	}
	return $data;
}
 
function db_query($query){
		$dbh = mysql_pconnect(DB_HOST,DB_USER,DB_PASS):
		mysql_select_db(DB_NAME,$dbh);
		$result = mysql_db_query(DB_NAME,$query,$dbh);
		return $result;
}
 
?>
plain code

ในโปรแกรมนี้ประกอบด้วยฟังก์ชันต่าง ๆ 4 ฟังก์ชันคือ

  1. search() สำหรับการค้นหาข้อมูล
  2. textsearch() สำหรับแปลงเนื้อหาของข้อมูลที่หาได้ อยู่ในรูปแบบที่เข้าใจง่าย
  3. db_array() เตรียมข้อมูลที่ได้จากฐานข้อมูล ให้อยู่ในรูปแบบ array
  4. db_query() ติดต่อกับฐานข้อมูล
สำหรับฟังก์ชั่น search() รับ parameter มาหนึ่งตัวแปรคือ $search_tables เพื่อใช้กำหนดว่าจะค้นหาข้อมูลในตารางใด และคอลัมน์ใดบ้าง ตัวแปร $search_tables มีหน้าตาประมาณนี้ครับ

Array(
	'0' =>Array(
		'table' => 'blog',
		'topic' => 'topic',
		'content' => 'content',
		'tag' => 'freetags',
		'update' => 'publish_time',
		'url' => 'blog'),
	'1' =>Array(
		'table' => 'article',
		'topic' => 'topic',
		'content' => 'content',
		'tag' => 'freetags',
		'update' => 'publish_time',
		'url' => 'article'),
	'2' =>Array(
		'table' => 'gallery',
		'topic' => 'topic',
		'content' => 'content',
		'tag' => 'freetags',
		'update' => 'publish_time',
		'url' => 'gallery')
)
plain code

ในตัวอย่างเป็นการตั้งค่าตามโปรแกรม X-BLC หากใครต้องการนำโปรแกรมนี้ไปใช้ ก็ต้องปรับโครงสร้างตัวแปรให้เข้ากับตารางในฐานข้อมูลอีกครั้งหนึ่งครับ โดยในตัวแปร $search_tables เป็น assosiate array ที่มีคีย์หกคีย์ดังนี้ครับ

  • table ชื่อตารางที่ต้องการค้นหา

  • topic ชื่อคอลัมน์ที่ใช้เก็บชื่อเรื่องในตาราง table

  • content ชื่อคอลัมน์ที่ใช้เก็บเนื้อหา
  • tag ชื่อคอลัมน์ที่ใช้เก็บ tag

  • update ชื่อคอลัมน์ทีจะใช้จัดเรียงข้อมูลตามเวลาการแก้ไข
  • url ใช้สำหรับสร้าง link ไปยังข้อมูลในการแสดงผลที่ได้จากการค้นหา
ทีนี้ก็มาดูการทำงานของโปรแกรมกันครับ โดยภาพรวมก็ดังที่อธิบายไปแล้วข้างต้นครับ คือโปรแกรมจะหาข้อมูลในสามระดับ คือ ชื่อเรื่อง tag และเนื้อหา ในแต่ละระดับก็จะหาข้อมูลในตารางต่าง ๆ ที่เรากำหนดโดย $search_tables โดยเรียงข้อมูลตามเวลาที่ข้อมูลได้รับการแก้ไขครั้งล่าสุด ข้อมูลที่ได้จะถูกเก็บไว้ในตัวแปรชื่อ $search_result ครับ สำหรับรายละเอียดการทำงานของโปรแกรมมีดังนี้ครับ

  • บรรทัดที่ 3 ตรวจสอบว่ามีคำที่ใช้ค้นหาหรือไม่ ด้วยการตรวจสอบจำนวนตัวอักษรของคำ ที่ต้องตรวจสอบก็ว่า หากโปรแกรมค้นหาคำที่มีความยาวตัวอักษรเป็นศูนย์ ข้อมูลทั้งหมดที่อยู่ในฐานข้อมูลก็จะเข้าเงื่อนไขดังกล่าว ทำให้โปรแกรมเกิดอาการ memory overflow ได้
  • บรรทัดที่ 8 กำหนดการเรียงลำดับในการค้นหา
  • บรรทัดที่ 9 ตรวจสอบการแยกคำ หากมีการแยกคำโดยใช้เครื่องหมายบวก (+) โปรแกรมก็จะกำหนดเงื่อนไขในการค้นหาเป็น และ (AND) หมายความว่า ค้นหาเฉพาะข้อมูลที่มีคำทั้งหมดที่กำหนดเท่านั้น หากมีการแยกคำโดยการเว้นวรรค เงื่อนไขในการค้นหาก็จะเป็น หรือ (OR) ข้อมูลที่มีคำใดคำหนึ่งที่เรากำหนด ก็จะเข้าเงือนไขดังกล่าว
  • บรรทัดที่ 20 และ บรรทัดที่ 32 โปรแกรมจะจำข้อมูลที่ได้รับการค้นหาแล้ว เพื่อไม่ให้ค้นหาข้อมูบดังกล่าวซ้ำอีกรอบ
  • บรรทัดที่ 6, บบรทัดที่ 7 และ บรรทัดที่ 33 ใช้กำหนดว่าข้อมูลใดบ้างที่จะได้รับการแสดงผลในแต่ละหน้า จำนวนข้อมูลในการแสดงผลในหนึ่งหน้า กำหนดโดยตัวแปร $search_perpage ปกติแล้วการแบ่งข้อมูลออกเป็นหลาย ๆ หน้า จะกำหนดโดย LIMIT ใน SQL แต่เนื่องจากเป็นการหาข้อมูลในหลาย ๆ ตารางมารวมกัน จึงไม่สามารถใช้วิธีดังกล่าวได้
  • บรรทัดที่ 40 และ บรรทัดที่ 43 นับจำนวนข้อมูลที่หาเจอ
  • บรรทัดที่ 55 แยก HTML-Tag ออกจากข้อมูลปกติ
  • บรรทัดที่ 67 นับจำนวน Keywords ที่อยู่ในข้อมูล
การใช้งานก็สามารถใช้งานผ่าน GET-Form ได้ตามปกติ จากการทดลองใช้ ผลที่ได้เป็นที่น่าพอใจทีเดียวครับ ผลการค้นหาตรงกับความต้องการในระดับหนึ่ง แต่โปรแกรมยังทำงานช้าอยู่ ต้องเสริมระบบ cache ลงไป

โปรแกรมนี้เหมาะสำหรับเวบไซท์ขนาดกลางเท่านั้นครับ เงื่อนไขในการเรียงลำดับข้อมูล ยังไม่เพียงพอสำหรับเวบไซท์ที่มีข้อมูลปริมาณมาก สำหรับเวบไซท์ขนาดใหญ่ การเรียงลำดับข้อมูลตามจำนวน Keywords เป็นวิธีที่เหมาะสมที่สุดครับ

16 Mar 07 | by | tags เขียนโปรแกรม Search Search Engine Google CMS PHP SQL X-BLC

read 6738

<<Dancing || มหาวิทยาลัยใต้สะดือ>>

พี่ไท้

จากการตรวจสอบโค้ดแล้วพบว่า น่าจะวนหลาย ๆ รอบมากเลยนะครับ ^o^ แบบนี้คงจะกิน CPU ของเครื่อง hosting ที่พวกเราเช่าใช้น่าดู ถ้ามีการแทรกคำสั่งที่สามารถ yield เพื่อคืนเวลาให้กับ CPU ได้ คงจะดีไม่น้อยเลย

21 Mar 07

bow_der_kleine

คือผมยังไม่เข้าใจ yield ครับพี่ไท้ ขอความกรุณาพี่ไท้อธิบายด้วยครับ :D

แต่ขั้นต่อไปที่ผมจะเพิ่มเข้ามา คือ ระบบ cache + incremental search ครับ แต่ตอนนี้ยังแก้ไม่ตกอยู่เหมือนกัน หากสำเร็จก็คงแก้ปัญหาเรื่องเปลืองพลัง CPU ได้ระดับหนึ่งครับ

22 Mar 07

ความคิดเห็น (click here to comment)

Search

Navigation

รวมลิงก์น่าสนใจ

ความเคลื่อนไหว

Login

name password

ลืมรหัสผ่าน