By Ibrahim Diallo
Published Jul 2 2014 ~ 16 minutes readSearch is an important feature on a website. When my few readers want to look for a particular passage on my blog, they use the search box. It used to be powered by Google Search, but I have since then changed it to my own home-brewed version not because I can do better but because it was an interesting challenge.
If you are in a hurry and just want your site to be searchable, well do what I did before, use Google.
// In search.php file $term = isset($_GET["query"])?$_GET["query"]: ""; $term = urlencode($term); $website = urlencode("www.yourwebsite.com"); $redirect = "https://www.google.com/search?q=site%3A{$website}+{$term}"; header("Location: $redirect"); exit;
What it does is pretty simple. Get the term passed by the user, and forward it to Google search page. Limit the search result to our current domain using the site: keyword in the search query. All your pages that are indexed by Google will be available through search now. If you do want to handle your search in house however, then keep reading.
Homemade Search Solution
Before we go any further, try using the search box on this blog. It uses the same process that I will describe below. If you feel that this is what you want then please continue reading.
This solution is catered to small websites. I make use of LIKE with wild cards on both ends, meaning your search cannot be indexed. This means the solution will work fine for your blog or personal website that doesn"t contain tons of data. Port it to a bigger website and it might become very slow. MySQL offers Full Text Search which is not what we are doing here.
Note: If you have 5000 blog posts you are still fine. .
We will take the structure of this blog as a reference. Each blog post has:
- A title p_title
- A url p_url
- A summary p_summary
- A post content p_content
- And catergories category.tagname
For every field that matches with our search term, we will give it a score. The score will be based on the importance of the match:
// the exact term matches is found in the title $scoreFullTitle = 6; // match the title in part $scoreTitleKeyword = 5; // the exact term matches is found in the summary $scoreFullSummary = 5; // match the summary in part $scoreSummaryKeyword = 4; // the exact term matches is found in the content $scoreFullDocument = 4; // match the document in part $scoreDocumentKeyword = 3; // matches a category $scoreCategoryKeyword = 2; // matches the url $scoreUrlKeyword = 1;
Before we get started, there are a few words that do not contribute much to a search that should be removed. Example "in","it","a","the","of" ... . We will filter those out and feel free to add any word you think is irrelevant. Another thing is, we want to limit the length of our query. We don"t want a user to write a novel in the search field and crash our MySQL server.
// Remove unnecessary words from the search term and return them as an array function filterSearchKeys($query){ $query = trim(preg_replace("/(\s+)+/", " ", $query)); $words = array(); // expand this list with your words. $list = array("in","it","a","the","of","or","I","you","he","me","us","they","she","to","but","that","this","those","then"); $c = 0; foreach(explode(" ", $query) as $key){ if (in_array($key, $list)){ continue; } $words = $key; if ($c >= 15){ break; } $c++; } return $words; } // limit words number of characters function limitChars($query, $limit = 200){ return substr($query, 0,$limit); }
Our helper functions can now limit character count and filter useless words. The way we will implement our algorithm is by giving a score every time we find a match. We will match words using the if statement and accumulate points as we match more words. At the end we can use that score to sort our results
Note: I will not be showing how to connect to MySQL database. If you are having problems to efficiently connect to the database I recommend reading this .
Let"s give our function a structure first. Note I left placeholders so we can implement sections separately.
Function search($query){ $query = trim($query); if (mb_strlen($query)===0){ // no need for empty search right? return false; } $query = limitChars($query); // Weighing scores $scoreFullTitle = 6; $scoreTitleKeyword = 5; $scoreFullSummary = 5; $scoreSummaryKeyword = 4; $scoreFullDocument = 4; $scoreDocumentKeyword = 3; $scoreCategoryKeyword = 2; $scoreUrlKeyword = 1; $keywords = filterSearchKeys($query); $escQuery = DB::escape($query); // see note above to get db object $titleSQL = array(); $sumSQL = array(); $docSQL = array(); $categorySQL = array(); $urlSQL = array(); /** Matching full occurrences PLACE HOLDER **/ /** Matching Keywords PLACE HOLDER **/ $sql = "SELECT p.p_id,p.p_title,p.p_date_published,p.p_url, p.p_summary,p.p_content,p.thumbnail, ((-- Title score ".implode(" + ", $titleSQL).")+ (-- Summary ".implode(" + ", $sumSQL).")+ (-- document ".implode(" + ", $docSQL).")+ (-- tag/category ".implode(" + ", $categorySQL).")+ (-- url ".implode(" + ", $urlSQL).")) as relevance FROM post p WHERE p.status = "published" HAVING relevance >
In the query, all scores will be summed up as the relevance variable and we can use it to sort the results.
Matching full occurrences
We make sure we have some keywords first then add our query.
If (count($keywords) > 1){ $titleSQL = "if (p_title LIKE "%".$escQuery."%",{$scoreFullTitle},0)"; $sumSQL = "if (p_summary LIKE "%".$escQuery."%",{$scoreFullSummary},0)"; $docSQL = "if (p_content LIKE "%".$escQuery."%",{$scoreFullDocument},0)"; }
Those are the matches with higher score. If the search term matches an article that contains these, they will have higher chances of appearing on top.
Matching keywords occurrences
We loop through all keywords and check if they match any of the fields. For the category match, I used a sub-query since a post can have multiple categories.
Foreach($keywords as $key){ $titleSQL = "if (p_title LIKE "%".DB::escape($key)."%",{$scoreTitleKeyword},0)"; $sumSQL = "if (p_summary LIKE "%".DB::escape($key)."%",{$scoreSummaryKeyword},0)"; $docSQL = "if (p_content LIKE "%".DB::escape($key)."%",{$scoreDocumentKeyword},0)"; $urlSQL = "if (p_url LIKE "%".DB::escape($key)."%",{$scoreUrlKeyword},0)"; $categorySQL = "if ((SELECT count(category.tag_id) FROM category JOIN post_category ON post_category.tag_id = category.tag_id WHERE post_category.post_id = p.post_id AND category.name = "".DB::escape($key)."") > 0,{$scoreCategoryKeyword},0)"; }
Also as pointed by a commenter below, we have to make sure that the these variables are not empty arrays or the query will fail.
// Just incase it"s empty, add 0 if (empty($titleSQL)){ $titleSQL = 0; } if (empty($sumSQL)){ $sumSQL = 0; } if (empty($docSQL)){ $docSQL = 0; } if (empty($urlSQL)){ $urlSQL = 0; } if (empty($tagSQL)){ $tagSQL = 0; }
At the end the queries are all concatenated and added together to determine the relevance of the post to the search term.
// Remove unnecessary words from the search term and return them as an array function filterSearchKeys($query){ $query = trim(preg_replace("/(\s+)+/", " ", $query)); $words = array(); // expand this list with your words. $list = array("in","it","a","the","of","or","I","you","he","me","us","they","she","to","but","that","this","those","then"); $c = 0; foreach(explode(" ", $query) as $key){ if (in_array($key, $list)){ continue; } $words = $key; if ($c >= 15){ break; } $c++; } return $words; } // limit words number of characters function limitChars($query, $limit = 200){ return substr($query, 0,$limit); } function search($query){ $query = trim($query); if (mb_strlen($query)===0){ // no need for empty search right? return false; } $query = limitChars($query); // Weighing scores $scoreFullTitle = 6; $scoreTitleKeyword = 5; $scoreFullSummary = 5; $scoreSummaryKeyword = 4; $scoreFullDocument = 4; $scoreDocumentKeyword = 3; $scoreCategoryKeyword = 2; $scoreUrlKeyword = 1; $keywords = filterSearchKeys($query); $escQuery = DB::escape($query); // see note above to get db object $titleSQL = array(); $sumSQL = array(); $docSQL = array(); $categorySQL = array(); $urlSQL = array(); /** Matching full occurences **/ if (count($keywords) > 1){ $titleSQL = "if (p_title LIKE "%".$escQuery."%",{$scoreFullTitle},0)"; $sumSQL = "if (p_summary LIKE "%".$escQuery."%",{$scoreFullSummary},0)"; $docSQL = "if (p_content LIKE "%".$escQuery."%",{$scoreFullDocument},0)"; } /** Matching Keywords **/ foreach($keywords as $key){ $titleSQL = "if (p_title LIKE "%".DB::escape($key)."%",{$scoreTitleKeyword},0)"; $sumSQL = "if (p_summary LIKE "%".DB::escape($key)."%",{$scoreSummaryKeyword},0)"; $docSQL = "if (p_content LIKE "%".DB::escape($key)."%",{$scoreDocumentKeyword},0)"; $urlSQL = "if (p_url LIKE "%".DB::escape($key)."%",{$scoreUrlKeyword},0)"; $categorySQL = "if ((SELECT count(category.tag_id) FROM category JOIN post_category ON post_category.tag_id = category.tag_id WHERE post_category.post_id = p.post_id AND category.name = "".DB::escape($key)."") > 0,{$scoreCategoryKeyword},0)"; } // Just incase it"s empty, add 0 if (empty($titleSQL)){ $titleSQL = 0; } if (empty($sumSQL)){ $sumSQL = 0; } if (empty($docSQL)){ $docSQL = 0; } if (empty($urlSQL)){ $urlSQL = 0; } if (empty($tagSQL)){ $tagSQL = 0; } $sql = "SELECT p.p_id,p.p_title,p.p_date_published,p.p_url, p.p_summary,p.p_content,p.thumbnail, ((-- Title score ".implode(" + ", $titleSQL).")+ (-- Summary ".implode(" + ", $sumSQL).")+ (-- document ".implode(" + ", $docSQL).")+ (-- tag/category ".implode(" + ", $categorySQL).")+ (-- url ".implode(" + ", $urlSQL).")) as relevance FROM post p WHERE p.status = "published" HAVING relevance > 0 ORDER BY relevance DESC,p.page_views DESC LIMIT 25"; $results = DB::query($sql); if (!$results){ return false; } return $results; }
Now your search.php file can look like this:
$term = isset($_GET["query"])?$_GET["query"]: ""; $search_results = search($term); if (!$search_results) { echo "No results"; exit; } // Print page with results here.
We created a simple search algorithm that can handle a fair amount of content. I arbitrarily chose the score for each match, feel free to tweak it to something that works best for you. And there is always room for improvement.
It is a good idea to track the search term coming from your users, this way you can see if most users search for the same thing. If there is a pattern, then you can save them a trip and just cache the results using Memcached .
If you want to see this search algorithm in action, go ahead and try looking for an article on the search box on top of the page. I have added extra features like returning the part where the match was found in the text. Feel free to add features to yours.
Did you like this article? You can subscribe to read more awesome ones. .
On a related note, here are some interesting articles.
It is time to deal with mysql_* functions once and for all. These methods are deprecated and slow. The time to upgrade has long passed yet we still see it everywhere. Since I cannot force every author to update their tutorial and blogs, I decided to write a post to hopefully rank better and provide the essential information to help new comers.
Making your own website shouldn"t be too difficult. Hosting companies like Godaddy or Hostgator make it super easy for anyone to get started; they allow you to create a whole website without ever writing code. For most people, it is plenty to run a WordPress blog. If this is what you are looking for you should head to Godaddy.com right now. We are done here. But on the other hand, if you want to have control and not be limited by the short comings of a shared hosting without busting your wallet, you have come to the right place.
Vim is my favorite text editor on the terminal. After playing for a little while with nano and emacs , I finally settled with vim for its simplicity (bare with me please). Although it can be customized and used like an entire IDE, I use it mostly for editing files on my servers and making small but crucial changes. Let"s not get into Editor war and get started.
Comments(45)
Zaryel Aug 12 2015:
Ian Mustafa Sep 26 2015:
Rob Sep 29 2015:
adeem Feb 11 2016:
Ivan Venediktov Apr 9 2016.
Добрый вечер!Подскажите стоит ли теперь вообще заполнять мета-тег keywords исходя из нижеописанной политики поисковиков?Представители Яндекса заявили о keywords следующее: «… может учитываться при определении соответствия страницы...
Есть решение
Можно ли вносить Meta keywords простым копированием из Excel? Там удобнее обрабатывать ключевики. Можно ли проставить после каждого ключевика запятую, пробел, потом выделить всю колонку, скопировать и вставить в окошко дл Meta keywords? Или нужно...
Вы можете копировать Meta keywords из Excel, ограничений здесь никаких нет. Что касается двойных пробелов - скрипты лишние пробелы не убирают. Если вручную удалять их долго, то можно скопировать текст в текстовый редактор (Блокнот, например) и выполнить поиск с заменой по всему тексту - дойной пробел заменить на одинарный.
Добрый день, в ХАБЕ нет возможности менять не title,
Keywords, Description в категориях, это так задумано или ошибка
1) Уточните, можно ли на главной странице, на страницах категорий на страницах под категорий и на страницах товаров (на каждой указанной странице) в индивидуальном порядке (в ручном режиме через админку) прописываться title, description, keywords,...
Добрый день! Сайт в облаке.Необходимо, чтобы мета тег keywords отсутствовал на некоторых страницах. Обращаю внимание: необходимо не задать пустые значения вида , а удалить тег полностью....
Т.к. в поисковых системах данный мета-тег не используетсяпланируют ли разработчики webasyst убрать его?
Есть решение
в теме нельзя делать проверок вроде {if !empty($wa->meta("keywords"))} ... ? Пытаюсь написать {if !empty($wa->meta("keywords"))}
PHP-функции empty() в качестве аргумента нужно передавать переменную, а не выражение, оттого и ошибка. Вот такое изменение вашего кода устранит ошибку:{$keywords = $wa->meta("keywords")}{if !empty($keywords)} {/if}Хотя ещё логичнее было бы изменить ваш код вот так (вам ведь нужно проверить, есть ли непустая строка в МЕТА-свойстве keywords):{if...
Есть решение
Заметил что на сайте (приложение сайт) , не путать с магазином, не выводится у страниц разметка. Зашел на сайт webasyst у них все ок, как ее реализовать, куда какой код вставить? И почему этого нет по умолчанию?
Сайт - Страницы - Выбрать страницу и наверху Настройки страницы - раздел Соцсети.
Есть решение
Здравствуйте!Подскажите, пожалуйста, как вручную отредактировать шаблон по которому создается титл по умолчанию в карточке товара? Допустим, хочу шаблон: "Купить *название раздела* *название товара*, цена *прайс* с доставкой по России". И... Попробуйте в начало файла product.html(Магазин-Витрина-Шаблоны) добавить строки:{* New product page title *}
{$_category_name = $wa->shop->category($product.category_id|default:null)}
{$_category_name = $_category_name.name|default:""}
{$_product_name = $product.name}
{$_price = shop_currency($product.price)}
{$_new_title = sprintf("Купить %1$s %2$s, цена %3$s с доставкой по России", $_category_name, $_product_name, $_price)}
{$wa->title($_new_title)}
{* /New...
Есть решение
Хочу поставить кнопку что бы показывало сколько онлайн человек на сайте, но когда добавляю код в index.html то на сайте пишет страница не найдена, подскажите пожалуйста куда этот код впихнуть? +1
shopProductsCollection: проблемы с при`join`неными таблицами
На рассмотрении
Используя shopProductsCollection::addJoin() добавил таблицы, но не получается вывести значения из них используя shopProductsCollection::getProducts($fields). Из $fields удаляются все поля не относящиеся к товару (p.*). Из-за этого также невозможно... Создания сайта, для розницы и опта, салона элитной сантехники, плитки и мебели
Общее ТЗ ниже.Сайт необходимо сделать с нуля, по аналогии с этим under-style.ru Добрый день!Хочу для главной страница прописать ключевики и описание в теги title, keywords, descriptionЗайдя в шаблон index увидел там эти теги, но они ссылаются на какие то блоки
+2
суть вот в чем.в настройках категорий есть куча всевозможных значений-элементов-настроек.вот с чем столкнулся.сейчас у меня есть чудо плагины"SEO-оптимизация" и"SEO описание категорий".никто ведь не будет спорить с тем фактом, что... Подскажите в каком файле в блоге новостей меняются title, description, keywordsВот например: Собственно используется тема дефолт-2 Shop-script 5Обратил внимание, что прописанные в бекэнде данные не отображаются (title, keywords & description)вопрос где искать проблему)) и как решать... Добрый день, работаю с Вебасистом, задача - сменить метатэги. Проблема в том, что они выводятся неизвестно (для меня) чем и как. Разумеется, я их пытаюсь вбить по пути "Магазин-Товар-Собственно Товар-Вкладка "Описания", где есть... Добрый день, у меня вот такая проблема:Товары стоят не так как надо! 1 ряд хорошо а вот последующие почему то не так! Мне нужно исправить данную ошибку чтобы было вот так:Помогите мне исправить данную ошибку. Мне нужно сделать это в ближайшее время...
Одна из самых популярных и необходимых функций на любом сайте – это поиск, реализованный с помощью специальной формы. Этот функционал позволяет посетителям быстро находить на сайте интересующий их контент.
Сегодня мы хотим рассказать, как сделать поиск по сайту с помощью специальной формы, которая будет опрашивать таблицы базы данных и выводить информацию о текущих руководителях на сайте. Вы научитесь создавать таблицы базы данных, которая будет содержать в себе информацию о текущем персонале.
Разрабатывать формы поиска при помощи PHP , а также познакомитесь с SQL (Structured Query Language ) – специальным языком для сбора, записи и модификации информации, содержащейся в базах данных. Перед тем как начать, рекомендуем вам скачать файлы проекта .
Что вам понадобится
- Инструмент для работы с базами данных MySQL .
- Локальный или удаленный сервер с поддержкой PHP .
- Текстовый редактор.
Создаем базу данных
Если вы не совсем уверены, что сможете разобраться с базой данных на вашем хостинге, то свяжитесь с хостером для получения соответствующих инструкций или помощи. После того как база данных будет создана, вам нужно будет подключить ее, создать таблицу и записать в нее необходимые данные.
Самым популярным инструментом для управления MySQL является PHP My Admin , Этого инструмента будет достаточно для нашего сегодняшнего руководства.
Создание таблицы
Наша таблица должна быть создана в следующем формате:
Column Name | Data Type | Length | Null or Not Null | Primary key? | Auto Increment |
ID | INT | 1 | Not Null | Yes | Yes |
FirstName | Varchar | 50 | Not Null | No | No |
LastName | Varchar | 50 | Not Null | No | No |
Varchar | 50 | Not Null | No | No | |
PhoneNumber | Varchar | 15 | Not Null | No | No |
Таблица базы данных состоит из столбцов и строк, как в Excel . Первый столбец позволяет идентифицировать данные по имени. Далее идет колонка Data types (тип данных ), которая указывает нам на тип данных, содержащихся в колонке. В поле Length (Длина ) указывается максимальный объем памяти (хранилища ) для столбца таблицы. Мы используем переменные, которые дают больше гибкости. Другими словами, если длина ФИО меньше 50 символов, то будет занята лишь часть отведенного места.
И среди данных персонала не может быть пустых значений (null, empty ). Первая строка выделена желтым цветом, потому что столбец ID – наш основной ключ. Основной ключ в базе данных гарантирует, что каждая запись будет уникальной. К этой колонке также применен автоинкремент, а это значит, что каждой записи в нашей базе данных будет присваиваться уникальный номер автоматически.
Вносим представителей персонала в таблицу
Как только разберетесь с таблицей, начните заполнять ее данными. 6 записей вполне достаточно, чтобы закрепить в уме процедуру. Ниже предлагаю вам собственный пример:
Column ID | FirstName | LastName | PhoneNumber | |
2 | Ryan | Butler | [email protected] | 417-854-8547 |
3 | Brent | Callahan | [email protected] | 417-854-6587 |
Разработка формы
Чтобы создать форму поиска по сайту через Google , откройте любой подходящий текстовый редактор. Я рекомендую воспользоваться бесплатным PSPad . Вы можете использовать любой текстовый редактор, где предусмотрена подсветка синтаксиса. Это в значительной степени облегчит процесс написания и отладки PHP-кода . Создавая страницу для формы поиска, не забудьте сохранить ее в формате .php , иначе PHP-код не будет обрабатываться должным образом. Как только сохраните документ, скопируйте в него следующую разметку:
Детальный поиск контактов
Вы можете искать по имени или фамилии
Если вы знакомы с языком HTML , то тут вам все должно быть понятно как минимум до открывающего тега form . Внутри этого тега находится важнейший элемент всего кода – атрибут action . В качестве действия нашей формы мы указали название нашего файла, а затем применили к нему строку запроса “go ”.
Проверка на соответствие критерию
Когда пользователь вводит имя или фамилию, а затем нажимает кнопку подтверждения, форма передает данные самой себе и добавляет в конце строку запроса “go ”. На данном этапе мы проверяем наличие строки запроса go . Если результат положительный, выводим результаты поиска.
До вывода запрашиваемых результатов нам нужно перепроверить: (1) была ли подтверждена форма, (2) содержит ли строка запроса значение go, (3) был ли поисковой запрос введен в нижнем или верхнем регистре? Если ни одна из проверок не дает положительного результата (true ), то от нас не требуется выполнять какие-либо действия.
Для начала добавим небольшой блок кода PHP поиск по сайту после закрывающего тега :