/** * Method for adding an object from the database into the index. * * @param DataObject * @param string * @param array */ protected function _addAs($object, $base, $options) { $includeSubs = $options['include_children']; $doc = new Apache_Solr_Document(); // Always present fields $doc->setField('_documentid', $this->getDocumentID($object, $base, $includeSubs)); $doc->setField('ID', $object->ID); $doc->setField('ClassName', $object->ClassName); foreach (SearchIntrospection::hierarchy(get_class($object), false) as $class) { $doc->addField('ClassHierarchy', $class); } // Add the user-specified fields foreach ($this->getFieldsIterator() as $name => $field) { if ($field['base'] == $base) { $this->_addField($doc, $object, $field); } } // CUSTOM Duplicate index combined fields ("Title" rather than // "SiteTree_Title"). // // This allows us to sort on these fields without deeper architectural // changes to the fulltextsearch module. Note: We can't use <copyField> // for this purpose because it only writes into multiValue=true // fields, and those can't be (reliably) sorted on. $this->_addField($doc, $object, $this->getCustomPropertyFieldData('Title', $object)); $this->_addField($doc, $object, $this->getCustomPropertyFieldData('LastEdited', $object, 'SSDatetime')); $this->getService()->addDocument($doc); return $doc; }
function appliesTo($class, $includeSubclasses) { return SearchIntrospection::has_extension($class, 'SiteTreeSubsitesPolyhome', $includeSubclasses); }
/** * Given a class, object id, set of stateful ids and a list of changed fields (in a special format), * return what statefulids need updating in this index * * Internal function used by SearchUpdater. * * @param $class * @param $id * @param $statefulids * @param $fields * @return array */ public function getDirtyIDs($class, $id, $statefulids, $fields) { $dirty = array(); // First, if this object is directly contained in the index, add it foreach ($this->classes as $searchclass => $options) { if ($searchclass == $class || $options['include_children'] && is_subclass_of($class, $searchclass)) { $base = ClassInfo::baseDataClass($searchclass); $dirty[$base] = array(); foreach ($statefulids as $statefulid) { $key = serialize($statefulid); $dirty[$base][$key] = $statefulid; } } } $current = SearchVariant::current_state(); // Then, for every derived field foreach ($this->getDerivedFields() as $derivation) { // If the this object is a subclass of any of the classes we want a field from if (!SearchIntrospection::is_subclass_of($class, $derivation['classes'])) { continue; } if (!array_intersect_key($fields, $derivation['fields'])) { continue; } foreach (SearchVariant::reindex_states($class, false) as $state) { SearchVariant::activate_state($state); $ids = array($id); foreach ($derivation['chain'] as $step) { if ($step['through'] == 'has_one') { $sql = new SQLQuery('"ID"', '"' . $step['class'] . '"', '"' . $step['foreignkey'] . '" IN (' . implode(',', $ids) . ')'); singleton($step['class'])->extend('augmentSQL', $sql); $ids = $sql->execute()->column(); } else { if ($step['through'] == 'has_many') { $sql = new SQLQuery('"' . $step['class'] . '"."ID"', '"' . $step['class'] . '"', '"' . $step['otherclass'] . '"."ID" IN (' . implode(',', $ids) . ')'); $sql->addInnerJoin($step['otherclass'], '"' . $step['class'] . '"."ID" = "' . $step['otherclass'] . '"."' . $step['foreignkey'] . '"'); singleton($step['class'])->extend('augmentSQL', $sql); $ids = $sql->execute()->column(); } } } SearchVariant::activate_state($current); if ($ids) { $base = $derivation['base']; if (!isset($dirty[$base])) { $dirty[$base] = array(); } foreach ($ids as $id) { $statefulid = array('id' => $id, 'state' => $state); $key = serialize($statefulid); $dirty[$base][$key] = $statefulid; } } } } return $dirty; }
protected function _addAs($object, $base, $options) { $includeSubs = $options['include_children']; $doc = new Apache_Solr_Document(); // Always present fields $doc->setField('_documentid', $this->getDocumentID($object, $base, $includeSubs)); $doc->setField('ID', $object->ID); $doc->setField('ClassName', $object->ClassName); foreach (SearchIntrospection::hierarchy(get_class($object), false) as $class) { $doc->addField('ClassHierarchy', $class); } // Add the user-specified fields foreach ($this->getFieldsIterator() as $name => $field) { if ($field['base'] == $base) { $this->_addField($doc, $object, $field); } } try { $this->getService()->addDocument($doc); } catch (Exception $e) { SS_Log::log($e, SS_Log::WARN); return false; } return $doc; }
function appliesTo($class, $includeSubclasses) { // Include all DataExtensions that contain a SubsiteID. // TODO: refactor subsites to inherit a common interface, so we can run introspection once only. return SearchIntrospection::has_extension($class, 'SiteTreeSubsites', $includeSubclasses) || SearchIntrospection::has_extension($class, 'GroupSubsites', $includeSubclasses) || SearchIntrospection::has_extension($class, 'FileSubsites', $includeSubclasses) || SearchIntrospection::has_extension($class, 'SiteConfigSubsites', $includeSubclasses); }
function appliesTo($class, $includeSubclasses) { return SearchIntrospection::has_extension($class, 'Versioned', $includeSubclasses); }
/** * Send updates to the current search processor for execution * * @param array $writes */ public static function process_writes($writes) { foreach ($writes as $write) { // For every index foreach (FullTextSearch::get_indexes() as $index => $instance) { // If that index as a field from this class if (SearchIntrospection::is_subclass_of($write['class'], $instance->dependancyList)) { // Get the dirty IDs $dirtyids = $instance->getDirtyIDs($write['class'], $write['id'], $write['statefulids'], $write['fields']); // Then add then then to the global list to deal with later foreach ($dirtyids as $dirtyclass => $ids) { if ($ids) { if (!self::$processor) { self::$processor = Injector::inst()->create('SearchUpdateProcessor'); } self::$processor->addDirtyIDs($dirtyclass, $ids, $index); } } } } } // If we do have some work to do register the shutdown function to actually do the work // Don't do it if we're testing - there's no database connection outside the test methods, so we'd // just get errors $runningTests = class_exists('SapphireTest', false) && SapphireTest::is_running_test(); if (self::$processor && !self::$registered && !$runningTests) { register_shutdown_function(array("SearchUpdater", "flush_dirty_indexes")); self::$registered = true; } }