RSS

Neil Crookes

Learnings and Teachings on Web Application Development & CakePHP

Nov

21

CakePHP Searchable Plugin

A flexible and full featured CakePHP plugin for quickly adding site wide, multi-model search functionality to your application.

Share and Enjoy:

  • Digg
  • del.icio.us
  • StumbleUpon
  • Technorati
  • Slashdot

At work we recently developed an application for a client that required site-wide search functionality, and provided a single results set from multiple models/sources. Normally I’d use the CakePHP Yahoo BOSS site search I wrote and blogged about previously, but this particular app requires users to login to access any content, so Yahoo wouldn’t be able to index any of the content.

I had a hunt around to see if there was anything already out there that would fit the requirements and I found this http://code.google.com/p/searchable-behaviour-for-cakephp/ which is a behavior that stores the data from multiple models in a single search_index table and performs mysql full text search on that, but it didn’t quite have all the features I needed such as scope for search results, i.e. you set the status of a record to in-active and the corresponding record in the search_index table goes in-active. I did, however, like this approach.

So, inspired by the above solution, I’ve written a plugin that you can add to your app and integrate site wide search functionality in a matter of minutes.

The code is available on my github account. Note, it relies on MySQL Full Text Search, but you could replace this with your own search algorithm or alternative RDBMS equivalent.

The plugin includes:

  1. A Searchable Behavior to attach to models in your app that automatically maintains a record in the search_index table for each record in the model you attach it to.
  2. A shell script to build/re-build the search_index table for all/some models that have the Searchable Behavior attached
  3. Model, View and Controller for the search_index table that handles performing the search, and displaying the results.

To get it up and running:

  1. Get the code from github
  2. Run the SQL in searchable/config/sql/search_index.sql
  3. Attach the Searchable Behavior to the models in your app that you want to search, e.g.

    var $actsAs = array('Searchable.Searchable');

  4. Run the build_search_index shell, e.g.

    $> cake build_search_index

  5. Add the searchable/config/routes.php file to you app/config/routes.php

    // app/config/routes.php
    include(APP.'plugins'.DS.'searchable'.DS.'config'.DS.'routes.php');

  6. Add the search form element to a page in your site, or in the default.ctp layout file e.g.

    echo $this->element('form', array('plugin' => 'searchable'));

Now type something in the search box and go.

Some additional features/notes:

  • You’ll notice on the search results page you can restrict your search to a single model.
  • Search results are paginated.
  • Search terms are added to the URL so you can deep link to search results
  • The search supports MySQL Full Text Search in boolean mode, so you can do things like searching for phrases using quotes and excluding words using the minus sign
  • The search_index table has a scope field which is a boolean (tinyint 1) and is set to 1 by default, but if you specify some normal cakephp conditions in the scope setting when you attach the Searchable Behavior, this will be set depending on whether these conditions are met for that particular record. E.g.var $actsAs = array(‘Searchable.Searchable’ => array(‘scope’ => array(‘Post.active’ => 1)));
  • Data from your model is stored in the ‘data’ field of the search_index table and is json_encode’d. This is to circumvent one of the issues of the Searchable behavior I found earlier that someone noted in the issues list – if you call saveField, only that field’s data got saved in the search_index table. With this behavior, when editing a record, if not all fields are present in the data you are saving, the existing content of the data field is merged with the new data you are saving, so you don’t lose any data that you had previously.
  • By default, all string type fields are included in the json_encode’d data field, but you can override this if necessary using the ‘fields’ setting when you attach the behavior. E.g.var $actsAs = array(‘Searchable.Searchable’ => array(‘fields’ => array(‘title’, ‘abstract’, ‘body’, ‘published‘)));
  • Sometimes it’s useful to be able to search for associated data as well, e.g. the name of the Category that a Post belongsTo, to achieve this you can do the following:var $actsAs = array(‘Searchable.Searchable’ => array(‘fields’ => array(‘title’, ‘abstract’, ‘body’, category_id‘ => ‘Category.name’)));I.e. the foreign key field in the searchable model => the model.field you want to fetch the value from.
  • The search_index table also includes fields for ‘name’ and ‘summary’, you can configure which fields in your model are used to populate these fields in the search_index table in the settings array too. What goes in here are what’s displayed in the search results.
  • If your data uses a published date field (or equivalent) to determine whether content should be displayed or not, as an alternative or in addition to scope, the search_index table also has a published field, and again you can configure which field in your model should map through to it. The search results are scope to only display records whose published field is null (which it will be by default if you have no published data), or the published date is in the past – but you can configure this as required by your app. For example on another app I’ve used this on I changed these conditions to published in the past, but not more than 6 months ago, or if logged in (i.e. an administrator), display future content as well so they can preview stuff.
  • By default, the search result will link through to the controller for the model of that search result, it’s view action, and pass the id of the record as a parameter. You can configure this to some extent at the moment, e.g. if your model is actually in another plugin, you can add this to the settings, but that’s about it at the moment, so no slugs or anything like that. If you need to configure the url formats, suggest you just amend the views/search_indexes/index.ctp view file to your requirements.

