/**
  * Initiate the FTS indexer.  Once initiated, all work will be done by the FTS consumers which will be invoked
  * by the job queue system.
  *
  * @param array $modules             Modules to index
  * @param bool  $deleteExistingData Remove existing index
  * @param bool  $runNow             Run indexing jobs immediately instead of placing them in job queue
  * @return SugarSearchEngineFullIndexer
  */
 public function initiateFTSIndexer(array $modules = array(), $deleteExistingData = true, $runNow = false)
 {
     $startTime = microtime(true);
     $GLOBALS['log']->info("Populating Full System Index Queue at {$startTime}");
     if (!$this->SSEngine instanceof SugarSearchEngineAbstractBase) {
         $GLOBALS['log']->info("No FTS engine enabled, not doing anything");
         return false;
     }
     $allModules = !empty($modules) ? $modules : array_keys(SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsForAllModules());
     // Create the index on the server side
     $this->SSEngine->createIndex($deleteExistingData, $allModules);
     // Clear the existing queue
     $this->clearFTSIndexQueue($allModules);
     // clear flag
     $admin = Administration::getSettings();
     if (!empty($admin->settings['info_fts_index_done'])) {
         $admin->saveSetting('info', 'fts_index_done', 0);
     }
     $totalCount = 0;
     foreach ($allModules as $module) {
         $totalCount += $this->populateIndexQueueForModule($module, $runNow);
     }
     $totalTime = number_format(round(microtime(true) - $startTime, 2), 2);
     $this->results['totalTime'] = $totalTime;
     $GLOBALS['log']->info("Total time to populate full system index queue: {$totalTime} (s)");
     $avgRecs = $totalCount != 0 && $totalTime != 0 ? number_format(round($totalCount / $totalTime, 2), 2) : 0;
     $GLOBALS['log']->info("Total number of modules queued: {$totalCount} , modules per sec. {$avgRecs}");
     return $this;
 }
 /**
  *
  * This function creates a full mapping for all modules.
  * index must exist before calling this function.
  *
  */
 public function setFullMapping()
 {
     $allModules = SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsForAllModules();
     // if the index already exists, is there a way to create mapping for multiple modules at once?
     // for now, create one mapping for a module at a time
     foreach ($allModules as $name => $module) {
         $this->setFieldMapping($name, $module);
     }
 }
 /**
  * This function returns an array of fields that can be passed to search engine.
  * @param Array $options
  * @param boolean $prefixed Add module name prefix (i.e. Contacts.first_name)
  * @return Array array of fields
  */
 protected function getSearchFields($options, $prefixed = true)
 {
     $fields = array();
     // determine list of modules/fields
     $allFieldDefs = array();
     if (!empty($options['moduleFilter'])) {
         foreach ($options['moduleFilter'] as $module) {
             $allFieldDefs[$module] = SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsPerModule($module);
         }
     } else {
         $allFieldDefs = SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsForAllModules();
     }
     // build list of fields with optional boost values (i.e. Accouns.name^3)
     foreach ($allFieldDefs as $module => $fieldDefs) {
         foreach ($fieldDefs as $fieldName => $fieldDef) {
             // skip non-supported field types
             $ftsType = $this->mapper->getFtsTypeFromDef($fieldDef);
             if (!$ftsType || in_array($ftsType['type'], $this->ignoreSearchTypes)) {
                 $this->logger->debug("Elastic: Ignoring unsupported type in query for {$module}/{$fieldName}");
                 continue;
             }
             // base field name
             if ($prefixed) {
                 $fieldName = $module . '.' . $fieldName;
             }
             // To enable a field for user search we require a boost value. There may be other fields
             // we index into Elastic but which should not be user searchable. We use the boost value
             // being set or not to distinguish between both scenarios. For example for extended facets
             // and related fields we can store additional fields or non analyzed data. While we need
             // those fields being indexed, we do not want the user to be able to hit those when
             // performing a search.
             if (empty($fieldDef['full_text_search']['boost'])) {
                 $this->logger->debug("Elastic: skipping {$module}/{$fieldName} for search field (no boost set)");
                 continue;
             } else {
                 if (!empty($options['addSearchBoosts'])) {
                     $fieldName .= '^' . $fieldDef['full_text_search']['boost'];
                 }
                 $fields[] = $fieldName;
             }
         }
     }
     return $fields;
 }