Add PHRAS-1539

This commit is contained in:
mike-esokia
2018-05-23 17:15:56 +04:00
committed by Mike Ng
parent f8fb8837cb
commit 04a0f56b4d
4 changed files with 215 additions and 209 deletions

View File

@@ -201,6 +201,11 @@ class ElasticSearchEngine implements SearchEngineInterface
$this->notImplemented();
}
private function notImplemented()
{
throw new LogicException('Not implemented');
}
/**
* {@inheritdoc}
*/
@@ -241,11 +246,6 @@ class ElasticSearchEngine implements SearchEngineInterface
$this->notImplemented();
}
private function notImplemented()
{
throw new LogicException('Not implemented');
}
/**
* {@inheritdoc}
*/
@@ -330,131 +330,6 @@ class ElasticSearchEngine implements SearchEngineInterface
);
}
private function buildHighlightRules(QueryContext $context)
{
$highlighted_fields = [];
foreach ($context->getHighlightedFields() as $field) {
switch ($field->getType()) {
case FieldMapping::TYPE_STRING:
$index_field = $field->getIndexField();
$raw_index_field = $field->getIndexField(true);
$highlighted_fields[$index_field] = [
// Requires calling Mapping::enableTermVectors() on this field mapping
'matched_fields' => [$index_field, $raw_index_field],
'type' => 'fvh'
];
break;
case FieldMapping::TYPE_FLOAT:
case FieldMapping::TYPE_DOUBLE:
case FieldMapping::TYPE_INTEGER:
case FieldMapping::TYPE_LONG:
case FieldMapping::TYPE_SHORT:
case FieldMapping::TYPE_BYTE:
continue;
case FieldMapping::TYPE_DATE:
default:
continue;
}
}
return [
'pre_tags' => ['[[em]]'],
'post_tags' => ['[[/em]]'],
'order' => 'score',
'fields' => $highlighted_fields
];
}
/**
* {@inheritdoc}
*/
public function autocomplete($query, SearchEngineOptions $options)
{
$params = $this->createCompletionParams($query, $options);
$res = $this->client->suggest($params);
$ret = [
'text' => [],
'byField' => []
];
foreach(array_keys($params['body']) as $fname) {
$t = [];
foreach($res[$fname] as $suggest) { // don't know why there is a sub-array level
foreach($suggest['options'] as $option) {
$text = $option['text'];
if(!in_array($text, $ret['text'])) {
$ret['text'][] = $text;
}
$t[] = [
'label' => $text,
'query' => $fname.':'.$text
];
}
}
if(!empty($t)) {
$ret['byField'][$fname] = $t;
}
}
return $ret;
}
/**
* {@inheritdoc}
*/
public function resetCache()
{
}
/**
* {@inheritdoc}
*/
public function clearCache()
{
}
/**
* {@inheritdoc}
*/
public function clearAllCache(\DateTime $date = null)
{
}
private function createCompletionParams($query, SearchEngineOptions $options)
{
$body = [];
$context = [
'record_type' => $options->getSearchType() === SearchEngineOptions::RECORD_RECORD ?
SearchEngineInterface::GEM_TYPE_RECORD : SearchEngineInterface::GEM_TYPE_STORY
];
$base_ids = $options->getBasesIds();
if (count($base_ids) > 0) {
$context['base_id'] = $base_ids;
}
$search_context = $this->context_factory->createContext($options);
$fields = $search_context->getUnrestrictedFields();
foreach($fields as $field) {
if($field->getType() == FieldMapping::TYPE_STRING) {
$k = '' . $field->getName();
$body[$k] = [
'text' => $query,
'completion' => [
'field' => "caption." . $field->getName() . ".suggest",
'context' => &$context
]
];
}
}
return [
'index' => $this->indexName,
'body' => $body
];
}
private function createRecordQueryParams($ESQuery, SearchEngineOptions $options, \record_adapter $record = null)
{
$params = [
@@ -483,66 +358,28 @@ class ElasticSearchEngine implements SearchEngineInterface
return $params;
}
private function getAggregationQueryParams(SearchEngineOptions $options)
private function createSortQueryParams(SearchEngineOptions $options)
{
$aggs = [];
// technical aggregates (enable + optional limit)
foreach (ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) {
$size = $this->options->getAggregableFieldLimit($k);
if ($size !== databox_field::FACET_DISABLED) {
if ($size === databox_field::FACET_NO_LIMIT) {
$size = ESField::FACET_NO_LIMIT;
}
$agg = [
'terms' => [
'field' => $f['field'],
'size' => $size
]
];
$aggs[$k] = $agg;
}
}
// fields aggregates
$structure = $this->context_factory->getLimitedStructure($options);
foreach ($structure->getFacetFields() as $name => $field) {
// 2015-05-26 (mdarse) Removed databox filtering.
// It was already done by the ACL filter in the query scope, so no
// document that shouldn't be displayed can go this far.
$agg = [
'terms' => [
'field' => $field->getIndexField(true),
'size' => $field->getFacetValuesLimit()
]
];
$aggs[$name] = AggregationHelper::wrapPrivateFieldAggregation($field, $agg);
}
return $aggs;
}
$sort = [];
private function createACLFilters(SearchEngineOptions $options)
{
// No ACLs if no user
if (false === $this->app->getAuthenticator()->isAuthenticated()) {
return [];
if ($options->getSortBy() === null || $options->getSortBy() === SearchEngineOptions::SORT_RELEVANCE) {
$sort['_score'] = $options->getSortOrder();
}
elseif ($options->getSortBy() === SearchEngineOptions::SORT_CREATED_ON) {
$sort['created_on'] = $options->getSortOrder();
}
elseif ($options->getSortBy() === 'recordid') {
$sort['record_id'] = $options->getSortOrder();
}
else {
$sort[sprintf('caption.%s', $options->getSortBy())] = $options->getSortOrder();
}
$acl = $this->app->getAclForUser($this->app->getAuthenticatedUser());
$grantedCollections = array_keys($acl->get_granted_base([\ACL::ACTIF]));
if (count($grantedCollections) === 0) {
return ['bool' => ['must_not' => ['match_all' => new \stdClass()]]];
if (!array_key_exists('record_id', $sort)) {
$sort['record_id'] = $options->getSortOrder();
}
$appbox = $this->app['phraseanet.appbox'];
$flagNamesMap = $this->getFlagsKey($appbox);
// Get flags rules
$flagRules = $this->getFlagsRules($appbox, $acl, $grantedCollections);
// Get intersection between collection ACLs and collection chosen by end user
$aclRules = $this->getACLsByCollection($flagRules, $flagNamesMap);
return $this->buildACLsFilters($aclRules, $options);
return $sort;
}
private function createQueryFilters(SearchEngineOptions $options)
@@ -600,27 +437,6 @@ class ElasticSearchEngine implements SearchEngineInterface
return $filters;
}
private function createSortQueryParams(SearchEngineOptions $options)
{
$sort = [];
if ($options->getSortBy() === null || $options->getSortBy() === SearchEngineOptions::SORT_RELEVANCE) {
$sort['_score'] = $options->getSortOrder();
} elseif ($options->getSortBy() === SearchEngineOptions::SORT_CREATED_ON) {
$sort['created_on'] = $options->getSortOrder();
} elseif ($options->getSortBy() === 'recordid') {
$sort['record_id'] = $options->getSortOrder();
} else {
$sort[sprintf('caption.%s', $options->getSortBy())] = $options->getSortOrder();
}
if (! array_key_exists('record_id', $sort)) {
$sort['record_id'] = $options->getSortOrder();
}
return $sort;
}
private function getFlagsKey(\appbox $appbox)
{
$flags = [];
@@ -635,6 +451,32 @@ class ElasticSearchEngine implements SearchEngineInterface
return $flags;
}
private function createACLFilters(SearchEngineOptions $options)
{
// No ACLs if no user
if (false === $this->app->getAuthenticator()->isAuthenticated()) {
return [];
}
$acl = $this->app->getAclForUser($this->app->getAuthenticatedUser());
$grantedCollections = array_keys($acl->get_granted_base([\ACL::ACTIF]));
if (count($grantedCollections) === 0) {
return ['bool' => ['must_not' => ['match_all' => new \stdClass()]]];
}
$appbox = $this->app['phraseanet.appbox'];
$flagNamesMap = $this->getFlagsKey($appbox);
// Get flags rules
$flagRules = $this->getFlagsRules($appbox, $acl, $grantedCollections);
// Get intersection between collection ACLs and collection chosen by end user
$aclRules = $this->getACLsByCollection($flagRules, $flagNamesMap);
return $this->buildACLsFilters($aclRules, $options);
}
private function getFlagsRules(\appbox $appbox, \ACL $acl, array $collections)
{
$rules = [];
@@ -766,4 +608,166 @@ class ElasticSearchEngine implements SearchEngineInterface
return [];
}
}
private function buildHighlightRules(QueryContext $context)
{
$highlighted_fields = [];
foreach ($context->getHighlightedFields() as $field) {
switch ($field->getType()) {
case FieldMapping::TYPE_STRING:
$index_field = $field->getIndexField();
$raw_index_field = $field->getIndexField(true);
$highlighted_fields[$index_field . ".light"] = [
// Requires calling Mapping::enableTermVectors() on this field mapping
// 'matched_fields' => [$index_field, $raw_index_field],
'type' => 'fvh',
];
break;
case FieldMapping::TYPE_FLOAT:
case FieldMapping::TYPE_DOUBLE:
case FieldMapping::TYPE_INTEGER:
case FieldMapping::TYPE_LONG:
case FieldMapping::TYPE_SHORT:
case FieldMapping::TYPE_BYTE:
continue;
case FieldMapping::TYPE_DATE:
default:
continue;
}
}
return [
'pre_tags' => ['[[em]]'],
'post_tags' => ['[[/em]]'],
'order' => 'score',
'fields' => $highlighted_fields
];
}
private function getAggregationQueryParams(SearchEngineOptions $options)
{
$aggs = [];
// technical aggregates (enable + optional limit)
foreach (ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) {
$size = $this->options->getAggregableFieldLimit($k);
if ($size !== databox_field::FACET_DISABLED) {
if ($size === databox_field::FACET_NO_LIMIT) {
$size = ESField::FACET_NO_LIMIT;
}
$agg = [
'terms' => [
'field' => $f['field'],
'size' => $size
]
];
$aggs[$k] = $agg;
}
}
// fields aggregates
$structure = $this->context_factory->getLimitedStructure($options);
foreach ($structure->getFacetFields() as $name => $field) {
// 2015-05-26 (mdarse) Removed databox filtering.
// It was already done by the ACL filter in the query scope, so no
// document that shouldn't be displayed can go this far.
$agg = [
'terms' => [
'field' => $field->getIndexField(true),
'size' => $field->getFacetValuesLimit()
]
];
$aggs[$name] = AggregationHelper::wrapPrivateFieldAggregation($field, $agg);
}
return $aggs;
}
/**
* {@inheritdoc}
*/
public function autocomplete($query, SearchEngineOptions $options)
{
$params = $this->createCompletionParams($query, $options);
$res = $this->client->suggest($params);
$ret = [
'text' => [],
'byField' => []
];
foreach (array_keys($params['body']) as $fname) {
$t = [];
foreach ($res[$fname] as $suggest) { // don't know why there is a sub-array level
foreach ($suggest['options'] as $option) {
$text = $option['text'];
if (!in_array($text, $ret['text'])) {
$ret['text'][] = $text;
}
$t[] = [
'label' => $text,
'query' => $fname . ':' . $text
];
}
}
if (!empty($t)) {
$ret['byField'][$fname] = $t;
}
}
return $ret;
}
private function createCompletionParams($query, SearchEngineOptions $options)
{
$body = [];
$context = [
'record_type' => $options->getSearchType() === SearchEngineOptions::RECORD_RECORD ?
SearchEngineInterface::GEM_TYPE_RECORD : SearchEngineInterface::GEM_TYPE_STORY
];
$base_ids = $options->getBasesIds();
if (count($base_ids) > 0) {
$context['base_id'] = $base_ids;
}
$search_context = $this->context_factory->createContext($options);
$fields = $search_context->getUnrestrictedFields();
foreach ($fields as $field) {
if ($field->getType() == FieldMapping::TYPE_STRING) {
$k = '' . $field->getName();
$body[$k] = [
'text' => $query,
'completion' => [
'field' => "caption." . $field->getName() . ".suggest",
'context' => &$context
]
];
}
}
return [
'index' => $this->indexName,
'body' => $body
];
}
/**
* {@inheritdoc}
*/
public function resetCache()
{
}
/**
* {@inheritdoc}
*/
public function clearCache()
{
}
/**
* {@inheritdoc}
*/
public function clearAllCache(\DateTime $date = null)
{
}
}