mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 18:03:17 +00:00
Merge branch 'master' of https://github.com/alchemy-fr/Phraseanet into PHRAS-1832-port-1776-to-master
This commit is contained in:
@@ -59,14 +59,14 @@ class SearchController extends Controller
|
||||
|
||||
$result = $this->getSearchEngine()->query($query, $options);
|
||||
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $result->getUserQuery());
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $result->getQueryText());
|
||||
|
||||
// log array of collectionIds (from $options) for each databox
|
||||
$collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox();
|
||||
foreach ($collectionsReferencesByDatabox as $sbid => $references) {
|
||||
$databox = $this->findDataboxById($sbid);
|
||||
$collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references);
|
||||
$this->getSearchEngineLogger()->log($databox, $result->getUserQuery(), $result->getTotal(), $collectionsIds);
|
||||
$this->getSearchEngineLogger()->log($databox, $result->getQueryText(), $result->getTotal(), $collectionsIds);
|
||||
}
|
||||
|
||||
$this->getSearchEngine()->clearCache();
|
||||
|
@@ -1509,14 +1509,14 @@ class V1Controller extends Controller
|
||||
|
||||
$search_result = $this->getSearchEngine()->query((string)$request->get('query'), $options);
|
||||
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getUserQuery());
|
||||
$this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getQueryText());
|
||||
|
||||
// log array of collectionIds (from $options) for each databox
|
||||
$collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox();
|
||||
foreach ($collectionsReferencesByDatabox as $sbid => $references) {
|
||||
$databox = $this->findDataboxById($sbid);
|
||||
$collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references);
|
||||
$this->getSearchEngineLogger()->log($databox, $search_result->getUserQuery(), $search_result->getTotal(), $collectionsIds);
|
||||
$this->getSearchEngineLogger()->log($databox, $search_result->getQueryText(), $search_result->getTotal(), $collectionsIds);
|
||||
}
|
||||
|
||||
$this->getSearchEngine()->clearCache();
|
||||
|
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Cache\Exception;
|
||||
use Alchemy\Phrasea\Collection\Reference\CollectionReference;
|
||||
use Alchemy\Phrasea\Controller\Controller;
|
||||
use Alchemy\Phrasea\Core\Configuration\DisplaySettingService;
|
||||
use Alchemy\Phrasea\Model\Entities\ElasticsearchRecord;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Search\QueryContextFactory;
|
||||
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
|
||||
@@ -132,9 +133,9 @@ class QueryController extends Controller
|
||||
// since the query comes from a submited form, normalize crlf,cr,lf ...
|
||||
$query = StringHelper::crlfNormalize($query);
|
||||
|
||||
$json = array(
|
||||
$json = [
|
||||
'query' => $query
|
||||
);
|
||||
];
|
||||
|
||||
$options = SearchEngineOptions::fromRequest($this->app, $request);
|
||||
|
||||
@@ -168,7 +169,7 @@ class QueryController extends Controller
|
||||
foreach ($collectionsReferencesByDatabox as $sbid => $references) {
|
||||
$databox = $this->findDataboxById($sbid);
|
||||
$collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references);
|
||||
$this->getSearchEngineLogger()->log($databox, $result->getUserQuery(), $result->getTotal(), $collectionsIds);
|
||||
$this->getSearchEngineLogger()->log($databox, $result->getQueryText(), $result->getTotal(), $collectionsIds);
|
||||
}
|
||||
|
||||
$proposals = $firstPage ? $result->getProposals() : false;
|
||||
@@ -274,29 +275,69 @@ class QueryController extends Controller
|
||||
} else {
|
||||
$template = 'prod/results/records.html.twig';
|
||||
}
|
||||
|
||||
$json['results'] = $this->render($template, ['results'=> $result]);
|
||||
|
||||
/** Debug */
|
||||
$json['parsed_query'] = $result->getEngineQuery();
|
||||
/** End debug */
|
||||
|
||||
$fieldLabels = [];
|
||||
// add technical fields
|
||||
$fieldLabels = [];
|
||||
foreach(ElasticsearchOptions::getAggregableTechnicalFields() as $k => $f) {
|
||||
$fieldLabels[$k] = $this->app->trans($f['label']);
|
||||
}
|
||||
|
||||
// add databox fields
|
||||
// get infos about fields, fusionned and by databox
|
||||
$fieldsInfos = []; // by databox
|
||||
foreach ($this->app->getDataboxes() as $databox) {
|
||||
$sbasId = $databox->get_sbas_id();
|
||||
$fieldsInfos[$sbasId] = [];
|
||||
foreach ($databox->get_meta_structure() as $field) {
|
||||
if (!isset($fieldLabels[$field->get_name()])) {
|
||||
$fieldLabels[$field->get_name()] = $field->get_label($this->app['locale']);
|
||||
$name = $field->get_name();
|
||||
$fieldsInfos[$sbasId][$name] = [
|
||||
'label' => $field->get_label($this->app['locale']),
|
||||
'type' => $field->get_type(),
|
||||
'business' => $field->isBusiness(),
|
||||
'multi' => $field->is_multi(),
|
||||
];
|
||||
if (!isset($fieldLabels[$name])) {
|
||||
$fieldLabels[$name] = $field->get_label($this->app['locale']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$facets = [];
|
||||
// populates fileds infos
|
||||
$json['fields'] = $fieldsInfos;
|
||||
|
||||
// populates rawresults
|
||||
// need acl so the result will not include business fields where not allowed
|
||||
$acl = $this->getAclForUser();
|
||||
$json['rawResults'] = [];
|
||||
/** @var ElasticsearchRecord $record */
|
||||
foreach($result->getResults() as $record) {
|
||||
$rawRecord = $record->asArray();
|
||||
|
||||
$sbasId = $record->getDataboxId();
|
||||
$baseId = $record->getBaseId();
|
||||
|
||||
$caption = $rawRecord['caption'];
|
||||
if($acl && $acl->has_right_on_base($baseId, \ACL::CANMODIFRECORD)) {
|
||||
$caption = array_merge($caption, $rawRecord['privateCaption']);
|
||||
}
|
||||
|
||||
// read the fields following the structure order
|
||||
$rawCaption = [];
|
||||
foreach($fieldsInfos[$sbasId] as $fieldName=>$fieldInfos) {
|
||||
if(array_key_exists($fieldName, $caption)) {
|
||||
$rawCaption[$fieldName] = $caption[$fieldName];
|
||||
}
|
||||
}
|
||||
$rawRecord['caption'] = $rawCaption;
|
||||
unset($rawRecord['privateCaption']);
|
||||
|
||||
$json['rawResults'][$record->getId()] = $rawRecord;
|
||||
}
|
||||
|
||||
// populates facets (aggregates)
|
||||
$facets = [];
|
||||
foreach ($result->getFacets() as $facet) {
|
||||
$facetName = $facet['name'];
|
||||
|
||||
@@ -311,6 +352,9 @@ class QueryController extends Controller
|
||||
$json['next_page'] = ($page < $npages && $result->getAvailable() > 0) ? ($page + 1) : false;
|
||||
$json['prev_page'] = ($page > 1 && $result->getAvailable() > 0) ? ($page - 1) : false;
|
||||
$json['form'] = $options->serialize();
|
||||
$json['queryCompiled'] = $result->getQueryCompiled();
|
||||
$json['queryAST'] = $result->getQueryAST();
|
||||
$json['queryESLib'] = $result->getQueryESLib();
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
// we'd like a message from the parser so get all the exceptions messages
|
||||
@@ -319,9 +363,9 @@ class QueryController extends Controller
|
||||
$msg .= ($msg ? "\n":"") . $e->getMessage();
|
||||
}
|
||||
$template = 'prod/results/help.html.twig';
|
||||
$result = array(
|
||||
$result = [
|
||||
'error' => $msg
|
||||
);
|
||||
];
|
||||
$json['results'] = $this->render($template, ['results'=> $result]);
|
||||
}
|
||||
|
||||
|
@@ -53,6 +53,42 @@ class ElasticsearchRecord implements RecordInterface, MutableRecordInterface
|
||||
private $flags = [];
|
||||
private $highlight = [];
|
||||
|
||||
public function asArray()
|
||||
{
|
||||
return [
|
||||
'_index' => $this->_index,
|
||||
'_type' => $this->_type,
|
||||
'_id' => $this->_id,
|
||||
'_version' => $this->_version,
|
||||
'_score' => $this->_score,
|
||||
'databoxId' => $this->databoxId,
|
||||
'recordId' => $this->recordId,
|
||||
'collectionId' => $this->collectionId,
|
||||
'baseId' => $this->baseId,
|
||||
'collectionName' => $this->collectionName,
|
||||
'mimeType' => $this->mimeType,
|
||||
'title' => $this->title,
|
||||
'originalName' => $this->originalName,
|
||||
'updated' => $this->updated,
|
||||
'created' => $this->created,
|
||||
'sha256' => $this->sha256,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'size' => $this->size,
|
||||
'uuid' => $this->uuid,
|
||||
'position' => $this->position,
|
||||
'type' => $this->type,
|
||||
'status' => $this->status,
|
||||
'isStory' => $this->isStory,
|
||||
'caption' => $this->caption,
|
||||
'privateCaption' => $this->privateCaption,
|
||||
'exif' => $this->exif,
|
||||
'subdefs' => $this->subdefs,
|
||||
'flags' => $this->flags,
|
||||
'highlight' => $this->highlight,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param string $type
|
||||
|
@@ -26,4 +26,4 @@ class V1SearchRecordsResultTransformer extends V1SearchTransformer
|
||||
{
|
||||
return $this->collection($resultView->getRecords(), $this->recordTransformer);
|
||||
}
|
||||
}
|
||||
}
|
@@ -36,7 +36,7 @@ abstract class V1SearchTransformer extends TransformerAbstract
|
||||
return $suggestion->toArray();
|
||||
}, $result->getSuggestions()->toArray()),
|
||||
'facets' => $result->getFacets(),
|
||||
'query' => $result->getEngineQuery(),
|
||||
'query' => $result->getQueryText(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -273,35 +273,35 @@ class ElasticSearchEngine implements SearchEngineInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query($string, SearchEngineOptions $options = null)
|
||||
public function query($queryText, SearchEngineOptions $options = null)
|
||||
{
|
||||
$options = $options ?: new SearchEngineOptions();
|
||||
$context = $this->context_factory->createContext($options);
|
||||
|
||||
/** @var QueryCompiler $query_compiler */
|
||||
$query_compiler = $this->app['query_compiler'];
|
||||
$recordQuery = $query_compiler->compile($string, $context);
|
||||
$queryAST = $query_compiler->parse($queryText)->dump();
|
||||
$queryCompiled = $query_compiler->compile($queryText, $context);
|
||||
|
||||
$params = $this->createRecordQueryParams($recordQuery, $options, null);
|
||||
$queryESLib = $this->createRecordQueryParams($queryCompiled, $options, null);
|
||||
|
||||
// ask ES to return field _version (incremental version number of document)
|
||||
$params['body']['version'] = true;
|
||||
$queryESLib['body']['version'] = true;
|
||||
|
||||
$params['body']['from'] = $options->getFirstResult();
|
||||
$params['body']['size'] = $options->getMaxResults();
|
||||
$queryESLib['body']['from'] = $options->getFirstResult();
|
||||
$queryESLib['body']['size'] = $options->getMaxResults();
|
||||
if($this->options->getHighlight()) {
|
||||
$params['body']['highlight'] = $this->buildHighlightRules($context);
|
||||
$queryESLib['body']['highlight'] = $this->buildHighlightRules($context);
|
||||
}
|
||||
|
||||
$aggs = $this->getAggregationQueryParams($options);
|
||||
if ($aggs) {
|
||||
$params['body']['aggs'] = $aggs;
|
||||
$queryESLib['body']['aggs'] = $aggs;
|
||||
}
|
||||
|
||||
$res = $this->client->search($params);
|
||||
$res = $this->client->search($queryESLib);
|
||||
|
||||
$results = new ArrayCollection();
|
||||
|
||||
$n = 0;
|
||||
foreach ($res['hits']['hits'] as $hit) {
|
||||
$results[] = ElasticsearchRecordHydrator::hydrate($hit, $n++);
|
||||
@@ -310,16 +310,13 @@ class ElasticSearchEngine implements SearchEngineInterface
|
||||
/** @var FacetsResponse $facets */
|
||||
$facets = $this->facetsResponseFactory->__invoke($res);
|
||||
|
||||
$query['ast'] = $query_compiler->parse($string)->dump();
|
||||
$query['query_main'] = $recordQuery;
|
||||
$query['query'] = $params['body'];
|
||||
$query['query_string'] = json_encode($params['body'], JSON_PRETTY_PRINT);
|
||||
|
||||
return new SearchEngineResult(
|
||||
$options,
|
||||
$results, // ArrayCollection of results
|
||||
$string, // the query as typed by the user
|
||||
json_encode($query),
|
||||
$results, // ArrayCollection of results
|
||||
$queryText, // the query as typed by the user
|
||||
$queryAST,
|
||||
$queryCompiled,
|
||||
$queryESLib,
|
||||
$res['took'], // duration
|
||||
$options->getFirstResult(),
|
||||
$res['hits']['total'], // available
|
||||
|
@@ -17,8 +17,10 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
class SearchEngineResult
|
||||
{
|
||||
protected $results;
|
||||
protected $user_query;
|
||||
protected $engine_query;
|
||||
protected $queryText;
|
||||
protected $queryAST;
|
||||
protected $queryCompiled;
|
||||
protected $queryESLib;
|
||||
protected $duration;
|
||||
protected $offsetStart;
|
||||
protected $available;
|
||||
@@ -37,11 +39,12 @@ class SearchEngineResult
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* SearchEngineResult constructor.
|
||||
* @param SearchEngineOptions $options
|
||||
* @param ArrayCollection $results
|
||||
* @param string $user_query query as user typed, "dog"
|
||||
* @param string $engine_query query parsed for engine, "{"ast":"<text:\"dog\">","query_main" ....
|
||||
* @param string $queryText
|
||||
* @param string $queryAST
|
||||
* @param string $queryCompiled
|
||||
* @param string $queryESLib
|
||||
* @param float $duration
|
||||
* @param int $offsetStart
|
||||
* @param int $available
|
||||
@@ -49,15 +52,17 @@ class SearchEngineResult
|
||||
* @param mixed $error
|
||||
* @param mixed $warning
|
||||
* @param ArrayCollection $suggestions
|
||||
* @param array $propositions
|
||||
* @param array $indexes
|
||||
* @param FacetsResponse|null $facets
|
||||
* @param Array $propositions
|
||||
* @param Array $indexes
|
||||
* @param FacetsResponse $facets
|
||||
*/
|
||||
public function __construct(
|
||||
SearchEngineOptions $options,
|
||||
ArrayCollection $results,
|
||||
$user_query,
|
||||
$engine_query,
|
||||
$queryText,
|
||||
$queryAST,
|
||||
$queryCompiled,
|
||||
$queryESLib,
|
||||
$duration,
|
||||
$offsetStart,
|
||||
$available,
|
||||
@@ -70,10 +75,11 @@ class SearchEngineResult
|
||||
FacetsResponse $facets = null
|
||||
) {
|
||||
$this->options = $options;
|
||||
|
||||
$this->results = $results;
|
||||
$this->user_query = $user_query;
|
||||
$this->engine_query = $engine_query;
|
||||
$this->queryText = $queryText;
|
||||
$this->queryAST = $queryAST;
|
||||
$this->queryCompiled = $queryCompiled;
|
||||
$this->queryESLib = $queryESLib;
|
||||
$this->duration = (float) $duration;
|
||||
$this->offsetStart = (int) $offsetStart;
|
||||
$this->available = (int) $available;
|
||||
@@ -104,35 +110,30 @@ class SearchEngineResult
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* The query related to these results
|
||||
* @obsolete use getUserQuery (unparsed query) or getEngineQuery (parsed)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->getEngineQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* The unparsed query related to these results
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserQuery()
|
||||
public function getQueryText()
|
||||
{
|
||||
return $this->user_query;
|
||||
return $this->queryText;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parsed query related to these results
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEngineQuery()
|
||||
|
||||
public function getQueryAST()
|
||||
{
|
||||
return $this->engine_query;
|
||||
return $this->queryAST;
|
||||
}
|
||||
|
||||
public function getQueryCompiled()
|
||||
{
|
||||
return $this->queryCompiled;
|
||||
}
|
||||
|
||||
public function getQueryESLib()
|
||||
{
|
||||
return $this->queryESLib;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user