Enjoy ;-)

Share and Enjoy:

  • Digg
  • del.icio.us
  • StumbleUpon
  • Technorati
  • Slashdot
(13 votes, average: 4.31 out of 5)
Loading ... Loading ...

107 Responses so far

Wow, great plugin. I will be implementing this into one of my current projects. Thanks.

Nice plugin – I’m already using it (slightly modified).

Very Cool Plugin! Many thanks.

Hi, I followed your tutorial but I get these errors as soon as I load a page:

Warning (2): include(/Sites/cake/app/plugins/searchable/config/routes.php) [function.include]: failed to open stream: No such file or directory [APP/config/routes.php, line 44]

Warning (2): include() [function.include]: Failed opening ‘Sites/cake/app/plugins/searchable/config/routes.php’ for inclusion (include_path=’/Sites/cake:/Users/ahandley/Sites/cake/app/:.:/Applications/MAMP/bin/php5/lib/php’) [APP/config/routes.php, line 44]

thanks

Either the path to the routes.php file inside app/plugins/searchable/config/routes.php is incorrect or you have not put the file there.

Great plugin.

Is it possible to search for associated data if it is a HABTM?

This looks like a killer plugin, however I’m getting errors when trying to run the shell…

“Warning: SQL Error: 1064: You h
ave an error in your SQL syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near ‘deleteSearchIndex’ at line 1 in D:\Server\cake\cake\libs\model\datasources\dbo_source.php on line 524
Query: deleteSearchIndex Erro
r: Could not delete search index”

Any ideas?

@Jasmin, not at the moment, but that would be a good enhancement, feel free to fork the code and have a go, and let me know how you get on.

@unidev, the shell deletes all the records in the search index in the db for the given model before rebuilding them. It does this by calling deleteSearchIndex() on the model. That method is on the SearchableBehavior, but cake will automatically pass it off to the Behavior if the method doesn’t exist in the model but does exist in one of the behaviors that are attached to the model. However, if cake isn’t aware that that method is available directly on the model, or in any behavior that is attached to it, it assumes it is a stored procedure in the db and tries to execute on the db directly – which is what is happening here. What you need to do is figure out why cake does not know that the method is available on the behavior that is attached to your model – sorry, I’m not sure.

Hi Neil,

Thanks for making this – seems similar to the site search by Kalt but with the shell rebuild features which should come in handy.

Q: Have you had any thoughts on the best way to lock the search down with permissions? i.e. restrict results depending on who is searching?

Cheers in advance and have a good chrimbo.

@unclezoot, just add logic in the controller action to set conditions based on the logged in user.

First of all merry xmas and thanks for this great plugin!

I have the same problem:
Error: Could not delete search index

I’m using ACL but even when inactive the error is displayed. Must be something else?

I’ve got this in the model:
‘Searchable.Searchable’ => array(‘fields’ => array(‘title’, ‘description’, ‘company_id’ => ‘Company.name’)),

Would be great if you know of anything else I could check.

Merry Xmas!

Btw, if I remove deleteSearchIndex from the shell script it works.

@Nader, if you add deleteSearchIndex back in and run it again, do you get the error? I wonder if I added that line after I ran it for the first time?

@Neil, when I put the function in again, it works now. Maybe it has got something todo with an initial empty table or weird database issue?

Using # Server version: 5.0.41
# Protocol version: 10
# Server: Localhost via UNIX socket

with MAMP on MacOS X Leopard

Hi Neil again,

any hint on where and how I would implement AJAX Search with jQuery and your Plugin?

@Nader, sorry, not my area of expertise, but check out the cook book for ajax form submissions and using the RequestHandler component for handling ajax requests.

Hey, thanks for the great plugin.

I’m trying to fill in the summary field from a field in my model, but I can’t quite figure out which options to pass to do that.

Nevermind, I got it.

