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