/** * 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; }
/** * * Load facet definitions from enabled FTS module fieldDefs * @param string $module * @return array */ protected function loadFacetDefs($module) { if (isset($this->facetDefs[$module])) { return $this->facetDefs[$module]; } $this->facetDefs[$module] = array(); $fieldDefs = SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsPerModule($module); foreach ($fieldDefs as $fieldName => $fieldDef) { // skip non-facet defs if (empty($fieldDef['full_text_search']['facet'])) { continue; } $facet = $fieldDef['full_text_search']['facet']; // the type needs to be defined to be valid if (is_array($facet) && !empty($facet['type'])) { // set empty options array if nothing specified if (empty($facet['options']) || !is_array($facet['options'])) { $facet['options'] = array(); } // set defaults for ui_type and label $facet['ui_type'] = empty($facet['ui_type']) ? 'checkbox' : $facet['ui_type']; $facet['label'] = empty($facet['label']) ? 'LBL_FACET_NOT_SET' : $facet['label']; $this->facetDefs[$module][$fieldName] = $facet; } } return $this->facetDefs[$module]; }
/** * Main function that handles the indexing of a bean and is called by the job queue system. * * @param $data */ public function run($module) { $serverOK = $this->updateFTSServerStatus(); if ($serverOK != true) { $GLOBALS['log']->fatal('FTS Server is down, postponing the job for full index.'); $this->schedulerJob->postponeJob(null, $this->postpone_job_time); return true; } $this->db->commit(); $GLOBALS['log']->info("Going to index all records in module {$module} "); $startTime = microtime(true); $fieldDefinitions = SugarSearchEngineMetadataHelper::retrieveFtsEnabledFieldsPerModule($module); if (!empty($fieldDefinitions)) { $count = $this->indexRecords($module, $fieldDefinitions); } else { $GLOBALS['log']->fatal(sprintf('Fields of %s are not enabled for Full Text Search', $module)); $this->schedulerJob->resolveJob(SchedulersJob::JOB_STATUS_DONE); return true; } if ($count == -1) { $GLOBALS['log']->fatal('FTS failed to index records, postponing job for next cron'); $this->schedulerJob->postponeJob(null, $this->postpone_job_time); return true; } // stats logging $totalTime = number_format(round(microtime(true) - $startTime, 2), 2); $avgRecs = $count != 0 && $totalTime != 0 ? number_format(round($count / $totalTime, 2), 2) : 0; $GLOBALS['log']->info(sprintf("FTS Consumer %s processed %s record(s) in %s secs, records per sec: %s", $this->schedulerJob->name, $count, $totalTime, $avgRecs)); // Mark the job that as pending so we can be invoked later again - this is considered to be a persistent job $this->schedulerJob->postponeJob(null, $this->postpone_job_time); return true; }