Of course I totally missed it in the comments until just after I posted here. Go Murphy.

protected function _setUrl seems incomplete. It doesn’t handle non standard URL components and mappings to dynamic fields (ie. slug).

This is what I patched:
http://bin.cakephp.org/view/1213709512

[...] Neil Crookes’ Searchable plugin also helped. [...]

Hi Neil,

Thanks for this great plugin!

I’m having a problem but I don’t know why could it be. The thing is when I perform any search the query returns every single record of the search_index table, even those which don’t include in the field “data” anything about the word I searched.
Could you help me?

Thanks!

@Adam, good work. You’re right, that was unfinished. I’ll take a look at the patch and add it. Thanks.

@Jorge, hmm interesting. Can’t think of any reason why that would be, check the SQL debug queries and copy the one containing MATCH…AGAINST, then run it in your SQL editor and see if you get the same results. If you do, sounds like it could be your MySQL not working correctly, maybe?

Hi Neil,

Finally I reinstalled the plugin and now is working perfectly!

Thank you so much for your work!

Hi again Niel,

I cannot figure out how to do to customize the name and summary field when you said “The search_index table also includes fields for ‘name’ and ’summary’, you can configure which fields in your model are used to populate these fields in the search_index table in the settings array too”.
How and where do I need to write something like ‘name’ => ‘myFieldName’?
I tried putting that in the settings array in diferents ways but never worked.

Thank you very much!

Hi Jorge,

Should be as simple as:

var $actsAs = array(‘Searchable.Searchable’ => array(’name’ => ‘my_field_name’));

Does this not work for you?

Hi Neil,

Nop, doesn’t work for me.
I tried something like this:

var $actsAs = array(‘Searchable.Searchable’ => array(‘fields’ => array(‘email’, ‘email1′, ‘direccion’), ‘name’ => ‘nombre’));

Then I re run the build_search_index and nothing changes in the name field.

Thanks!

Hi Neil,

I’m having problems to run the shell to index. Copied the codes over to my app and once I ran the shell, prompted with
Fatal error: Class ‘SearchableAppModel’ not found in …… search_index.php on line 2

Any idea what is really happening? Thanks Neil

Sorry my bad. I’ve totally misintepreted…plugins… Well now it works

@ Jorge / Neil

Take a look at lines 100 – 103 in searchable.php – there’s a condition preventing the ‘name’ being set.

Thanks for the great plugin.

Hi Paul,

Thank you so much for your help!

@Jorge sorry for not getting back to you.

@Paul, thanks for pointing out the bug, I’ll check it out and patch the repo on github when I get a sec. I’ll post back here when it’s done

Hi Neil,

This looks really interesting. Is it (or can it be) compatible with 1.3?

@Andy, should be compatible with 1.3, I have not tested it yet though, it shouldn’t take too long to try it, if you get chance, pop back here and let us know how you get on.

I did some basic test in 1.3 and seems pretty good.

When running the build_search_index shell, I got the following errors:

Warning: array_keys(): The first argument should be an array in /app/plugins/searchable/vendors/shells/build_search_index.php on line 113
Warning: current(): Passed variable is not an array or object in /app/plugins/searchable/vendors/shells/build_search_index.php on line 118

However, the rest of the process continued on as normal:

Possible Models based on your current database:
1. Product
Enter one or more numbers from the list above separated by a space, or type in the names of one or more other models, ‘a’ for all or ‘q’ to exit
[q] > 1

The error was in _setDbConfig(), the $config = get_class_vars(‘DATABASE_CONFIG’) is empty. Didn’t seem to affect the indexing, though, db table was populated with data as expected.

Also, I needed to modify the SearchIndexesController $helpers array by appending the plugin name to the searchable helper. Eg:
var $helpers = array(‘Searchable.Searchable’);

Other than that, seems to work fine. I’ll do some more in-depth testing and let you know of any other issues that come up.

By the way, this is pretty sweet. I was using a modified version of the Zend Lucene search, which is nice, but soooo slow! This is nice and fast to index and query, so that’s nice.

Neil, thank you very much for this great plugin. Has been really very useful to me. The tutorial is perfect as well.

I am new to cakePHP and it is great to see that new features are always being added.

Thanks!

Neil,

I am trying to get this working but when I go to build the search index I am getting the following error

Parse error: syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or ‘}’ in /home/user/example.com/app/plugins/searchable/vendors/shells/build_search_index.php on line 25

Looking at the build_search_index.php file line 25 is blank line 24 states protected $_useDbConfig;

