/** * @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')); }