Search Behavior

Hi All:

For those of you who are missing search capability in Radiant, I created
a
simple “Search” behavior for pages. It lets you create a search page
that
receives keywords as parameters and lists pages that match those
keywords.
It also has live search capabilities. It is wrapped into a Rails plugin,
so
all you need to do is to dump the plugin it into the ‘vendor/plugins’
directory of your Radiant installation and restart the instance.

The easiest way to get it is via Subversion:

$ cd vendor/plugins
$ svn co http://nobits.org/svn/rails/plugins/search_behavior

or unpack the tarball (http://nobits.org/files/search_behavior.tar.gz)
into
the same directory.

This time there are no unit tests yet.

You can find a demo at: http://nobits.org/

Please let me know any comments or suggestions. Below follows the usage
description, copied directly from the source.

Cheers,
Oliver

Usage:

The "Search" behavior adds search capabilities to a Radiant site.  A
page with this behavior responds to search queries that are passed 

to
it through the request parameter “q”. Each space separated word in
the
query string is treated as a keyword. The search result includes
all published pages whose titles or parts match (substring matching)
all of the keywords specified in the query.

Using the "Search" behavior it is possible to provide standard
form-based search as well as "live" search using the prototype 

library.
When the corresponding search page is called with its canonical URL,
it
is rendered similar to a normal page, i.e. with its entire layout.
When, however, “/live” is appended to the URL the response is
rendered
from the “live” page part defined for the page without any layout.

To process search results the behavior provides the following tags:

  <r:search:results> [content] </r:search:results>

    Renders [content] only if the result set is not empty.

  <r:search:results:each> [content] </r:search:results:each>

    Renders [content] for every page in the result set. Within this 

tag
default tags to access page attributes (i.e. <r:url/>,
<r:link/>,
r:title/>, <r:author/>, etc.) are valid and defined for the
currently processed page from the result set.

  <r:search:empty> [content] </r:search:empty>

    Render [content] only if the result set is empty
    (e.g. "No match").

  <r:search:query/>

    The original search query string.

Additionally the behavior provides a tag to generate a simple search
form, though its use is not required. Search forms can also be 

defined
in normal HTML by the user:

  <r:search:form [label="Search term:"] [live="element-to-update]
                 [prototype="true"]/>

    Generates a simple search form that submits its content to the
    search page for which it was created. Additional attributes are
    optional:

      label:
        Defines an alternative label for the search field, the 

default
is “Search:”.

      live:
        Enables the form observer for live search and specifies the 

ID
of the HTML element that is updated with the results.

      prototype:
        If its value is "true" the Prototype JavaScript library 

shipped
with Radiant is explicitly loaded, but only if the live
search
is enabled.

Example:

  Assume a page with slug "search" as a child of the root page. The
  "body" page part of this page may look like:

  <r:search:form />
  <hr />
  <r:search:empty>
    No match.
  </r:search:empty>
  <r:search:results>
    Results:
    <ul>
      <r:search:results:each>
        <li>
          <r:link/> by <r:author/>
        </li>
      </r:search:results:each>
    </ul>
  </r:search:results>

  To extend this example to allow a "live" search from the header
  snippet that is included into every page, (Typo style live 

search),
two additional page parts are created for the search page:

  The "live" page part is only rendered when search results are
  returned from a "live" search request:

  <ul>
    <r:search:results:each>
      <li><r:link/></li>
    </r:search:results:each>
  </ul>

  The "form" page part is included into the header snippet, 

described
below:

  <r:search:form live="search-results" prototype="true"
                 label="Live search:" />
  <div id="search-results"></div>

  Since, the header snippet itself is not always rendered in the
  context of a "Search" behavior the <r:search:form> tag is not 

always
available. This prevents us from adding the form directly into the
header snippet and requires the creation of the “form” page part
for
the search page. This page part can be easily included into the
header snippet with the following lines:

  <r:find url="/search">
    <r:content part="form"/>
  </r:find>

  This finds the search page and renders its "form" page part in the
  context of the "Search" behavior.

  TODO:

    * escape query string for inclusion in response page
    * behavior based filter for search results

Very, very cool.


John

Hi Maurizio,

On 10-Jun-2006 18:40 +0200, Maurizio B. was heard to say:

After I search for a term which is not found, the expected text
appears. But when I access again (reload) the page, the text within
the “<r:search:empty>” tag remains on the page while I’d expect it
should not. Maybe there is something I’m doing wrongly …

this is currently not taking care of. That is, the content of
<r:search:empty> will also be evaluated when no query was submitted. I
will
fix this in the next few days and let you know.

The request:

Could you also provide a tag which will show a snippet of the found
page (for example the first 5 rows or so)?

Yes, limiting the number of search results, certainly not a problem to
implement.

Cheers,
Oliver

Hello Oliver. This is a great plugin! I’ve an issue and I’ve a request:

The issue:

If I’ve a page like this one:

 <r:search:form label="Immettete di seguito il testo da ricercare " 

/>



<r:search:empty>
Nessuna pagina trovata contenente il termine:
“<r:search:query/>”.
</r:search:empty>
<r:search:results>
Results:

    <r:search:results:each>

  • <r:link/> (by <r:author/>)

  • </r:search:results:each>

</r:search:results>

After I search for a term which is not found, the expected text
appears. But when I access again (reload) the page, the text within
the “<r:search:empty>” tag remains on the page while I’d expect it
should not. Maybe there is something I’m doing wrongly …

The request:

Could you also provide a tag which will show a snippet of the found
page (for example the first 5 rows or so)?

Best regards,

–M

Hi Wolfgang,

I am happy the search behavior works well for you.

On 12-Jul-2006 22:21 +0200, Wolfgang Wopperer was heard to say:

  • An onComplete attribute for the search:form tag that is evaluated in
    case live search is enabled. I replaced the radius tag with an HTML form
    and the prototype observer and AJAX call. To this, I added the
    onComplete attribute to call a JS function that controls the visibility
    of my result list. This works perfectly, but it would surely be more
    elegant if one could just use an attribute of your tag to reference a JS
    function.

Do you mean, instead of having the onComplete attribute being hard-coded
you would like to have the option to specify a custom function instead?

This certainly is possible and I will add this on a newer version of the
behavior.

  • Excluding certain pages from search, e.g. virtual pages (404 pages) or
    xml files (in my case, a Google sitemap). The most elegant solution
    would be to make the exclusion list configurable, of course.

I planned some sort of filtering as well. Also such that the search page
itself does not show up in the search.

I may be able to add those improvements in one of the next few days and
let
you know.

Cheers,
Oliver

Olivier: add me to the list of happy search_behavior users :slight_smile:

In regards to a previous issue:

On 12-Jul-06, at 4:58 PM | Jul 12, Oliver B. wrote:

On 12-Jul-2006 22:21 +0200, Wolfgang Wopperer was heard to say:

  • Excluding certain pages from search, e.g. virtual pages (404
    pages) or
    xml files (in my case, a Google sitemap). The most elegant solution
    would be to make the exclusion list configurable, of course.

I planned some sort of filtering as well. Also such that the search
page
itself does not show up in the search.

What I just did with my implementation is change line 192 in
search_behavior.rb

from this…
@query_result = pages.delete_if { |p| !p.published? }

to this:
@query_result = pages.delete_if { |p| !p.published? ||
p.parts.find_by_name(‘unsearchable’) }

Then I add a page part called ‘unsearchable’ to any pages I want
excluded. Kind of ugly, but it works well.

James

Thanks for a really great Radiant behavior! I got search up and running
in a matter of minutes.

In the course of integrating it into our website, I came up with two
possible extensions:

  • An onComplete attribute for the search:form tag that is evaluated in
    case live search is enabled. I replaced the radius tag with an HTML form
    and the prototype observer and AJAX call. To this, I added the
    onComplete attribute to call a JS function that controls the visibility
    of my result list. This works perfectly, but it would surely be more
    elegant if one could just use an attribute of your tag to reference a JS
    function.

  • Excluding certain pages from search, e.g. virtual pages (404 pages) or
    xml files (in my case, a Google sitemap). The most elegant solution
    would be to make the exclusion list configurable, of course.

I think these additions could be of use for many others, too.

Keep up the good work!

Wolfgang