I am using cakephp 1.2.6

Any help would be greatly appreciated.

Are you running PHP4? Type “php -v” at your shell prompt or “/path/to/php -v” if php is not a recognised command (“C:\path\to\php -v” if on windows).

Note, sometimes cli (comman line interpreter) versions of php can be different to one you installed as an apache module for example.

What I’m getting at is the cli might be baulking at the “protected” keyword, if so, change all instances of public, protected or private to just var when they precede a class property and just remove them altogether when they precede a method.

Interesting. While my php info tells me I’m running PHP 5.2.12 the php -v states PHP 4.4.9 (cli) (built: Sep 17 2008 12:02:18)

So if I can update my cli then I should be able to run the build_search_index script otherwise I’ll need to make the above modifications?

Yeah – if you are using wamp or xamp or mamp or something, they sometimes install the old cli, or fail to update them or something. Google around for getting those things to use the same version of php for the cli and it should sort your problem. You should definitely be running 5.2.12 on there as well.

Neil,

I now have cli 5.2.12 running and can access the Build Search Index Shell. It offers me a choice between either my default database or my test. However Possible Models based on your current database: always returns no results. I have added var $actsAs => array(‘Searchable.Searchable’); to the two models I want searchable. I have also tried to add the names of the models and ‘a’ for all but I still get nothing.

@foroctfralion I’m not sure what’s wrong off the top of my head. Suggest you do some debugging in the

plugins/searchable/vendors/build_search_index.php

file. Specifically the setAvailableModels() method. Add some echos, pr() calls and die()s on a few of the variables to see what’s going on.

The code is well commented so it should be fairly easy to understand what it’s trying to do, and then why it’s not working in your case.

Please come back and leave a comment when you find out.

in the example you had:

var $actsAs => array(‘Searchable.Searchable’);

but it should be

var $actsAs = array(‘Searchable.Searchable’);

this might be causing some problems for people

@Paul, thanks, I’ve updated the post

I am having a problem with the plug-in causing some of my links from my default.ctp layout being redirected to the searchable controller. Any idea why this would happen? They point to somewhere that does not exist and therefore I get an error when I click on them.

@Adam, CakePHPs Router class assumes you want to link to the plugin you are in if you are in one. If you don’t want to link to a controller/action in the plugin, you have to tell it which plugin the controller/action you want to link to is in, or null, if you want to link to a controller/action in your main app directory. e.g. echo $html->link(‘Home’, array(‘plugin’ => null, ‘controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’));

I have implemented the searchable plug-in to several models in my app, but I can’t for the life of me find out how to do run a search across all those models, all fields.

The Google documentation says to use Model->find(), but how do I pass in the search parameter to find()?

I want the equivalent of:

$this->AllModels->search($mySearchString);

@Josh, are you using this plugin or the one on http://code.google.com/p/searchable-behaviour-for-cakephp/ ?

Suggest you use my plugin ;-) and read the full post above – should tell you everything you need to know about this plugin. If not, come back and post another comment.

Hey Neil Thanks for the gr8 plugin .
I am tying to modify to support habtm can you help me ?!!!!

tell me can i replace this
if (empty($nonStandardUrlComponents)) {
$url[] = $this->_foreignKey;
$this->SearchIndex->data['SearchIndex']['url'] = json_encode($url);
return;
}

in function setUrl
with
if (empty($nonStandardUrlComponents)) {
$url[] = $foreignKey = $model->getInsertID();
$this->SearchIndex->data['SearchIndex']['url'] = json_encode($url);
return;
}

Does this plugin supports has many??

Hi Avinash, thanks for the comment. To be honest, I haven’t tried it with hasMany or hasAndBelongsToMany. In theory, it could, see SearchableBehavior line 195:

      if (method_exists($model, 'getSearchableData')) {
        $data = $model->getSearchableData($this->_modelData);
      } else {
      .....

You can implement your own getSearchableData() method in your model that the behavior is attached to and get any data you want from within that method. For example, listing the names of the tags that that record is tagged with. Let me know how you get on.

Hi Niel,
very very great work ;-)

And question. Its possible to populate for example name field with datas from asociate table?

Example:
table Attitudes(id:int, attitude:text, poll_id:int)
table Polls(id:int, name:varchar(25) …)

var $actsAs = array(‘Searchable.Searchable’ => array(‘name’ => ‘Poll.name’, ‘summary’ => ‘attitude’, ‘fields’ => array(‘attitude’, ‘poll_id’ => ‘Poll.name’)));

