diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php index 95c956724b..0a9f65023e 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndexer.php @@ -353,15 +353,16 @@ class RecordIndexer private function buildCaptionMapping(array $fields, Mapping $root, $section) { $mapping = new Mapping(); + foreach ($fields as $field) { $this->addFieldToMapping($field, $mapping); } + $root->add($section, $mapping); $root ->add(sprintf('%s_all', $section), 'string') ->addLocalizedSubfields($this->locales) - ->addRawVersion() - ; + ->addRawVersion(); } private function addFieldToMapping(Field $field, Mapping $mapping) @@ -376,6 +377,7 @@ class RecordIndexer if ($type === Mapping::TYPE_STRING) { $searchable = $field->isSearchable(); $facet = $field->isFacet(); + if (!$searchable && !$facet) { $mapping->notIndexed(); } else { diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php index 3ba553353d..303fd68b90 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php @@ -36,6 +36,7 @@ class QueryContext if (is_array($this->fields)) { // Ensure we are not escaping from original fields restrictions $fields = array_intersect($this->fields, $fields); + if (!$fields) { throw new QueryException('Query narrowed to non available fields'); } @@ -56,7 +57,8 @@ class QueryContext return $this->filterFields($this->structure->getPrivateFields()); } - public function getHighlightedFields() { + public function getHighlightedFields() + { return $this->filterFields($this->structure->getAllFields()); } @@ -74,6 +76,7 @@ class QueryContext if ($name instanceof ASTField) { $name = $name->getValue(); } + return $this->structure->get($name); } @@ -82,6 +85,7 @@ class QueryContext if ($name instanceof Flag) { $name = $name->getName(); } + return $this->structure->getFlagByName($name); } @@ -96,6 +100,7 @@ class QueryContext public function localizeField(Field $field) { $index_field = $field->getIndexField(); + if ($field->getType() === Mapping::TYPE_STRING) { return $this->localizeFieldName($index_field); } else { @@ -110,6 +115,7 @@ class QueryContext $boost = ($locale === $this->queryLocale) ? '^5' : ''; $fields[] = sprintf('%s.%s%s', $field, $locale, $boost); } + // TODO Put generic analyzers on main field instead of "light" sub-field $fields[] = sprintf('%s.light^10', $field); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContextFactory.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContextFactory.php index 82759132c7..f199434677 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContextFactory.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContextFactory.php @@ -36,9 +36,11 @@ class QueryContextFactory private function getSearchedFields(SearchEngineOptions $options) { $fields = []; + foreach ($options->getFields() as $field) { $fields[] = $field->get_name(); } + return $fields; } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php index 9c5f77f9f6..7eecbc19f9 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryVisitor.php @@ -121,6 +121,7 @@ class QueryVisitor implements Visit private function visitQuery(Element $element) { $root = null; + foreach ($element->getChildren() as $child) { $root = $child->accept($this); } @@ -176,6 +177,7 @@ class QueryVisitor implements Visit private function handleBinaryExpression(Element $element, \Closure $factory) { $this->assertChildrenCount($element, 2); + $left = $element->getChild(0)->accept($this); $right = $element->getChild(1)->accept($this); @@ -284,6 +286,7 @@ class QueryVisitor implements Visit private function visitString(TreeNode $node) { $tokens = array(); + foreach ($node->getChildren() as $child) { $value = $child->getValue(); if ($value === null || !isset($value['value'])) { diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php index 06d8730c52..f4b7687aa5 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php @@ -14,17 +14,36 @@ use databox_field; */ class Field implements Typed { - private $name; - private $type; - private $is_searchable; - private $is_private; - private $facet; // facet values limit or NULL (zero means no limit) - private $thesaurus_roots; - private $used_by_collections; const FACET_DISABLED = null; const FACET_NO_LIMIT = 0; + /** + * @var string + */ + private $name; + + /** + * @var string + */ + private $type; + + /** + * @var bool + */ + private $is_searchable; + + /** + * @var bool + */ + private $is_private; + + private $facet; // facet values limit or NULL (zero means no limit) + + private $thesaurus_roots; + + private $used_by_collections; + public static function createFromLegacyField(databox_field $field) { $type = self::getTypeFromLegacy($field); @@ -83,12 +102,15 @@ class Field implements Typed Assertion::boolean($this->is_searchable); Assertion::boolean($this->is_private); + if ($this->facet !== self::FACET_DISABLED) { Assertion::integer($this->facet); } + if ($this->thesaurus_roots !== null) { Assertion::allIsInstanceOf($this->thesaurus_roots, Concept::class); } + Assertion::allScalar($this->used_by_collections); } @@ -197,6 +219,7 @@ class Field implements Typed } $thesaurus_roots = null; + if ($this->thesaurus_roots !== null || $other->thesaurus_roots !== null) { $thesaurus_roots = array_merge( (array) $this->thesaurus_roots, diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php index dab3a3986b..6fcf0e2028 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php @@ -51,14 +51,17 @@ final class GlobalStructure implements Structure { $fields = []; $flags = []; + foreach ($databoxes as $databox) { foreach ($databox->get_meta_structure() as $fieldStructure) { $fields[] = Field::createFromLegacyField($fieldStructure); } + foreach ($databox->getStatusStructure() as $status) { $flags[] = Flag::createFromLegacyStatus($status); } } + return new self($fields, $flags, MetadataHelper::createTags()); } @@ -87,20 +90,25 @@ final class GlobalStructure implements Structure public function add(Field $field) { $name = $field->getName(); + if (isset($this->fields[$name])) { $field = $this->fields[$name]->mergeWith($field); } + $this->fields[$name] = $field; if ($field->getType() === Mapping::TYPE_DATE) { $this->date_fields[$name] = $field; } + if ($field->isPrivate()) { $this->private[$name] = $field; } + if ($field->isFacet() && $field->isSearchable()) { $this->facets[$name] = $field; } + if ($field->hasConceptInference()) { $this->thesaurus_fields[$name] = $field; }