Merge pull request #1577 from bburnichon/PHRAS-821-sort-facets

Sort Facets and Give a Name
This commit is contained in:
Benoît Burnichon
2015-11-23 17:20:43 +01:00
8 changed files with 103 additions and 21 deletions

View File

@@ -184,7 +184,30 @@ class QueryController extends Controller
$json['parsed_query'] = $result->getQuery();
/** End debug */
$json['facets'] = $result->getFacets();
$fieldLabels = [
'Base_Name' => $this->app->trans('prod::facet:base_label'),
'Collection_Name' => $this->app->trans('prod::facet:collection_label'),
'Type_Name' => $this->app->trans('prod::facet:doctype_label'),
];
foreach ($this->app->getDataboxes() as $databox) {
foreach ($databox->get_meta_structure() as $field) {
if (!isset($fieldLabels[$field->get_name()])) {
$fieldLabels[$field->get_name()] = $field->get_label($this->app['locale']);
}
}
}
$facets = [];
foreach ($result->getFacets() as $facet) {
$facetName = $facet['name'];
$facet['label'] = isset($fieldLabels[$facetName]) ? $fieldLabels[$facetName] : $facetName;
$facets[] = $facet;
}
$json['facets'] = $facets;
$json['phrasea_props'] = $proposals;
$json['total_answers'] = (int) $result->getAvailable();
$json['next_page'] = ($page < $npages && $result->getAvailable() > 0) ? ($page + 1) : false;

View File

