diff --git a/apps/qubit/modules/default/actions/moveAction.class.php b/apps/qubit/modules/default/actions/moveAction.class.php index 9ae8160ddc..22273eaafa 100644 --- a/apps/qubit/modules/default/actions/moveAction.class.php +++ b/apps/qubit/modules/default/actions/moveAction.class.php @@ -124,13 +124,13 @@ public function execute($request) if (isset($request->query)) { - $query = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->query)); - $query->setDefaultOperator('AND'); - $query->setFields(array( - 'identifier', - 'referenceCode', - sprintf('i18n.%s.title', sfContext::getInstance()->user->getCulture()))); - $this->queryBool->addMust($query); + $fields = array( + 'identifier' => 1, + 'referenceCode' => 1, + sprintf('i18n.%s.title', sfContext::getInstance()->user->getCulture()) => 1); + $this->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString($request->query, $fields) + ); } else { diff --git a/apps/qubit/modules/informationobject/actions/autocompleteAction.class.php b/apps/qubit/modules/informationobject/actions/autocompleteAction.class.php index 6efd22c8c1..fdd25a8417 100644 --- a/apps/qubit/modules/informationobject/actions/autocompleteAction.class.php +++ b/apps/qubit/modules/informationobject/actions/autocompleteAction.class.php @@ -51,13 +51,12 @@ public function execute($request) } else { - $queryString = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->query)); - $queryString->setDefaultOperator('AND'); + $fields = array('i18n.'.$culture.'.title.autocomplete' => 1); // Search for referenceCode or identifier, and title if (1 == sfConfig::get('app_inherit_code_informationobject', 1)) { - $queryString->setFields(array('i18n.'.$culture.'.title.autocomplete', 'referenceCode.autocomplete')); + $fields['referenceCode.autocomplete'] = 1; // Change sort order $this->query->setSort(array( @@ -67,10 +66,12 @@ public function execute($request) } else { - $queryString->setFields(array('i18n.'.$culture.'.title.autocomplete', 'identifier')); + $fields['identifier'] = 1; } - $this->queryBool->addMust($queryString); + $this->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString($request->query, $fields) + ); } // Filter results by parent diff --git a/apps/qubit/modules/repository/actions/browseAction.class.php b/apps/qubit/modules/repository/actions/browseAction.class.php index b54f52f83e..20b2e9b9c9 100644 --- a/apps/qubit/modules/repository/actions/browseAction.class.php +++ b/apps/qubit/modules/repository/actions/browseAction.class.php @@ -122,11 +122,11 @@ public function execute($request) } else { - $queryText = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->subquery)); - $queryText->setDefaultOperator('OR'); - arElasticSearchPluginUtil::setFields($queryText, 'repository'); - - $this->search->queryBool->addMust($queryText); + $this->search->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString( + $request->subquery, arElasticSearchPluginUtil::getAllFields('repository') + ) + ); } $i18n = sprintf('i18n.%s.', $this->selectedCulture); diff --git a/apps/qubit/modules/search/actions/indexAction.class.php b/apps/qubit/modules/search/actions/indexAction.class.php index 92e0539572..94404398a1 100644 --- a/apps/qubit/modules/search/actions/indexAction.class.php +++ b/apps/qubit/modules/search/actions/indexAction.class.php @@ -28,11 +28,11 @@ public function execute($request) { parent::execute($request); - $queryText = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->query)); - $queryText->setDefaultOperator('AND'); - arElasticSearchPluginUtil::setFields($queryText, 'informationObject'); - - $this->search->queryBool->addMust($queryText); + $this->search->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString( + $request->query, arElasticSearchPluginUtil::getAllFields('informationObject') + ) + ); // Realm filter if (isset($request->repos) && ctype_digit($request->repos)) diff --git a/apps/qubit/modules/taxonomy/actions/indexAction.class.php b/apps/qubit/modules/taxonomy/actions/indexAction.class.php index 8a22855ad1..320b71c8d2 100644 --- a/apps/qubit/modules/taxonomy/actions/indexAction.class.php +++ b/apps/qubit/modules/taxonomy/actions/indexAction.class.php @@ -170,33 +170,30 @@ public function execute($request) if (1 !== preg_match('/^[\s\t\r\n]*$/', $request->subquery)) { - $queryString = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->subquery)); - switch ($request->subqueryField) { case 'preferredLabel': - $queryString->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.name')); + $fields = array('i18n.%s.name' => 1); break; case 'useForLabels': - $queryString->setFields(arElasticSearchPluginUtil::getI18nFieldNames('useFor.i18n.%s.name')); + $fields = array('useFor.i18n.%s.name' => 1); break; case 'allLabels': default: // Search over preferred label (boosted by five) and "Use for" labels - $fields = array('i18n.%s.name', 'useFor.i18n.%s.name'); - $boost = array('i18n.%s.name' => 5); - $queryString->setFields(arElasticSearchPluginUtil::getI18nFieldNames($fields, null, $boost)); - $queryString->setDefaultOperator('AND'); + $fields = array('i18n.%s.name' => 5, 'useFor.i18n.%s.name' => 1); break; } // Filter results by subquery - $this->queryBool->addMust($queryString); + $this->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString($request->subquery, $fields) + ); } // Set query diff --git a/plugins/arElasticSearchPlugin/lib/arElasticSearchPlugin.class.php b/plugins/arElasticSearchPlugin/lib/arElasticSearchPlugin.class.php index 758137aac2..d42692ba28 100644 --- a/plugins/arElasticSearchPlugin/lib/arElasticSearchPlugin.class.php +++ b/plugins/arElasticSearchPlugin/lib/arElasticSearchPlugin.class.php @@ -520,22 +520,6 @@ public function partialUpdate($object, $data) } } - /** - * Function helper to parse query strings - */ - public function parse(string $query) - { - if (empty($query)) - { - throw new Exception('No search terms specified.'); - } - - $query = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($query)); - $query->setDefaultOperator('AND'); - - return $query; - } - // --------------------------------------------------------------------------- public function delete($object) diff --git a/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginQuery.class.php b/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginQuery.class.php index 426ea056a0..a461f55f8d 100644 --- a/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginQuery.class.php +++ b/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginQuery.class.php @@ -261,35 +261,27 @@ protected function parseQuery($params, $archivalStandard) protected function queryField($field, $query, $archivalStandard) { - $query = arElasticSearchPluginUtil::escapeTerm($query); - switch ($field) { case 'identifier': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultField('identifier'); - $queryField->setDefaultOperator('AND'); - - break; - case 'referenceCode': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultField('referenceCode'); - $queryField->setDefaultOperator('AND'); + case 'descriptionIdentifier': + $fields = array($field => 1); break; case 'title': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.title')); - $queryField->setDefaultOperator('AND'); - - break; - case 'scopeAndContent': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.scopeAndContent')); - $queryField->setDefaultOperator('AND'); + case 'extentAndMedium': + case 'authorizedFormOfName': + case 'datesOfExistence': + case 'history': + case 'legalStatus': + case 'generalContext': + case 'institutionResponsibleIdentifier': + case 'sources': + case 'places': + $fields = array('i18n.%s.'.$field => 1); break; @@ -297,204 +289,90 @@ protected function queryField($field, $query, $archivalStandard) ProjectConfiguration::getActive()->loadHelpers(array('Asset', 'Qubit')); // Check archival history visibility - if (($archivalStandard == 'rad' && check_field_visibility('app_element_visibility_rad_archival_history')) - || ($archivalStandard == 'isad' && check_field_visibility('app_element_visibility_isad_archival_history')) - || ($archivalStandard != 'isad' && $archivalStandard != 'rad')) + if (($archivalStandard == 'rad' && !check_field_visibility('app_element_visibility_rad_archival_history')) + || ($archivalStandard == 'isad' && !check_field_visibility('app_element_visibility_isad_archival_history'))) { - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.archivalHistory')); - $queryField->setDefaultOperator('AND'); + return; } - break; - - case 'extentAndMedium': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.extentAndMedium')); - $queryField->setDefaultOperator('AND'); + $fields = array('i18n.%s.archivalHistory' => 1); break; case 'genre': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('genres.i18n.%s.name')); - $queryField->setDefaultOperator('AND'); + $fields = array('genres.i18n.%s.name' => 1); break; case 'subject': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('subjects.i18n.%s.name')); - $queryField->setDefaultOperator('AND'); + $fields = array('subjects.i18n.%s.name' => 1); break; case 'name': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('names.i18n.%s.authorizedFormOfName')); - $queryField->setDefaultOperator('AND'); + $fields = array('names.i18n.%s.authorizedFormOfName' => 1); break; case 'creator': - $queryField = new \Elastica\Query\BoolQuery; - - $queryCreatorTerm = new \Elastica\Query\QueryString($query); - $queryCreatorTerm->setFields(arElasticSearchPluginUtil::getI18nFieldNames('creators.i18n.%s.authorizedFormOfName')); - $queryField->addShould($queryCreatorTerm); - - $queryInheritedCreatorTerm = new \Elastica\Query\QueryString($query); - $queryInheritedCreatorTerm->setFields(arElasticSearchPluginUtil::getI18nFieldNames('inheritedCreators.i18n.%s.authorizedFormOfName')); - $queryField->addShould($queryInheritedCreatorTerm); + $fields = array( + 'creators.i18n.%s.authorizedFormOfName' => 1, + 'inheritedCreators.i18n.%s.authorizedFormOfName' => 1 + ); break; case 'place': - $queryField = new \Elastica\Query\BoolQuery; - - $queryPlaceTermName = new \Elastica\Query\QueryString($query); - $queryPlaceTermName->setFields(arElasticSearchPluginUtil::getI18nFieldNames('places.i18n.%s.name')); - $queryPlaceTermName->setDefaultOperator('AND'); - $queryField->addShould($queryPlaceTermName); - - $queryPlaceTermUseFor = new \Elastica\Query\QueryString($query); - $queryPlaceTermUseFor->setFields(arElasticSearchPluginUtil::getI18nFieldNames('places.useFor.i18n.%s.name')); - $queryPlaceTermUseFor->setDefaultOperator('AND'); - $queryField->addShould($queryPlaceTermUseFor); + $fields = array( + 'places.i18n.%s.name' => 1, + 'places.useFor.i18n.%s.name' => 1 + ); break; case 'findingAidTranscript': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultField('findingAid.transcript'); - $queryField->setDefaultOperator('AND'); + $fields = array('findingAid.transcript' => 1); break; case 'digitalObjectTranscript': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultField('transcript'); - $queryField->setDefaultOperator('AND'); + $fields = array('transcript' => 1); break; case 'allExceptFindingAidTranscript': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultOperator('AND'); - $except = array('findingAid.transcript'); - arElasticSearchPluginUtil::setFields($queryField, 'informationObject', $except); - - break; - - case 'authorizedFormOfName': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.authorizedFormOfName')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'datesOfExistence': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.datesOfExistence')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'history': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.history')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'legalStatus': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.legalStatus')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'generalContext': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.generalContext')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'institutionResponsibleIdentifier': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.institutionResponsibleIdentifier')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'sources': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.sources')); - $queryField->setDefaultOperator('AND'); + $fields = arElasticSearchPluginUtil::getAllFields( + 'informationObject', array('findingAid.transcript') + ); break; case 'parallelNames': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('parallelNames.i18n.%s.name')); - $queryField->setDefaultOperator('AND'); - - break; - case 'otherNames': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('otherNames.i18n.%s.name')); - $queryField->setDefaultOperator('AND'); - - break; - case 'occupations': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('occupations.i18n.%s.name')); - $queryField->setDefaultOperator('AND'); + $fields = array($field.'.i18n.%s.name' => 1); break; case 'occupationNotes': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('occupations.i18n.%s.content')); - $queryField->setDefaultOperator('AND'); + $fields = array('occupations.i18n.%s.content' => 1); break; case 'maintenanceNotes': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('maintenanceNotes.i18n.%s.content')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'places': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setFields(arElasticSearchPluginUtil::getI18nFieldNames('i18n.%s.places')); - $queryField->setDefaultOperator('AND'); - - break; - - case 'descriptionIdentifier': - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultField('descriptionIdentifier'); - $queryField->setDefaultOperator('AND'); + $fields = array('maintenanceNotes.i18n.%s.content' => 1); break; case '_all': default: - $queryField = new \Elastica\Query\QueryString($query); - $queryField->setDefaultOperator('AND'); $documentType = ($archivalStandard == 'isaar') ? 'actor' : 'informationObject'; - arElasticSearchPluginUtil::setFields($queryField, $documentType); + $fields = arElasticSearchPluginUtil::getAllFields($documentType); break; } - return $queryField; + return arElasticSearchPluginUtil::generateBoolQueryString($query, $fields); } protected function addToQueryBool(&$queryBool, $operator, $queryField) diff --git a/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginUtil.class.php b/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginUtil.class.php index cb2faea506..0f1e906739 100644 --- a/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginUtil.class.php +++ b/plugins/arElasticSearchPlugin/lib/arElasticSearchPluginUtil.class.php @@ -123,9 +123,8 @@ private static function getTemplate($indexType) */ private static function getHiddenFields() { - // Create array with relations (hidden field => ES mapping field) for the actual template and cultures + // Create array with relations (hidden field => ES mapping field) for the actual template $relations = array(); - $cultures = sfConfig::get('app_i18n_languages'); if (null !== $template = self::getTemplate('informationObject')) { @@ -134,20 +133,20 @@ private static function getHiddenFields() case 'isad': $relations = array( - 'isad_archival_history' => self::getI18nFieldNames('i18n.%s.archivalHistory', $cultures), - 'isad_immediate_source' => self::getI18nFieldNames('i18n.%s.acquisition', $cultures), - 'isad_appraisal_destruction' => self::getI18nFieldNames('i18n.%s.appraisal', $cultures), + 'isad_archival_history' => 'i18n.%s.archivalHistory', + 'isad_immediate_source' => 'i18n.%s.acquisition', + 'isad_appraisal_destruction' => 'i18n.%s.appraisal', 'isad_notes' => '', - 'isad_physical_condition' => self::getI18nFieldNames('i18n.%s.physicalCharacteristics', $cultures), + 'isad_physical_condition' => 'i18n.%s.physicalCharacteristics', 'isad_control_description_identifier' => '', - 'isad_control_institution_identifier' => self::getI18nFieldNames('i18n.%s.institutionResponsibleIdentifier', $cultures), - 'isad_control_rules_conventions' => self::getI18nFieldNames('i18n.%s.rules', $cultures), + 'isad_control_institution_identifier' => 'i18n.%s.institutionResponsibleIdentifier', + 'isad_control_rules_conventions' => 'i18n.%s.rules', 'isad_control_status' => '', 'isad_control_level_of_detail' => '', - 'isad_control_dates' => self::getI18nFieldNames('i18n.%s.revisionHistory', $cultures), + 'isad_control_dates' => 'i18n.%s.revisionHistory', 'isad_control_languages' => '', 'isad_control_scripts' => '', - 'isad_control_sources' => self::getI18nFieldNames('i18n.%s.sources', $cultures), + 'isad_control_sources' => 'i18n.%s.sources', 'isad_control_archivists_notes' => ''); break; @@ -155,20 +154,20 @@ private static function getHiddenFields() case 'rad': $relations = array( - 'rad_archival_history' => self::getI18nFieldNames('i18n.%s.archivalHistory', $cultures), - 'rad_physical_condition' => self::getI18nFieldNames('i18n.%s.physicalCharacteristics', $cultures), - 'rad_immediate_source' => self::getI18nFieldNames('i18n.%s.acquisition', $cultures), + 'rad_archival_history' => 'i18n.%s.archivalHistory', + 'rad_physical_condition' => 'i18n.%s.physicalCharacteristics', + 'rad_immediate_source' => 'i18n.%s.acquisition', 'rad_general_notes' => '', 'rad_conservation_notes' => '', 'rad_control_description_identifier' => '', - 'rad_control_institution_identifier' => self::getI18nFieldNames('i18n.%s.institutionResponsibleIdentifier', $cultures), - 'rad_control_rules_conventions' => self::getI18nFieldNames('i18n.%s.rules', $cultures), + 'rad_control_institution_identifier' => 'i18n.%s.institutionResponsibleIdentifier', + 'rad_control_rules_conventions' => 'i18n.%s.rules', 'rad_control_status' => '', 'rad_control_level_of_detail' => '', - 'rad_control_dates' => self::getI18nFieldNames('i18n.%s.revisionHistory', $cultures), + 'rad_control_dates' => 'i18n.%s.revisionHistory', 'rad_control_language' => '', 'rad_control_script' => '', - 'rad_control_sources' => self::getI18nFieldNames('i18n.%s.sources', $cultures)); + 'rad_control_sources' => 'i18n.%s.sources'); break; @@ -184,10 +183,7 @@ private static function getHiddenFields() if(!(bool)$setting->getValue(array('sourceCulture' => true)) && isset($relations[$setting->name]) && $relations[$setting->name] != '') { - foreach ($relations[$setting->name] as $fieldName) - { - $hiddenFields[] = $fieldName; - } + $hiddenFields[] = $relations[$setting->name]; } } @@ -195,12 +191,15 @@ private static function getHiddenFields() } /** - * Set fields for a QueryString, removing those hidden for public users and those included in the except array. + * Gets all string fields for a given index type, removing those hidden for public users + * and those included in the except array. Returns a key/value array with the field names + * as key and the boost as value, i18n fields will contain "%s" as culture placeholder, which + * will need to be replaced/extended with the required cultures before query. * * Tried to add the result fields to the cache but APC (our default caching engine) uses separate * memory spaces for web/cli and the cached fields can't be removed in arSearchPopulateTask */ - public static function setFields(\Elastica\Query\QueryString $query, $indexType, $except = array()) + public static function getAllFields($indexType, $except = array()) { // Load ES mappings $mappings = arElasticSearchPlugin::loadMappings()->asArray(); @@ -210,7 +209,6 @@ public static function setFields(\Elastica\Query\QueryString $query, $indexType, throw new sfException('Unrecognized index type: ' . $indexType); } - $cultures = sfConfig::get('app_i18n_languages'); $i18nIncludeInAll = null; if ($indexType === 'informationObject') @@ -223,48 +221,67 @@ public static function setFields(\Elastica\Query\QueryString $query, $indexType, $indexType, $mappings[$indexType], $prefix = '', - $cultures, false, $i18nIncludeInAll ); - self::setBoostValues($indexType, $allFields, $cultures); - // Remove fields in except (use array_values() because array_diff() adds keys) if (count($except) > 0) { $allFields = array_values(array_diff($allFields, $except)); } - // Do not check hidden fields for authenticated users, actors or repositories - if (sfContext::getInstance()->user->isAuthenticated() || $indexType == 'actor' || $indexType == 'repository') + // Check information object hidden fields for unauthenticated users + if ($indexType == 'informationObject' && !sfContext::getInstance()->user->isAuthenticated()) { - $query->setFields($allFields); - - return; + // Remove hidden fields from ES mapping fields (use array_values() because array_diff() adds keys) + $allFields = array_values(array_diff($allFields, self::getHiddenFields())); } - // Remove hidden fields from ES mapping fields (use array_values() because array_diff() adds keys) - $filteredFields = array_values(array_diff($allFields, self::getHiddenFields())); - - $query->setFields($filteredFields); + return self::setBoostValues($indexType, $allFields); } /** - * Delegates out to other functions based on indexType, which will in turn set the boost values for each field. + * Based on indexType, set the boost values for each field. * - * @param string $indexType which index type we're setting the field boost values for. - * @param array &$fields a reference to the fields we're setting the boost values on. - * @param array $cultures a list of cultures we'll be boosting for in i18n fields + * @param string $indexType Which index type we're setting the field boost values for. + * @param array $fields The fields we're setting the boost values on. + * + * @return array Key/value array with fieldname/boost. */ - private static function setBoostValues($indexType, &$fields, $cultures) + private static function setBoostValues($indexType, $fields) { + $boost = $boostedFields = array(); + switch ($indexType) { case 'informationObject': - arElasticSearchInformationObject::setBoostValues($fields, $cultures); + $boost = array( + 'i18n.%s.title' => 10, + 'creators.i18n.%s.authorizedFormOfName' => 6, + 'identifier' => 5, + 'subjects.i18n.%s.name' => 5, + 'i18n.%s.scopeAndContent' => 5, + 'names.i18n.%s.authorizedFormOfName' => 3, + 'places.i18n.%s.name' => 3, + ); + break; } + + foreach ($fields as $field) + { + if (isset($boost[$field])) + { + $boostedFields[$field] = $boost[$field]; + } + else + { + $boostedFields[$field] = 1; + } + } + + return $boostedFields; } /** @@ -300,7 +317,6 @@ private static function checkI18nIncludeInAll($prefix, $fieldName, $i18nIncludeI * * @param array &$fields A reference to our list of fields we're searching over with our _all query. * @param string $prefix The current prefix for the field name, e.g. "creators." for "creators.name" - * @param string $culture The current culture for the i18n field we're adding. * @param string $fieldName The current field name, e.g. "name" in "creators.name" * @param bool $foreignType Whether or not this field in question is being parsed for a foreign type, * e.g. inside informationObject.creators @@ -309,7 +325,7 @@ private static function checkI18nIncludeInAll($prefix, $fieldName, $i18nIncludeI * * */ - private static function handleI18nStringFields($rootIndexType, &$fields, $prefix, $culture, $fieldName, $foreignType, + private static function handleI18nStringFields($rootIndexType, &$fields, $prefix, $fieldName, $foreignType, $i18nIncludeInAll) { // We may add special rules for other index types in the future @@ -324,8 +340,8 @@ private static function handleI18nStringFields($rootIndexType, &$fields, $prefix break; } - // Concatenate object name ($prefix), culture and field name - $fields[] = $prefix.'i18n.'.$culture.'.'.$fieldName; + // Concatenate object name ($prefix), culture placeholder and field name + $fields[] = $prefix.'i18n.%s.'.$fieldName; } /** @@ -379,13 +395,12 @@ private static function handleNonI18nStringFields($rootIndexType, &$fields, $pre * * @param array $object An array containing the current object mappings. * @param string $prefix The current prefix for the prop name, e.g. "informationObject." in "informationObject.slug" - * @param array $cultures A list of cultures we'll be adding i18n fields from * @param bool $foreignType Whether or not this field in question is being parsed for a foreign type, * e.g. inside informationObject.creators * * @param array $i18nIncludeInAll A list of i18n fields to be allowed when searching _all */ - protected static function getAllObjectStringFields($rootIndexType, $object, $prefix, $cultures, $foreignType = false, + protected static function getAllObjectStringFields($rootIndexType, $object, $prefix, $foreignType = false, $i18nIncludeInAll = null) { $fields = array(); @@ -394,21 +409,25 @@ protected static function getAllObjectStringFields($rootIndexType, $object, $pre { foreach ($object['properties'] as $propertyName => $propertyProperties) { - // Get i18n fields for selected cultures, they're always included in _all + // Get i18n fields, they're always included in _all if ($propertyName == 'i18n') { - foreach ($cultures as $culture) + // Get the fields from a single culture and format them with + // 'i18n.%s.' to set the required cultures at query time. + foreach ($propertyProperties['properties'] as $culture => $cultureProperties) { - if (!isset($propertyProperties['properties'][$culture]['properties'])) + if ($culture == 'languages') { continue; } - foreach ($propertyProperties['properties'][$culture]['properties'] as $fieldName => $fieldProperties) + foreach ($cultureProperties['properties'] as $fieldName => $fieldProperties) { - self::handleI18nStringFields($rootIndexType, $fields, $prefix, $culture, $fieldName, $foreignType, + self::handleI18nStringFields($rootIndexType, $fields, $prefix, $fieldName, $foreignType, $i18nIncludeInAll); } + + break; } } // Get nested objects fields @@ -417,8 +436,7 @@ protected static function getAllObjectStringFields($rootIndexType, $object, $pre $nestedFields = self::getAllObjectStringFields( $rootIndexType, $object['properties'][$propertyName], - $prefix.$propertyName.'.', - $cultures + $prefix.$propertyName.'.' ); $fields = array_merge($fields, $nestedFields); @@ -430,7 +448,6 @@ protected static function getAllObjectStringFields($rootIndexType, $object, $pre $rootIndexType, $object['properties'][$propertyName], $prefix.$propertyName.'.', - $cultures, true, $i18nIncludeInAll ); @@ -453,7 +470,7 @@ protected static function getAllObjectStringFields($rootIndexType, $object, $pre } /** - * Expands i18n field names into various specified cultures, with the option to add boosting. + * Expands i18n field names into various specified cultures. * * @param array $fields Which fields to expand. For example, 'i18n.%s.title' will expand to 'i18n.en.title', * 'i18n.fr.title', 'i18n.es.title', etc. @@ -461,9 +478,8 @@ protected static function getAllObjectStringFields($rootIndexType, $object, $pre * @param array $cultures An array specifying which cultures to expand to. If not specified, we look up which * cultures are active in AtoM and go off that. * - * @param array $boost An array specifying filedName => (int)boostValue to add boost values onto the fields. */ - public static function getI18nFieldNames($fields, $cultures = null, $boost = array()) + public static function getI18nFieldNames($fields, $cultures = null) { // Get all available cultures if $cultures isn't set if (empty($cultures)) @@ -477,24 +493,74 @@ public static function getI18nFieldNames($fields, $cultures = null, $boost = arr $fields = array($fields); } - // Format fields $i18nFieldNames = array(); + + // Format fields foreach ($cultures as $culture) { foreach ($fields as $field) { - $formattedField = sprintf($field, $culture); + $i18nFieldNames[] = sprintf($field, $culture); + } + } - if (isset($boost[$field])) + return $i18nFieldNames; + } + + /** + * Generate a boolean query with a should clause for each field. + * + * @param string $query Unescaped search term. + * @param string $fields Key/value array with fieldname/boost. + * + * @return \Elastica\Query\BoolQuery The generated boolean query. + */ + public static function generateBoolQueryString($query, $fields) + { + $cultures = sfConfig::get('app_i18n_languages'); + $boolQuery = new \Elastica\Query\BoolQuery; + $query = self::escapeTerm($query); + + foreach ($fields as $field => $boost) + { + if (strpos($field, '%s') !== false) + { + foreach ($cultures as $culture) { - $formattedField .= '^'.$boost[$field]; + $boolQuery->addShould( + self::generateQueryString($query, sprintf($field, $culture), $boost) + ); } - - $i18nFieldNames[] = $formattedField; + } + else + { + $boolQuery->addShould( + self::generateQueryString($query, $field, $boost) + ); } } - return $i18nFieldNames; + return $boolQuery; + } + + /** + * Generate a query string query. + * + * @param string $query Escaped search term. + * @param string $field Full fieldname (including culture if needed). + * @param float $boost Boost for the query. Default: 1. + * @param string $operator Query operator (AND/OR). Default: AND. + * + * @return \Elastica\Query\QueryString The generated query string query. + */ + public static function generateQueryString($query, $field, $boost = 1, $operator = 'AND') + { + $queryString = new \Elastica\Query\QueryString($query); + $queryString->setDefaultOperator($operator); + $queryString->setDefaultField($field); + $queryString->setBoost($boost); + + return $queryString; } /* diff --git a/plugins/arElasticSearchPlugin/lib/model/arElasticSearchInformationObject.class.php b/plugins/arElasticSearchPlugin/lib/model/arElasticSearchInformationObject.class.php index 264fd2a742..c36ec10e59 100644 --- a/plugins/arElasticSearchPlugin/lib/model/arElasticSearchInformationObject.class.php +++ b/plugins/arElasticSearchPlugin/lib/model/arElasticSearchInformationObject.class.php @@ -174,28 +174,4 @@ public static function getChildren($parentId) return $children; } - - /** - * Set boost values for various information object fields. - * - * @param array &$fields A reference to our array of fields we're adding boost values to. - * @param array $cultures An array specifying which cultures the i18n fields cover. - */ - public static function setBoostValues(&$fields, $cultures) - { - $i18nBoostFields = array( - 'i18n.%s.title' => 10, - 'subjects.i18n.%s.name' => 5, - 'creators.i18n.%s.authorizedFormOfName' => 6, - 'names.i18n.%s.authorizedFormOfName' => 3, - 'places.i18n.%s.name' => 3, - 'i18n.%s.scopeAndContent' => 5, - ); - - $nonI18nBoostFields = array( - 'identifier' => 5, - ); - - self::addBoostValuesToFields($fields, $i18nBoostFields, $nonI18nBoostFields); - } } diff --git a/plugins/arElasticSearchPlugin/lib/model/arElasticSearchModelBase.class.php b/plugins/arElasticSearchPlugin/lib/model/arElasticSearchModelBase.class.php index 44152fe299..a2c30ea29c 100644 --- a/plugins/arElasticSearchPlugin/lib/model/arElasticSearchModelBase.class.php +++ b/plugins/arElasticSearchPlugin/lib/model/arElasticSearchModelBase.class.php @@ -138,48 +138,6 @@ public static function update($object) return true; } - /** - * Add boost values to various fields. - * - * @param array &$fields An array of the fields to be modified with their boost values added. - * @param array $i18nfields Specifies which i18n fields to boost, and which boost value to use. - * The array is in the form of 'fieldName' => (int)boostNumber - * - * @param array $nonI18nFields Same as above, except for non-i18n string fields. - */ - protected static function addBoostValuesToFields(&$fields, $i18nFields, $nonI18nFields) - { - // Expand all the i18n fields into their various cultures, add boost values - $i18nBoostFields = arElasticSearchPluginUtil::getI18nFieldNames( - array_keys($i18nFields), - null, - $i18nFields - ); - - foreach ($fields as &$field) - { - foreach ($i18nBoostFields as $i18nBoostField) - { - // Match boost field against current field, add boost if match found. - // i.e.: i18n.en.title will turn into i18n.en.title^10 - if (0 === strpos($i18nBoostField, $field)) - { - $field = $i18nBoostField; - } - } - - foreach ($nonI18nFields as $nonI18nBoostField => $boost) - { - $nonI18nBoostField = $nonI18nBoostField.'^'.$boost; - - if (0 === strpos($nonI18nBoostField, $field)) - { - $field = $nonI18nBoostField; - } - } - } - } - public static function getRelatedTerms($objectId, $taxonomyIds) { // We can't reuse this statement as there is no way to bind diff --git a/plugins/qtAccessionPlugin/modules/accession/actions/browseAction.class.php b/plugins/qtAccessionPlugin/modules/accession/actions/browseAction.class.php index 987316356a..bdb9a3176d 100644 --- a/plugins/qtAccessionPlugin/modules/accession/actions/browseAction.class.php +++ b/plugins/qtAccessionPlugin/modules/accession/actions/browseAction.class.php @@ -93,40 +93,28 @@ public function execute($request) } else { - $queryString = new \Elastica\Query\QueryString(arElasticSearchPluginUtil::escapeTerm($request->subquery)); - $queryString->setDefaultOperator('AND'); - - $boost = array( + $fields = array( + 'identifier' => 10, 'donors.i18n.%s.authorizedFormOfName' => 10, 'i18n.%s.title' => 10, 'i18n.%s.scopeAndContent' => 10, 'i18n.%s.locationInformation' => 5, 'i18n.%s.processingNotes' => 5, 'i18n.%s.sourceOfAcquisition' => 5, - 'i18n.%s.archivalHistory' => 5); - - $fields = arElasticSearchPluginUtil::getI18nFieldNames(array( - 'donors.i18n.%s.authorizedFormOfName', - 'i18n.%s.title', - 'i18n.%s.scopeAndContent', - 'i18n.%s.locationInformation', - 'i18n.%s.processingNotes', - 'i18n.%s.sourceOfAcquisition', - 'i18n.%s.archivalHistory', - 'i18n.%s.appraisal', - 'i18n.%s.physicalCharacteristics', - 'i18n.%s.receivedExtentUnits', - 'alternativeIdentifiers.i18n.%s.name', - 'creators.i18n.%s.authorizedFormOfName', - 'alternativeIdentifiers.i18n.%s.note', - 'alternativeIdentifiers.type.i18n.%s.name'), null, $boost); - - $fields[] = 'identifier^10'; - $fields[] = 'donors.contactInformations.contactPerson'; - - $queryString->setFields($fields); - - $this->queryBool->addMust($queryString); + 'i18n.%s.archivalHistory' => 5, + 'i18n.%s.appraisal' => 1, + 'i18n.%s.physicalCharacteristics' => 1, + 'i18n.%s.receivedExtentUnits' => 1, + 'alternativeIdentifiers.i18n.%s.name' => 1, + 'creators.i18n.%s.authorizedFormOfName' => 1, + 'alternativeIdentifiers.i18n.%s.note' => 1, + 'alternativeIdentifiers.type.i18n.%s.name' => 1, + 'donors.contactInformations.contactPerson' => 1, + ); + + $this->queryBool->addMust( + arElasticSearchPluginUtil::generateBoolQueryString($request->subquery, $fields) + ); $this->sortOptions['relevance'] = $this->context->i18n->__('Relevance'); }