/**
  * Determine if a module is FTS enabled.
  *
  * @param $module
  * @return bool
  */
 protected function isModuleFtsEnabled($module)
 {
     return SugarSearchEngineMetadataHelper::isModuleFtsEnabled($module);
 }
Example #2
0
 /**
  * The collector method for modules.  Gets metadata for all of the module specific data
  *
  * @param $moduleName The name of the module to collect metadata about.
  * @param MetaDataContextInterface|null $context Metadata context
  * @return array An array of hashes containing the metadata.  Empty arrays are
  * returned in the case of no metadata.
  */
 public function getModuleData($moduleName, MetaDataContextInterface $context = null)
 {
     require_once 'include/SugarSearchEngine/SugarSearchEngineMetadataHelper.php';
     $vardefs = $this->getVarDef($moduleName);
     if (!empty($vardefs['fields']) && is_array($vardefs['fields'])) {
         require_once 'include/MassUpdate.php';
         $vardefs['fields'] = MassUpdate::setMassUpdateFielddefs($vardefs['fields'], $moduleName);
     }
     $data['fields'] = isset($vardefs['fields']) ? $vardefs['fields'] : array();
     // Add the _hash for the fields array
     $data['fields']['_hash'] = md5(serialize($data['fields']));
     $data['nameFormat'] = isset($vardefs['name_format_map']) ? $vardefs['name_format_map'] : null;
     $data['views'] = $this->getModuleViews($moduleName, $context);
     $data['datas'] = $this->getModuleDatas($moduleName);
     $data['layouts'] = $this->getModuleLayouts($moduleName);
     $data['fieldTemplates'] = $this->getModuleFields($moduleName);
     $data['menu'] = $this->getModuleMenu($moduleName);
     $data['config'] = $this->getModuleConfig($moduleName);
     $data['filters'] = $this->getModuleFilters($moduleName);
     // Indicate whether Module Has duplicate checking enabled --- Rules must exist and Enabled flag must be set
     $data['dupCheckEnabled'] = isset($vardefs['duplicate_check']) && isset($vardefs['duplicate_check']['enabled']) && $vardefs['duplicate_check']['enabled'] === true;
     // Indicate whether a Module has activity stream enabled
     $data['activityStreamEnabled'] = ActivityQueueManager::isEnabledForModule($moduleName);
     $data['ftsEnabled'] = SugarSearchEngineMetadataHelper::isModuleFtsEnabled($moduleName);
     // TODO we need to have this kind of information on the module itself not hacked around on globals
     $data['isBwcEnabled'] = in_array($moduleName, $GLOBALS['bwcModules']);
     $seed = BeanFactory::newBean($moduleName);
     $data['globalSearchEnabled'] = $this->getGlobalSearchEnabled($seed, $vardefs, $this->platforms[0]);
     if (!empty($seed)) {
         $favoritesEnabled = $seed->isFavoritesEnabled() !== false ? true : false;
         $data['favoritesEnabled'] = $favoritesEnabled;
     }
     // Currently no way to disable following
     // But this flag is here in case we add that feature in the future
     $data['followingEnabled'] = true;
     $data["_hash"] = $this->hashChunk($data);
     return $data;
 }
 /**
  * This function is used to hand off the global search to the FTS Search Emgine
  * @param $api ServiceBase The API class of the request
  * @param $args array The arguments array passed in from the API
  * @param $searchEngine SugarSearchEngine The SugarSpot search engine created using the Factory in the caller
  * @param $options array An array of options to pass through to the search engine, they get translated to the $searchOptions array so you can see exactly what gets passed through
  * @return array Two elements, 'records' the list of returned records formatted through FormatBean, and 'next_offset' which will indicate to the user if there are additional records to be returned.
  */
 protected function globalSearchFullText(ServiceBase $api, array $args, SugarSearchEngineAbstractBase $searchEngine, array $options)
 {
     $options['append_wildcard'] = 1;
     if (empty($options['moduleList'])) {
         require_once 'modules/ACL/ACLController.php';
         $moduleList = SugarSearchEngineMetadataHelper::getSystemEnabledFTSModules();
         // filter based on User Access if Blank
         $ACL = new ACLController();
         // moduleList is passed by reference
         $ACL->filterModuleList($moduleList);
         $options['moduleList'] = $moduleList;
     }
     if (!empty($options['searchFields'])) {
         $customWhere = array();
         foreach ($options['moduleList'] as $module) {
             $seed = BeanFactory::getBean($module);
             $fields = array_keys($seed->field_defs);
             $existingfields = array_intersect($fields, $options['searchFields']);
             if (!empty($existingfields)) {
                 foreach ($existingfields as $field) {
                     if (empty($seed->field_defs[$field]['full_text_search'])) {
                         continue;
                     }
                     $prefix = $seed->module_name;
                     if (!isset($seed->field_defs[$field]['source']) || $seed->field_defs[$field]['source'] != 'non-db') {
                         $customWhere[] = "{$prefix}.{$field}";
                     }
                 }
             }
         }
         $options['searchFields'] = $customWhere;
     }
     $options['moduleFilter'] = $options['moduleList'];
     $results = $searchEngine->search($options['query'], $options['offset'], $options['limit'], $options);
     $returnedRecords = array();
     $total = 0;
     $api->action = 'list';
     if (is_object($results)) {
         foreach ($results as $result) {
             $record = BeanFactory::retrieveBean($result->getModule(), $result->getId());
             // if we can't get the bean skip it
             if (!$record) {
                 continue;
             }
             $module = $record->module_dir;
             // Need to override the filter arg so that it looks like something formatBean expects
             if (!empty($options['fieldFilters'][$module])) {
                 $moduleFields = $options['fieldFilters'][$module];
             } else {
                 if (!empty($options['fieldFilters']['_default'])) {
                     $moduleFields = $options['fieldFilters']['_default'];
                 } else {
                     $moduleFields = array();
                 }
             }
             if (!empty($moduleFields) && !in_array('id', $moduleFields)) {
                 $moduleFields[] = 'id';
             }
             $moduleArgs['fields'] = implode(',', $moduleFields);
             $formattedRecord = $this->formatBean($api, $moduleArgs, $record);
             $formattedRecord['_module'] = $module;
             // The SQL based search engine doesn't know how to score records, so set it to 1
             $formattedRecord['_search']['score'] = $result->getScore();
             //Add highlighted text
             $formattedRecord['_search']['highlighted'] = $result->getHighlightedHitText();
             $returnedRecords[] = $formattedRecord;
         }
         $total = $results->getTotalHits();
     }
     if ($total > $options['limit'] + $options['offset']) {
         $nextOffset = $options['offset'] + $options['limit'];
     } else {
         $nextOffset = -1;
     }
     return array('next_offset' => $nextOffset, 'records' => $returnedRecords);
 }
 /**
  *
  * 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);
     }
 }
 /**
  * Remove the cached unified_search_modules.php file
  */
 public function clearSearchCache()
 {
     global $mod_strings, $sugar_config;
     if ($this->show_output) {
         echo "<h3>{$mod_strings['LBL_QR_CLEARSEARCH']}</h3>";
     }
     // clear sugar_cache backend for SugarSearchEngine
     SugarSearchEngineMetadataHelper::clearCache();
     // Clear the cache file AFTER the cache clear, as it will be rebuilt by
     // clearCache otherwise
     UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
 }
