Documentation

This commit is contained in:
Ingo Schommer 2012-08-29 21:52:56 +02:00
parent 2c7d6d0ab8
commit 29b17a860a
2 changed files with 142 additions and 16 deletions

View File

@ -3,7 +3,7 @@
/**
* Represents a search query
*
* API very much still in flux. Generally, calling with multiple arguments = OR, calling multiple times = AND.
* API very much still in flux.
*/
class SearchQuery extends ViewableData {
@ -32,9 +32,9 @@ class SearchQuery extends ViewableData {
}
/**
* @param [type] $text [description]
* @param [type] $fields [description]
* @param array $boost Map of field names to float values. The higher the value,
* @param String $text Search terms. Exact format (grouping, boolean expressions, etc.) depends on the search implementation.
* @param array $fields Limits the search to specific fields (using composite field names)
* @param array $boost Map of composite field names to float values. The higher the value,
* the more important the field gets for relevancy.
*/
function search($text, $fields = null, $boost = array()) {
@ -42,10 +42,13 @@ class SearchQuery extends ViewableData {
}
/**
* @param [type] $text [description]
* @param [type] $fields [description]
* @param array $boost Map of field names to float values. The higher the value,
* the more important the field gets for relevancy.
* Similar to {@link search()}, but uses stemming and other similarity algorithms
* to find the searched terms. For example, a term "fishing" would also likely find results
* containing "fish" or "fisher". Depends on search implementation.
*
* @param String $text See {@link search()}
* @param array $fields See {@link search()}
* @param array $boost See {@link search()}
*/
function fuzzysearch($text, $fields = null, $boost = array()) {
$this->search[] = array('text' => $text, 'fields' => $fields ? (array)$fields : null, 'boost' => $boost, 'fuzzy' => true);
@ -55,12 +58,25 @@ class SearchQuery extends ViewableData {
$this->classes[] = array('class' => $class, 'includeSubclasses' => $includeSubclasses);
}
/**
* Similar to {@link search()}, but typically used to further narrow down
* based on other facets which don't influence the field relevancy.
*
* @param String $field Composite name of the field
* @param Mixed $values Scalar value, array of values, or an instance of SearchQuery_Range
*/
function filter($field, $values) {
$requires = isset($this->require[$field]) ? $this->require[$field] : array();
$values = is_array($values) ? $values : array($values);
$this->require[$field] = array_merge($requires, $values);
}
/**
* Excludes results which match these criteria, inverse of {@link filter()}.
*
* @param String $field
* @param mixed $values
*/
function exclude($field, $values) {
$excludes = isset($this->exclude[$field]) ? $this->exclude[$field] : array();
$values = is_array($values) ? $values : array($values);

View File

@ -18,11 +18,9 @@ This is a module aimed at adding support for standalone fulltext search engines
It contains several layers:
* A fulltext API, ignoring the actual provision of fulltext searching
* A connector API, providing common code to allow connecting a fulltext searching engine to the fulltext API, and
* Some connectors for common fulltext searching engines.
* A fulltext API, ignoring the actual provision of fulltext searching
* A connector API, providing common code to allow connecting a fulltext searching engine to the fulltext API, and
* Some connectors for common fulltext searching engines.
## Reasoning
@ -38,7 +36,7 @@ This module instead provides the ability to define those indexes and queries in
between the SilverStripe object model and the connector-specific fulltext engine index model. This module then interrogates model metadata
to build the specific index definition.
It also hooks into Sapphire in order to update the indexes when the models change and connectors then convert those index and query definitions
It also hooks into SilverStripe framework in order to update the indexes when the models change and connectors then convert those index and query definitions
into fulltext engine specific code.
The intent of this module is not to make changing fulltext search engines seamless. Where possible this module provides
@ -57,13 +55,17 @@ Basic usage is a four step process:
class MyIndex extends SolrIndex {
function init() {
$this->addClass('Page');
$this->addFulltextField('DocumentContents');
$this->addFulltextField('Title');
$this->addFulltextField('Content');
}
}
You can also skip listing all searchable fields, and have the index
figure it out automatically via `addAllFulltextFields()`.
2). Add something to the index (Note: You can also just update an existing document in the CMS. but adding _existing_ objects to the index is connector specific)
$page = new Page(array('Contents' => 'Help me. My house is on fire. This is less than optimal.'));
$page = new Page(array('Content' => 'Help me. My house is on fire. This is less than optimal.'));
$page->write();
Note: There's usually a connector-specific "reindex" task for this.
@ -79,6 +81,89 @@ Note: There's usually a connector-specific "reindex" task for this.
Note that for most connectors, changes won't be searchable until _after_ the request that triggered the change.
## Searching Specific Fields
By default, the index searches through all indexed fields.
This can be limited by arguments to the `search()` call.
$query = new SearchQuery();
$query->search('My house is on fire', array('Page_Title'));
// No results, since we're searching in title rather than page content
$results = singleton('MyIndex')->search($query);
## Searching Value Ranges
Most values can be expressed as ranges, most commonly dates or numbers.
To search for a range of values rather than an exact match,
use the `SearchQuery_Range` class. The range can include bounds on both sides,
or stay open ended by simply leaving the argument blank.
$query = new SearchQuery();
$query->search('My house is on fire');
// Only include documents edited in 2011 or earlier
$query->filter('Page_LastEdited', new SearchQuery_Range(null, '2011-12-31T23:59:59Z'));
$results = singleton('MyIndex')->search($query);
Note: At the moment, the date format is specific to the search implementation.
## Searching Empty or Existing Values
Since there's a type conversion between the SilverStripe database, object properties
and the search index persistence, its often not clear which condition is searched for.
Should it equal an empty string, or only match if the field wasn't indexed at all?
The `SearchQuery` API has the concept of a "missing" and "present" field value for this:
$query = new SearchQuery();
$query->search('My house is on fire');
// Needs a value, although it can be false
$query->filter('Page_ShowInMenus', SearchQuery::$present);
$results = singleton('MyIndex')->search($query);
## Indexing Multiple Classes
An index is a denormalized view of your data, so can hold data from more than one model.
As you can only search one index at a time, all searchable classes need to be included.
// File: mysite/code/MyIndex.php:
<?php
class MyIndex extends SolrIndex {
function init() {
$this->addClass('Page');
$this->addClass('Member');
$this->addFulltextField('Content'); // only applies to Page class
$this->addFulltextField('FirstName'); // only applies to Member class
}
}
## Indexing Relationships
TODO
## Weighting/Boosting Fields
Results aren't all created equal. Matches in some fields are more important
than others, for example terms in a page title rather than its content
might be considered more relevant to the user.
To account for this, a "weighting" (or "boosting") factor can be applied to each
searched field. The default is 1.0, anything below that will decrease the relevance,
anthing above increases it.
Example:
$query = new SearchQuery();
$query->search(
'My house is on fire',
null,
array(
'Page_Title' => 1.5,
'Page_Content' => 1.0
)
);
$results = singleton('MyIndex')->search($query);
## Filtering
## Connectors
### Solr
@ -88,3 +173,28 @@ See Solr.md
### Sphinx
Not written yet
## FAQ
### How do I exclude draft pages from the index?
By default, the `SearchUpdater` class indexes all available "variant states",
so in the case of the `Versioned` extension, both "draft" and "live".
For most cases, you'll want to exclude draft content from your search results.
You can either prevent the draft content from being indexed in the first place,
by adding the following to your `SearchIndex->init()` method:
$this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
Alternatively, you can index draft content, but simply exclude it from searches.
This can be handy to preview search results on unpublished content, in case a CMS author is logged in.
Before constructing your `SearchQuery`, conditionally switch to the "live" stage:
if(!Permission::check('CMS_ACCESS_CMSMain')) Versioned::reading_stage('Live');
$query = new SearchQuery();
// ...
### How do I write nested/complex filters?
TODO