@@ -70,13 +70,13 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
);
});
$app['search_engine.structure'] = $app->share(function ($app) {
$app['search_engine.structure'] = $app->share(function (\Alchemy\Phrasea\Application $app) {
$databoxes = $app->getDataboxes();
return GlobalStructure::createFromDataboxes($databoxes);
});
$app['elasticsearch.facets_response.factory'] = $app->protect(function (array $response) use ($app) {
return new FacetsResponse(new Escaper(), $response);
return new FacetsResponse(new Escaper(), $response, $app['search_engine.structure']);
});

View File

@@ -422,17 +422,17 @@ class ElasticSearchEngine implements SearchEngineInterface
// We always want a collection facet right now
$collection_facet_agg = array();
$collection_facet_agg['terms']['field'] = 'collection_name';
$aggs['Collection'] = $collection_facet_agg;
$aggs['Collection_Name'] = $collection_facet_agg;
// We always want a base facet right now
$base_facet_agg = array();
$base_facet_agg['terms']['field'] = 'databox_name';
$aggs['Base'] = $base_facet_agg;
$aggs['Base_Name'] = $base_facet_agg;
// We always want a type facet right now
$base_facet_agg = array();
$base_facet_agg['terms']['field'] = 'type';
$aggs['Type'] = $base_facet_agg;
$aggs['Type_Name'] = $base_facet_agg;
$structure = $this->context_factory->getLimitedStructure($options);
foreach ($structure->getFacetFields() as $name => $field) {

View File

@@ -3,11 +3,11 @@
namespace Alchemy\Phrasea\SearchEngine\Elastic\Search;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\SearchEngine\Elastic\Structure\Structure;
use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
use Doctrine\Common\Collections\ArrayCollection;
use JsonSerializable;
class FacetsResponse implements JsonSerializable
class FacetsResponse
{
private $escaper;
private $facets = array();
@@ -71,14 +71,14 @@ class FacetsResponse implements JsonSerializable
private function buildQuery($name, $value)
{
switch($name) {
case 'Collection':
case 'Collection_Name':
return sprintf('collection:%s', $this->escaper->escapeWord($value));
case 'Base':
case 'Base_Name':
return sprintf('database:%s', $this->escaper->escapeWord($value));
case 'Type':
case 'Type_Name':
return sprintf('type:%s', $this->escaper->escapeWord($value));
default:
return sprintf('%s = %s',
return sprintf('field.%s = %s',
$this->escaper->escapeWord($name),
$this->escaper->escapeWord($value));
}
@@ -89,7 +89,10 @@ class FacetsResponse implements JsonSerializable
throw new RuntimeException('Invalid aggregation response');
}
public function jsonSerialize()
/**
* @return array
*/
public function toArray()
{
return $this->facets;
}

View File

@@ -27,6 +27,7 @@ class SearchEngineResult
protected $suggestions;
protected $propositions;
protected $indexes;
/** @var FacetsResponse */
protected $facets;
public function __construct(ArrayCollection $results, $query, $duration, $offsetStart, $available, $total, $error,
@@ -182,6 +183,6 @@ class SearchEngineResult
*/
public function getFacets()
{
return $this->facets;
return $this->facets->toArray();
}
}

View File

@@ -1,14 +1,14 @@
<?xml version='1.0' encoding='utf-8'?>
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2015-11-19T12:47:12Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<file date="2015-11-23T15:43:56Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
</header>
<body>
<trans-unit id="da39a3ee5e6b4b0d3255bfef95601890afd80709" resname="">
<source/>
<target state="new"/>
<source></source>
<target state="new"></target>
<jms:reference-file line="47">Form/Configuration/EmailFormType.php</jms:reference-file>
<jms:reference-file line="60">Form/Login/PhraseaAuthenticationForm.php</jms:reference-file>
</trans-unit>
@@ -136,7 +136,7 @@
</trans-unit>
<trans-unit id="de0804eb70c10b14d71df74292e45c6daa13d672" resname="%number% documents&lt;br/&gt;selectionnes" approved="yes">
<source><![CDATA[%number% documents<br/>selectionnes]]></source>
<target state="translated">%number% documents&lt;br/&gt;selected</target>
<target state="translated"><![CDATA[%number% documents<br/>selected]]></target>
<jms:reference-file line="154">Controller/Prod/QueryController.php</jms:reference-file>
</trans-unit>
<trans-unit id="ac5c6fe2979cfa2496c95dcb218f135fd916040d" resname="%quantity% Stories attached to the WorkZone" approved="yes">
@@ -10837,6 +10837,21 @@
<target state="translated">Find</target>
<jms:reference-file line="280">prod/actions/edit_default.html.twig</jms:reference-file>
</trans-unit>
<trans-unit id="cb08c63b1c016e31a255a38795c8e4cb66b0e66e" resname="prod::facet:base_label">
<source>prod::facet:base_label</source>
<target state="new">Base</target>
<jms:reference-file line="188">Controller/Prod/QueryController.php</jms:reference-file>
</trans-unit>
<trans-unit id="36fa870bac03b1a7c83f2b7030bf93ed4718e0a7" resname="prod::facet:collection_label">
<source>prod::facet:collection_label</source>
<target state="new">Collection</target>
<jms:reference-file line="189">Controller/Prod/QueryController.php</jms:reference-file>
</trans-unit>
<trans-unit id="12988153991e94fd6fc58934903504b0bf03c30a" resname="prod::facet:doctype_label">
<source>prod::facet:doctype_label</source>
<target state="new">Document Type</target>
<jms:reference-file line="190">Controller/Prod/QueryController.php</jms:reference-file>
</trans-unit>
<trans-unit id="2a4d65f9a1aaeec92617d3d5dc3ef0167a019e82" resname="prod::recherche: Attention : la liste des bases selectionnees pour la recherche a ete changee." approved="yes">
<source>prod::recherche: Attention : la liste des bases selectionnees pour la recherche a ete changee.</source>
<target state="translated">Warning, list of collections to search in has been changed</target>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
<file date="2015-11-19T12:47:12Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<file date="2015-11-23T15:43:56Z" source-language="en" target-language="en" datatype="plaintext" original="not.available">
<header>
<tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
<note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>

View File

@@ -620,16 +620,56 @@ function loadFacets(facets) {
});
// Facet
return {
title: facet.name,
name: facet.name,
title: facet.label,
folder: true,
children: values,
expanded: _.isUndefined(selectedFacetValues[facet.name])
};
});
treeSource.sort(sortFacets('title', true, function(a){return a.toUpperCase()}));
treeSource = sortByPredefinedFacets(treeSource, 'name', ['Base_Name', 'Collection_Name', 'Type_Name']);
return getFacetsTree().reload(treeSource);
}
function sortByPredefinedFacets(source, field, predefinedFieldOrder) {
var filteredSource = source,
ordered = [];
_.forEach(predefinedFieldOrder, function(fieldValue, index){
_.forEach(source, function(facet, facetIndex) {
if (facet[field] === fieldValue) {
ordered.push(facet);
// remove from filtered
filteredSource.splice(facetIndex, 1);
}
});
});
// push reordoned objects on top of array:
// walk backward
var olen = ordered.length;
for(var i = olen-1; i>=0; i--) {
filteredSource.unshift(ordered[i]);
}
return filteredSource;
}
// from stackoverflow
// http://stackoverflow.com/questions/979256/sorting-an-array-of-javascript-objects/979325#979325
function sortFacets(field, reverse, primer) {
var key = function (x) {return primer ? primer(x[field]) : x[field]};
return function (a,b) {
var A = key(a), B = key(b);
return ( (A < B) ? -1 : ((A > B) ? 1 : 0) ) * [-1,1][+!!reverse];
}
}
function getFacetsTree() {
var $facetsTree = $('#proposals');
if (!$facetsTree.data('ui-fancytree')) {