Example #6
0
 /**
  *
  * 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];
 }
Example #7
0
 /**
  * Get the enabled and disabled modules for the datatable
  *
  * @param $moduleFilter array Requested modules for search
  * @param $disabledModules array Requested modules for disable in search
  * @return array
  */
 protected function getFilterModules($moduleFilter, $disabledModules)
 {
     $filteredEnabled = SugarSearchEngineMetadataHelper::getUserEnabledFTSModules();
     $userDisabled = $GLOBALS['current_user']->getPreference('fts_disabled_modules');
     $userDisabled = explode(",", $userDisabled);
     // Filter by System enabled FTS modules
     $systemEnabledModules = SugarSearchEngineMetadataHelper::getSystemEnabledFTSModules();
     $userDisabled = array_intersect_key($systemEnabledModules, array_flip($userDisabled));
     $filteredEnabled = array_intersect_key($systemEnabledModules, array_flip($filteredEnabled));
     $userDisabled = $this->translateModulesList($userDisabled);
     $filteredEnabled = $this->translateModulesList($filteredEnabled);
     sort($filteredEnabled);
     if (!empty($moduleFilter)) {
         foreach ($filteredEnabled as $key => $info) {
             if (!in_array($info['module'], $moduleFilter) && in_array($info['module'], $disabledModules)) {
                 unset($filteredEnabled[$key]);
                 // its not enabled, its disabled
                 $userDisabled = $info;
             }
         }
     }
     return array('enabled' => $filteredEnabled, 'disabled' => $userDisabled);
 }
 /**
  * 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;
 }
 /**
  * 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;
 }