Neil, let me first thank you for the awesome plugin! I’ve noticed something which I believe is intended behavior but wanted to clarify. If I search for the word “browse” and my data contains the word “browser” but not “browse” I get no results; if I search for “browse*” it matches “browser”. How can I get “browse” to match “browser” without the asterisk?

Thanks, Neil, for this cool plugin! Looks great!

Unfortunatly, It doesn’t support the Hebrew language. In order for it to work I need to go over the records in serch_index and change the data field from:

{“Celeb.first_name”:”\u05e8\u05de\u05d9″,”Celeb.last_name”:”\u05d1\u05e8\u05d5\u05da”}

to:

{“Celeb.first_name”:”???”,”Celeb.last_name”:”????”}

Meaning, from strange coding to hebrew (utf-8?).

Btw – the name field in the table contains correct hebrew. it is just the data filed that is incorect.

ok, problem solved!

I realized that the strange encoding was json.
for some the search term was not decoded and the data in the table was in json.

So i changed line 50 in search_indexes_controller.php and added the json_ensode function:

$term = Sanitize::escape(json_encode($term));

I wander if the data in the table should have been not in json?…

I’ve managed to get the search working properly with Hebrew. The problem is that the json_encode turns each Hebrew letter into \uXXXX with 4 hex characters. This is using a lot of space in the DB and more impotently it makes the search plugin act very strange (every search would be as if it had ‘*’ in it, whitespace was ignored, + and – had no affect, and so on…)

What i did to solve the problem was to build my own functions who replace json_encode and json_decode. these functions work the same way but leave the Hebrew character as is.

I had to do some extra minor changes and now everything works great!

Thanks, Neil, for a terrific plug in!

by the way –
The search does not work for me when i look for strings in english. i assume it has something to do with me using utf-8.

@Yonatan, thanks for your comments. Surprised you have problems with json and utf8 – that’s what I use too and don’t have any problems.

I would not be surprised if the issue was elsewhere. However, I haven’t tested with RTL languages (and unfortunately wouldn’t know where to start).

Do you have any issues running MATCH … AGAINST directly on the database?

@unidev,
@Nader,

There seems to be an error in:
app/plugins/searchable/models/behaviors/searchable.php and deleteSearchIndex() method.

The problem is with different versions of CakePHP and model method: deleteAll() which returns false or empty array() on empty table.
Before triggering SearchIndex->deleteAll() we need to check for presence of records.
I did something like this:

if(!isset($this->SearchIndex))
$this->SearchIndex = ClassRegistry::init(‘Searchable.SearchIndex’);

$records = $this->SearchIndex->find(‘all’, array(‘conditions’ => $conditions));
if($records === false || (is_array($records) && empty($records)))
return true;
else
return $this->SearchIndex->deleteAll($conditions);

Little correction since I can not edit my own comment and I’m kinda tired ;):
The problem is with different versions of CakePHP and model method: deleteAll() which returns false on empty table (*not empty array()*).

@Neil Crookes,
Killer plugin! Thank you for developing it :)

Hi,

I am trying your search plugin and I am getting the error below when running the build-search_index shell:

Parse error: parse error, unexpected T_OBJECT_OPERATOR in /var/www/html/crm/app/vendors/shells/build_search_index.php on line 158

I am using PHP4 and cake 1.2.5

thanks
mb

I got the search plugin to work, though i had to make quite a few changes for it to work with older versions of PHP and cakephp

Thanks for the great plugin.
mb

Hi!

First of all, thank you for the plugin!
Unfortunately I have a problem. I tried cakephp 1.2 and php5, cakephp blog tutorial.
I’ve got a little menu top of the screen (posts, add post, and search link). Everything works fine, except I click on the search link. After the search I get weird links, if I want to click the posts or add post link.
Before the search the links: localhost/blog/posts/ and localhost/blog/posts/add
After the search: localhost/blog/searchable/posts and localhost/blog/seachable/posts/add
And they don’t work. What should I do, to resolve this problem?
Thanks!

@peter CakePHP’s HtmlHelper::link() method assumes the controller/action you want to link to is in the plugin that the current request results in. If you want to link to a controller/action outside of the current plugin, from a view within a plugin, you need to specify array(‘plugin’ => ‘my_plugin’, ‘controller’ => ‘my_controller’, ‘action’ => ‘my_action’) in the 2nd parameter of HtmlHelper::link(). If the controller/action you want to link to is not in a plugin, use ‘plugin’ => null. Hope this helps.

