/**
  * @param string $keywords
  * @param array $filters [optional]
  * @param array $facetSpec [optional]
  * @param int $start [optional]
  * @param int $limit [optional]
  * @param string $sort [optional]
  * @return ArrayData
  */
 function searchFromVars($keywords, array $filters = array(), array $facetSpec = array(), $start = -1, $limit = -1, $sort = '')
 {
     $searchable = ShopSearch::get_searchable_classes();
     $matches = new ArrayList();
     foreach ($searchable as $className) {
         $list = DataObject::get($className);
         // get searchable fields
         $keywordFields = $this->scaffoldSearchFields($className);
         // convert that list into something we can pass to Datalist::filter
         $keywordFilter = array();
         if (!empty($keywords)) {
             foreach ($keywordFields as $searchField) {
                 $name = strpos($searchField, ':') !== FALSE ? $searchField : "{$searchField}:PartialMatch";
                 $keywordFilter[$name] = $keywords;
             }
         }
         if (count($keywordFilter) > 0) {
             $list = $list->filterAny($keywordFilter);
         }
         // add in any other filters
         $list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
         // add any matches to the big list
         $matches->merge($list);
     }
     return new ArrayData(array('Matches' => $matches, 'Facets' => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool) Config::inst()->get('ShopSearch', 'auto_facet_attributes'))));
 }
 /**
  * @param string $keywords
  * @param array $filters [optional]
  * @param array $facetSpec [optional]
  * @param int $start [optional]
  * @param int $limit [optional]
  * @param string $sort [optional]
  * @return ArrayData
  */
 function searchFromVars($keywords, array $filters = array(), array $facetSpec = array(), $start = -1, $limit = -1, $sort = '')
 {
     $searchable = ShopSearch::get_searchable_classes();
     $matches = new ArrayList();
     foreach ($searchable as $className) {
         $list = DataObject::get($className);
         // get searchable fields
         $keywordFields = $this->getSearchFields($className);
         // build the filter
         $filter = array();
         // Use parametrized query if SilverStripe >= 3.2
         if (SHOP_SEARCH_IS_SS32) {
             foreach ($keywordFields as $indexFields) {
                 $filter[] = array("MATCH ({$indexFields}) AGAINST (?)" => $keywords);
             }
             $list = $list->whereAny($filter);
         } else {
             foreach ($keywordFields as $indexFields) {
                 $filter[] = sprintf("MATCH ({$indexFields}) AGAINST ('%s')", Convert::raw2sql($keywords));
             }
             // join all the filters with an "OR" statement
             $list = $list->where(implode(' OR ', $filter));
         }
         // add in any other filters
         $list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
         // add any matches to the big list
         $matches->merge($list);
     }
     return new ArrayData(array('Matches' => $matches, 'Facets' => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool) Config::inst()->get('ShopSearch', 'auto_facet_attributes'))));
 }
 /**
  * Sets up the index
  */
 public function init()
 {
     $searchables = ShopSearch::get_searchable_classes();
     // Add each class to the index
     foreach ($searchables as $class) {
         $this->addClass($class);
     }
     // add the fields they've specifically asked for
     $fields = $this->getFulltextSpec();
     foreach ($fields as $def) {
         $this->addFulltextField($def['field'], $def['type'], $def['params']);
     }
     // add the filters they've asked for
     $filters = $this->getFilterSpec();
     foreach ($filters as $filterName => $def) {
         // NOTE: I'm pulling the guts out of this function so we can access Solr's full name
         // for the field (SiteTree_Title for Title) and build the fieldMap in one step instead
         // of two.
         //$this->addFilterField($def['field'], $def['type'], $def['params']);
         $singleFilter = $this->fieldData($def['field'], $def['type'], $def['params']);
         $this->filterFields = array_merge($this->filterFields, $singleFilter);
         foreach ($singleFilter as $solrName => $solrDef) {
             if ($def['field'] == $solrDef['field']) {
                 $this->fieldMap[$filterName] = $solrName;
             }
         }
     }
     //		Debug::dump($this->filterFields);
     // Add spellcheck fields
     //		$spellFields = $cfg->get('ShopSearch', 'spellcheck_dictionary_source');
     //		if (empty($spellFields) || !is_array($spellFields)) {
     //			$spellFields = array();
     //			$ftFields = $this->getFulltextFields();
     //			foreach	($ftFields as $name => $fieldDef) {
     //				$spellFields[] = $name;
     //			}
     //		}
     //
     //		foreach ($spellFields as $f) {
     //			$this->addCopyField($f, '_spellcheckContent');
     //		}
     // Technically, filter and sort fields are the same in Solr/Lucene
     //		$this->addSortField('ViewCount');
     //		$this->addSortField('LastEdited', 'SSDatetime');
     // Aggregate fields for spelling checks
     //		$this->addCopyField('Title', 'spellcheckData');
     //		$this->addCopyField('Content', 'spellcheckData');
     //		$this->addFullTextField('Category', 'Int', array(
     //			'multi_valued'  => true,
     //			'stored'        => true,
     //			'lookup_chain'  => array(
     //				'call'      => 'method',
     //				'method'    => 'getAllProductCategoryIDs',
     //			)
     //		));
     // I can't get this to work. Need a way to create the Category field that get used
     //		$this->addFilterField('Category', 'Int');
     //		$this->addFilterField('Parent.ID');
     //		$this->addFilterField('ProductCategories.ID');
     //		$this->addCopyField('SiteTree_Parent_ID', 'Category');
     //		$this->addCopyField('Product_ProductCategories_ID', 'Category');
     // These will be added in a pull request to shop module. If they're not present they'll be ignored
     //		$this->addFilterField('AllCategoryIDs', 'Int', array('multiValued' => 'true'));
     //		$this->addFilterField('AllRecursiveCategoryIDs', 'Int', array('multiValued' => 'true'));
     // This will cause only live pages to be indexed. There are two ways to do
     // this. See fulltextsearch/docs/en/index.md for more information.
     // Not sure if this is really the way to go or not, but for now this is it.
     $this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
 }