@mb, glad you got it working, if you have time, other PHP4 users might benefit from you experiences so please come back and add a comment about what you did

@Neil
Thanks, it’s work!

One more thing: there is the post table, in my db. There are title, body, created, modified fields. If I want to search by title there isn’t result. By body -> everything fine.
Did I miss something when I re_indexed the db?

@Neil Crookes,
Everything is working. I can search in both english and in hebrew.
I has to add some addslashes() and stripslashes() because in hebrew some letters contain tags (‘) but now everything in fine!

Thanks again for the plugin!

I have a problem with the routing. When I search for something the URL looks like this:
http://localhost/search/All/sunshine

But now all the other links have the name of the plugin in their URL.
For example this:
$html->link(__(‘News’, true), array(‘controller’=>’news’, ‘action’=>’index’));
creates this link url: http://localhost/searchable/news

I already have this in app/config/routes.php:
Router::connect(‘/search/:type/:term/*’, array(
‘plugin’ => ‘searchable’,
‘controller’ => ‘search_indexes’,
‘action’ => ‘index’,
));

Any idea how I can get rid of “/search/” for my “normal” app links???

@Mathias, see comments above, this has already been answered twice. Look for “‘plugin’ => null”.

Neil,
Thank you so much for this awesome plugin! It works like a charm, and I am very grateful for it.

I was wondering if it is possible to make results from specific models appear before other models. For example, my search indexes three models: Listings, Special Events and Venues. The Listings always appear first, but I would prefer to have Venues and Special Events appear first in the search results.

Do you have any advice for me?

Thanks again,
Mark

@Mark hmmm, good question. You’ve probably figured out there is no functionality built-in to allow this. You could probably achieve it quite easily though by adding some elements to the query options. In the search_indexes_controller.php file, lines 52 & 53 add in something like:

$this->paginate['SearchIndex']['fields'] = "*, MATCH(data) AGAINST('$term' IN BOOLEAN MODE) AS score, CASE `model` WHEN 'Venues' THEN 0 WHEN 'Special Events' THEN 0 WHEN 'Listings' THEN 1 END AS mycustomorder";
$this->paginate['SearchIndex']['order'] = "mycustomorder, score DESC";

This is untested, but you get the idea – you add a MySQL case statement to the fields array setting a mycustomorder field to an integer depending on the value of the record’s model field, then in the order by clause, we first order by mycustomorder, then by score.

Let me know how you get on.

Thanks for the quick reply, Neil. I could not get the custom ordering to work. I’ve played with it quite a bit, but the msyql case stuff is over my head, so this will need to be on the back burner for now.

I have another question if you don’t mind. The values that go into the ‘name’ field of the search_index table sometimes have html tags that get eliminated during indexing … is there any way to preserve these tags?

Thanks.

Update:
Got the custom ordering to work… I kept on reading about the mysql CASE (wasn’t actually too complicated after all), and then when reviewing the sql I noticed my models were named wrong (plural rather than singular).

The code you provided above works perfectly (as long as the models are spelled correctly of course).

Thanks!

@foroctfralion and mr crookes.
it seems that build_search_index asks for which database to use but regardless of your answer, it uses the “default” one.
great plugin btw. thx!

Can anyone offer advice on how to set up a crontab to update the search index? Thanks!

crontab -e

0 0 * * * /path/to/php /path/to/cake/console/cake build_search_index -app /path/to/app

Then edit the shell to atomatically run for all models.

Re: crontabs
Hey Neil, sorry to be a pita, but I cannot figure out how to edit the shell to run automatically. Can you expand on that if/when you get a chance?

It would be ideal to be able to update the index using a cronjob on my production server, and also by simply clicking a link in my admin panel. Is the latter even possible?

Thanks for any insight you can offer.

wow. Just what i needed. Will install it tonight!

The plugin is great, there’s one feature I’m missing however.

I’ve Images (id, url, caption, album_id) that belongs to an Album (id, title). Now I’d like to search those images witch respect for their caption, but in search results I’d like name to be taken from the album title field, otherwise it appears as Image.id which is preety ugly (the Image doesn’t have name/title field on their own) :-)

So for an Image sth. like that:
var $actsAs = array(‘Searchable.Searchable’ => array(
‘fields’ => array(‘caption’),
‘name’ => array(‘album_id’ => ‘Album.title’),
‘summary’ => ‘caption’
));

@ArteQ that’s a good idea, if you implement it, let me know and I’ll add it to the github repo, or better yet, fork it and send me a pull request

i am getting this error when i try to build index http://variable3.com/files/screenshots/2010-11-08_1255.png

Hi, I’ve tried to use this plugins, but
I have some problems, when I use shell to type this command
cake build_search_index
It notice me an error, I don’t know how to solve it :(

PHP Warning: array_keys() expects parameter 1 to be array, boolean given in C:\
wamp\www\myapp\plugins\searchable\vendors\shells\build_search_index.php on
line 113

Warning: array_keys() expects parameter 1 to be array, boolean given in C:\wamp\
www\myapp\plugins\searchable\vendors\shells\build_search_index.php on line
113
PHP Warning: current() expects parameter 1 to be array, null given in C:\wamp\w
ww\myapp\plugins\searchable\vendors\shells\build_search_index.php on line
118

Warning: current() expects parameter 1 to be array, null given in C:\wamp\www\myapp\plugins\searchable\vendors\shells\build_search_index.php on line 118
Possible Models based on your current database:
Enter one or more numbers from the list above separated by a space, or type in t
he names of one or more other models, ‘a’ for all or ‘q’ to exit

when I type something to search, It is have nothing.

How can I solve it?
Thanks

I think I fixed my problem, so It have a new problem, the search plugin is good to find English, but it can’t find any thing by vietnamese :(

I am using cakephp 1.3
I had some installation issues
searchable.searchable never worked, so I changed the class behavior to searchable. ( I’m a new, I’m sure it’s cos I’ve got something wrong )
Nice build script, tks for writing one! but it did not work, I had to paste class searchablelAppModel in the same script as search_index.php

Warning: array_keys(): The first argument should be an array in /var/www/mysite/app/vendors/shells/build_search_index.php on line 114
I think the ‘DATABASE_CONFIG’ is not defined as an array

The default form points to searchable.. which does not exist :-(
As you’ve said earlier.. set the plugin => null
And the results were always returning ALL records

This maybe a 1.3 issue, but in search_indexes_controller.php $this->params has further default dimensions.. so there is no $this->params['term'] or $this->params['type']
So, I changed these to $this->params['named'] ['term'] and $this->params['named'] ['type']

apart from that, cool beans, tks very much!

Hi Neil – just wanted to say thank you for this plugin. Its really rather good and the comments covered all the modifications i needed, especially the custom ordering solution – very clever.
I’m just getting into CakePHP and this helped alot.

[...] ?????????????????????CakePHP Searchable Plugin?????????????? [...]

For anyone getting “SQL Error: 1062: Duplicate entry ‘Model-######’ for key ‘model’” errors – Make sure the foreign_key is the same type/length INT as your model’s id column.

I have a table using BIGINT(20) and was getting this error because it truncated the value of the foreign key and caused duplicates.

Removing the array_keys error in cakePHP 1.3:

I’m a newbie, but add
include_once CONFIGS . ‘database.php’; on the first line of:
plugins/searchable/vendors/shells/build_search_index.php

I’m wondering if it’s possible to improve on the search function so that even if you are searching for part of a word, the result will still show. For example if you search for ‘Car’, any entry in the search index table containing ‘cars’ will be returned.

I am also having trouble setting the ‘name’ and ‘summary’ setting for the search.
For example I want to search through user’s usernames and emails but I only want their usernames to be returned in the search results. The code I have is
‘Searchable.Searchable’ => array((‘fields’ => array(‘username’,'email’)), ‘name’ => ‘username’, ‘summary’ =>’username’),
But the email field still shows in the results and no name is set

Hi!

Love your Plugin, great work!!

Anyhow, I do have a question/suggestion and maybe you can help me with it:

I don’t really see the need for the search_index table.
In my case, I have all the data already stored in the database and the search_index table is kind of blowing up, all the data is already in the database.

So regarding this, now my question is:
How can I make the Plugin work without the search_index table?
Where do I have to make the changes to search my existing database tables?

Thanks for sharing your work again!!

Hi Neil,

Apologies if you’ve answered this question previously… Does your plugin require cakephp version 1.3 as this one does: http://cakedc.com/eng/downloads/view/cakephp_search_plugin

Your thoughts would be hugely helpful on this!

Cheers,
N

Trying to get this to work and run the build search index.

Build Search Index Shell
—————————————————————
Use Database Config: (default/test)
[default] >
PHP Fatal error: Class ‘SearchableAppModel’ not found

What am I doing wrong here? I copied folder structure exaclty…

Ok I figured that out… but whenever I try to search anything (after successful creating and filling my search_index) I get the following errors:

Warning (512): Model “User” is not associated with model “Person” [CORE/cake/libs/model/behaviors/containable.php, line 363]
Warning (2): Cannot modify header information – headers already sent by (output started at /cake/libs/debugger.php:673) [CORE/cake/libs/controller/controller.php, line 742]

Hi,
I am lost.I am trying to install your plugin in my cakePHP folder manually but i can’t do it. I do not know how to run the script and could you help me giving me a source (any website)that i can look at?

I’m using admin routing and wanted to deploy the search plugin in the admin backend. It seems to lose the [SearchIndex] array data coming from the form input. Any clue on why that would happen?

Hi, hoping you can help. I noticed someone else here had a similar error, but I’m unable to solve mine:

Warning: array_keys() expects parameter 1 to be array, boolean given in /var/www/html/plugins/searchable/vendors/shells/build_search_index.php on line 113

Warning: current() expects parameter 1 to be array, null given in /var/www/html/plugins/searchable/vendors/shells/build_search_index.php on line 118
Possible Models based on your current database:
Enter one or more numbers from the list above separated by a space, or type in the names of one or more other models, ‘a’ for all or ‘q’ to exit
[q] >

Thanks!

Hey Nail, Terry

I’m having the same problem that terry .

I’m having problems to run the shell to index. Copied the codes over to my app and once I ran the shell, prompted with
Fatal error: Class ‘SearchableAppModel’ not found in …… search_index.php on line 2

Any idea what is really happening? Thanks Neil

Please Help.

Hi Nail, seems like my problem was solved.

Now, I put in the search form “th” as a term to look for, but it returns all records in the table search_index even found no records that match this criterion, How can i handle that? I just want to show records wich match with “th”.

I’m implementing this in a personal project and this meaning a big problem to that.

I hope your help. Thanks

Hi Louis G,

Can you tell me how you fixed the shell problem please?

Thanks,

Hi all,

I get error message:

Error: Class BuildSearchIndexShell could not be loaded.

from cake console. What seems to be the problem in my environment?

// Jari

@Jari: You’re probably running the console from the cake directory, while your shell is in the app directory.

Copy your /cake/sonsole to /app/console and try again.

I, too, ran into the problem with running the shell.

When I launch the shell I get this error:

Warning: array_keys() expects parameter 1 to be array, boolean given in /var/www/html/iw4dev2/app/vendors/shells/build_search_index.php on line 113

Warning: current() expects parameter 1 to be array, null given in /var/www/html/iw4dev2/app/vendors/shells/build_search_index.php on line 118

Fatal error: Class ‘SearchableAppModel’ not found in /var/www/html/iw4dev2/app/models/search_index.php on line 2

I noticed two people above me with the same problem, but I could not extrapolate the answer on how to fix it. Please help?

Nvm.. I got it.. Now I’m getting another error when I’m in the shell -

Notice: Undefined index: SearchIndex in /var/www/html/iw4dev2/app/plugins/searchable/models/behaviors/searchable.php on line 385

Warning: array_merge(): Argument #1 is not an array in /var/www/html/iw4dev2/app/plugins/searchable/models/behaviors/searchable.php on line 389

Warning: array_merge(): Argument #1 is not an array in /var/www/html/iw4dev2/app/plugins/searchable/models/behaviors/searchable.php on line 203
Station 157 XETV

Any thoughts?

A tip for anyone using this plugin and finds that certain search terms give no results, even though they should. MySQL Fulltext search has a list of stop words – if your search is for one of these exact words, it will be ignored. I was searching for the word “best”, and could see the word in the data column of search_index, but no results were returned. Turns out “best” is on the stop list. See here http://www.nivas.hr/blog/2009/09/15/how-to-disable-mysql-fulltext-stopwords/ for instructions on how to get MySQL to search on these words (and 3 letter words of you need them too).

Thanks to Neil for the great plugin.

Hi,

I followed you directions, but now it throws

Error: Class PhpShell could not be loaded.

// Jari

Sorry about the last message, it was because I ran the command:

cake build_search_index.php

// Jari

If anyone else is getting Duplicate entry for key ‘model’ errors, I noticed it was happening with multiple saves in some controller actions, which I solved for now by removing the if (!$created) condition in _initialiseSearchIndexData function of the searchable behaviour, which creates some extra db calls but nothing too terrible.

Is it working with cakephp 2.1 version?

Leave a comment