[SearchEngine] Fix build, add tests

This commit is contained in:
Romain Neutron
2012-10-22 18:35:41 +02:00
parent 360d7a91fb
commit 18ba7f40aa
10 changed files with 207 additions and 69 deletions

View File

@@ -152,9 +152,17 @@ class Query implements ControllerProviderInterface
$options->setSearchType($request->request->get('search_type')); $options->setSearchType($request->request->get('search_type'));
$options->setRecordType($request->request->get('recordtype')); $options->setRecordType($request->request->get('recordtype'));
$options->setMinDate($request->request->get('datemin'));
$options->setMaxDate($request->request->get('datemax'));
$min_date = $max_date = null;
if ($request->request->get('datemin')) {
$min_date = \DateTime::createFromFormat('Y/m/d H:i:s', $request->request->get('datemin') . ' 00:00:00');
}
if ($request->request->get('datemax')) {
$max_date = \DateTime::createFromFormat('Y/m/d H:i:s', $request->request->get('datemax') . ' 23:59:59');
}
$options->setMinDate($min_date);
$options->setMaxDate($max_date);
$databoxDateFields = array(); $databoxDateFields = array();
@@ -175,7 +183,7 @@ class Query implements ControllerProviderInterface
$options->setSort($request->request->get('sort'), $request->request->get('ord', PHRASEA_ORDER_DESC)); $options->setSort($request->request->get('sort'), $request->request->get('ord', PHRASEA_ORDER_DESC));
$options->useStemming($request->request->get('stemme')); $options->useStemming($request->request->get('stemme'));
$form = serialize($options); $form = $options->serialize();
$perPage = (int) $app['phraseanet.user']->getPrefs('images_per_page'); $perPage = (int) $app['phraseanet.user']->getPrefs('images_per_page');
@@ -317,21 +325,21 @@ class Query implements ControllerProviderInterface
$app->abort(400, 'Search engine options are missing'); $app->abort(400, 'Search engine options are missing');
} }
if (false !== $options = unserialize($optionsSerial)) { try {
$searchEngine = new \searchEngine_adapter($app); $options = SearchEngineOptions::hydrate($app, $optionsSerial);
$searchEngine->set_options($options); $app['phraseanet.SE']->setOptions($options);
} else { } catch (\Exception $e) {
$app->abort(400, 'Provided search engine options are not valid'); $app->abort(400, 'Provided search engine options are not valid');
} }
$pos = (int) $request->request->get('pos', 0); $pos = (int) $request->request->get('pos', 0);
$query = $request->request->get('query', ''); $query = $request->request->get('query', '');
$record = new \record_preview($app, 'RESULT', $pos, '', '', $searchEngine, $query); $record = new \record_preview($app, 'RESULT', $pos, '', $app['phraseanet.SE'], $query);
return $app->json(array( return $app->json(array(
'current' => $app['twig']->render('prod/preview/result_train.html.twig', array( 'current' => $app['twig']->render('prod/preview/result_train.html.twig', array(
'records' => $record->get_train($pos, $query, $searchEngine), 'records' => $record->get_train($pos, $query, $app['phraseanet.SE']),
'selected' => $pos 'selected' => $pos
)) ))
)); ));

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Controller\Prod; namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
@@ -111,8 +112,11 @@ class Tooltip implements ControllerProviderInterface
$search_engine = $app['phraseanet.SE']; $search_engine = $app['phraseanet.SE'];
if ($context == 'answer') { if ($context == 'answer') {
if (($search_engine_options = unserialize($app['request']->request->get('options_serial'))) !== false) { try {
$search_engine_options = SearchEngineOptions::hydrate($app, $app['request']->request->get('options_serial'));
$search_engine->setOptions($search_engine_options); $search_engine->setOptions($search_engine_options);
} catch (\Exception $e) {
} }
} }

View File

@@ -11,7 +11,9 @@
namespace Alchemy\Phrasea\SearchEngine; namespace Alchemy\Phrasea\SearchEngine;
class SearchEngineOptions implements \Serializable use Alchemy\Phrasea\Application;
class SearchEngineOptions
{ {
const RECORD_RECORD = 0; const RECORD_RECORD = 0;
const RECORD_GROUPING = 1; const RECORD_GROUPING = 1;
@@ -100,16 +102,6 @@ class SearchEngineOptions implements \Serializable
protected $sort_ord = self::SORT_MODE_DESC; protected $sort_ord = self::SORT_MODE_DESC;
protected $business_fields = array(); protected $business_fields = array();
/**
* Constructor
*
* @return SearchEngineOptions
*/
public function __construct()
{
return $this;
}
/** /**
* *
* @param string $locale * @param string $locale
@@ -356,16 +348,16 @@ class SearchEngineOptions implements \Serializable
} }
/** /**
*
* @param string $min_date
* @return SearchEngineOptions * @return SearchEngineOptions
*/ */
public function setMinDate($min_date) public function setMinDate(\DateTime $min_date = null)
{ {
if (!is_null($min_date) && trim($min_date) !== '') { if ($min_date && $this->date_max && $min_date > $this->date_max) {
$this->date_min = DateTime::createFromFormat('Y/m/d H:i:s', $min_date . ' 00:00:00'); throw new \LogicException('Min-date should be before max-date');
} }
$this->date_min = $min_date;
return $this; return $this;
} }
@@ -383,12 +375,14 @@ class SearchEngineOptions implements \Serializable
* @param string $max_date * @param string $max_date
* @return SearchEngineOptions * @return SearchEngineOptions
*/ */
public function setMaxDate($max_date) public function setMaxDate(\DateTime $max_date = null)
{ {
if (!is_null($max_date) && trim($max_date) !== '') { if ($max_date && $this->date_max && $max_date < $this->date_min) {
$this->date_max = \DateTime::createFromFormat('Y/m/d H:i:s', $max_date . ' 23:59:59'); throw new \LogicException('Min-date should be before max-date');
} }
$this->date_max = $max_date;
return $this; return $this;
} }
@@ -431,7 +425,12 @@ class SearchEngineOptions implements \Serializable
$ret = array(); $ret = array();
foreach ($this as $key => $value) { foreach ($this as $key => $value) {
if ($value instanceof \DateTime) { if ($value instanceof \DateTime) {
$value = $value->format('d-m-Y h:i:s'); $value = $value->format(DATE_ATOM);
}
if (in_array($key, array('date_fields', 'fields'))) {
$value = array_map(function(\databox_field $field) {
return $field->get_databox()->get_sbas_id() . '_' . $field->get_id();
}, $value);
} }
if (in_array($key, array('collections', 'business_fields'))) { if (in_array($key, array('collections', 'business_fields'))) {
$value = array_map(function($collection) { $value = array_map(function($collection) {
@@ -450,16 +449,27 @@ class SearchEngineOptions implements \Serializable
* @param string $serialized * @param string $serialized
* @return SearchEngineOptions * @return SearchEngineOptions
*/ */
public function unserialize($serialized) public static function hydrate(Application $app, $serialized)
{ {
$serialized = json_decode($serialized); $serialized = json_decode($serialized, true);
if (!is_array($serialized)) {
throw new \InvalidArgumentException('SearchEngineOptions data are corrupted');
}
$options = new SearchEngineOptions();
$options->disallowBusinessFields();
foreach ($serialized as $key => $value) { foreach ($serialized as $key => $value) {
if (is_null($value)) {
switch (true) {
case is_null($value):
$value = null; $value = null;
} elseif (in_array($key, array('date_min', 'date_max'))) { break;
$value = new DateTime($value); case in_array($key, array('date_min', 'date_max')):
} elseif ($value instanceof stdClass) { $value = \DateTime::createFromFormat(DATE_ATOM, $value);
break;
case $value instanceof stdClass:
$tmpvalue = (array) $value; $tmpvalue = (array) $value;
$value = array(); $value = array();
@@ -467,15 +477,79 @@ class SearchEngineOptions implements \Serializable
$k = ctype_digit($k) ? (int) $k : $k; $k = ctype_digit($k) ? (int) $k : $k;
$value[$k] = $data; $value[$k] = $data;
} }
} elseif (in_array($key, array('collections', 'business_fields'))) { break;
$value = array_map(function($base_id) { case in_array($key, array('date_fields', 'fields')):
return \collection::get_from_base_id($base_id); $value = array_map(function($serialized) use ($app) {
$data = explode('_', $serialized);
return \databox_field::get_instance($app, $app['phraseanet.appbox']->get_databox($data[0]), $data[1]);
return \collection::get_from_base_id($app, $base_id);
}, $value); }, $value);
break;
case in_array($key, array('collections', 'business_fields')):
$value = array_map(function($base_id) use ($app) {
return \collection::get_from_base_id($app, $base_id);
}, $value);
break;
} }
$this->$key = $value; $sort_by = $sort_ord = null;
switch ($key) {
case 'record_type':
$options->setRecordType($value);
break;
case 'search_type':
$options->setSearchType($value);
break;
case 'status':
$options->setStatus($value);
break;
case 'date_min':
$options->setMinDate($value);
break;
case 'date_max':
$options->setMaxDate($value);
break;
case 'i18n':
$options->setLocale($value);
break;
case 'stemming':
$options->useStemming($value);
break;
case 'sort_by':
$sort_by = $value;
break;
case 'sort_ord':
$sort_ord = $value;
break;
case 'date_fields':
$options->setDateFields($value);
break;
case 'fields':
$options->setFields($value);
break;
case 'collections':
$options->onCollections($value);
break;
case 'business_fields':
$options->allowBusinessFieldsOn($value);
break;
default:
throw new \RuntimeException(sprintf('Unable to handle key `%s`', $key));
break;
} }
return $this; }
if ($sort_by) {
if ($sort_ord) {
$options->setSort($sort_by, $sort_ord);
} else {
$options->setSort($sort_by);
}
}
return $options;
} }
} }

View File

@@ -970,8 +970,17 @@ class API_V1_adapter extends API_V1_Abstract
$options->setSearchType($params['search_type']); $options->setSearchType($params['search_type']);
$options->setRecordType($params['recordtype']); $options->setRecordType($params['recordtype']);
$options->setMinDate($params['datemin']);
$options->setMaxDate($params['datemax']); $min_date = $max_date = null;
if ($params['datemin']) {
$min_date = \DateTime::createFromFormat('Y/m/d H:i:s', $params['datemin'] . ' 00:00:00');
}
if ($params['datemax']) {
$max_date = \DateTime::createFromFormat('Y/m/d H:i:s', $params['datemax'] . ' 23:59:59');
}
$options->setMinDate($min_date);
$options->setMaxDate($max_date);
$databoxDateFields = array(); $databoxDateFields = array();

View File

@@ -85,10 +85,9 @@ class record_preview extends record_adapter
* @param string $env * @param string $env
* @param int $pos * @param int $pos
* @param mixed content $contId * @param mixed content $contId
* @param boolean $reload_train
* @return record_preview * @return record_preview
*/ */
public function __construct(Application $app, $env, $pos, $contId, $reload_train, SearchEngineInterface $search_engine = null, $query = '') public function __construct(Application $app, $env, $pos, $contId, SearchEngineInterface $search_engine = null, $query = '')
{ {
$number = null; $number = null;
$this->env = $env; $this->env = $env;
@@ -102,10 +101,10 @@ class record_preview extends record_adapter
$results = $search_engine->query($query, (int) ($pos), 1); $results = $search_engine->query($query, (int) ($pos), 1);
if ($results->get_datas()->is_empty()) { if ($results->results()->isEmpty()) {
throw new Exception('Record introuvable'); throw new Exception('Record introuvable');
} }
foreach ($results->get_datas() as $record) { foreach ($results->results() as $record) {
$number = $pos; $number = $pos;
$sbas_id = $record->get_sbas_id(); $sbas_id = $record->get_sbas_id();
$record_id = $record->get_record_id(); $record_id = $record->get_record_id();
@@ -210,7 +209,7 @@ class record_preview extends record_adapter
$page = (int) ceil($pos / $perPage); $page = (int) ceil($pos / $perPage);
$results = $search_engine->query($query, $index, $perPage); $results = $search_engine->query($query, $index, $perPage);
$this->train = $results->get_datas(); $this->train = $results->results()->toArray();
break; break;
case 'BASK': case 'BASK':
$this->train = $this->container->getElements(); $this->train = $this->container->getElements();

View File

@@ -4,6 +4,7 @@ namespace Alchemy\Phrasea\Controller\Prod;
require_once __DIR__ . '/../../../../PhraseanetWebTestCaseAuthenticatedAbstract.class.inc'; require_once __DIR__ . '/../../../../PhraseanetWebTestCaseAuthenticatedAbstract.class.inc';
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Alchemy\Phrasea\Controller\Prod\Query; use Alchemy\Phrasea\Controller\Prod\Query;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -36,10 +37,9 @@ class QueryTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
self::$DI['app']->openAccount($auth); self::$DI['app']->openAccount($auth);
self::$DI['record_24']; self::$DI['record_24'];
$options = new \searchEngine_options(); $options = new SearchEngineOptions();
$acl = self::$DI['app']['phraseanet.user']->ACL(); $options->onCollections(self::$DI['app']['phraseanet.user']->ACL()->get_granted_base());
$options->set_bases(array_keys($acl->get_granted_base()), $acl); $serializedOptions = $options->serialize();
$serializedOptions = serialize($options);
self::$DI['client']->request('POST', '/prod/query/answer-train/', array( self::$DI['client']->request('POST', '/prod/query/answer-train/', array(
'options_serial' => $serializedOptions, 'options_serial' => $serializedOptions,

View File

@@ -89,7 +89,7 @@ class ControllerTooltipTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
foreach ($routes as $route) { foreach ($routes as $route) {
$option = new SearchEngineOptions(); $option = new SearchEngineOptions();
$crawler = self::$DI['client']->request('POST', $route, array('options_serial' => serialize($option))); $crawler = self::$DI['client']->request('POST', $route, array('options_serial' => $option->serialize()));
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
} }

View File

@@ -0,0 +1,34 @@
<?php
namespace Alchemy\Phrasea\SearchEngine;
require_once __DIR__ . '/../../../PhraseanetPHPUnitAbstract.class.inc';
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
class SphinxSearchOptionsTest extends \PhraseanetPHPUnitAbstract
{
public function testSerialize()
{
$options = new SearchEngineOptions(self::$DI['app']);
$options->onCollections(array(self::$DI['collection']));
$options->allowBusinessFieldsOn(array(self::$DI['collection']));
foreach (self::$DI['collection']->get_databox()->get_meta_structure() as $field) {
$options->setFields(array($field));
$options->setDateFields(array($field));
break;
}
$min_date = new \DateTime('-5 days');
$max_date = new \DateTime('+5 days');
$options->setMinDate(\DateTime::createFromFormat(DATE_ATOM, $min_date->format(DATE_ATOM)));
$options->setMaxDate(\DateTime::createFromFormat(DATE_ATOM, $max_date->format(DATE_ATOM)));
$serialized = $options->serialize();
$this->assertEquals($options, SearchEngineOptions::hydrate(self::$DI['app'], $serialized));
}
}

View File

@@ -132,8 +132,17 @@ $options->onCollections($bas);
$options->setSearchType($parm['search_type']); $options->setSearchType($parm['search_type']);
$options->setRecordType($parm['recordtype']); $options->setRecordType($parm['recordtype']);
$options->setMinDate($parm['datemin']);
$options->setMaxDate($parm['datemax']); $min_date = $max_date = null;
if ($parm['datemin']) {
$min_date = \DateTime::createFromFormat('Y/m/d H:i:s', $parm['datemin'] . ' 00:00:00');
}
if ($parm['datemax']) {
$max_date = \DateTime::createFromFormat('Y/m/d H:i:s', $parm['datemax'] . ' 23:59:59');
}
$options->setMinDate($min_date);
$options->setMaxDate($max_date);
$databoxDateFields = array(); $databoxDateFields = array();
@@ -160,7 +169,7 @@ $options->setDateFields($databoxDateFields);
$options->setSort($parm['sort'], $parm['ord']); $options->setSort($parm['sort'], $parm['ord']);
$options->useStemming($parm['stemme']); $options->useStemming($parm['stemme']);
$form = serialize($options); $form = $options->serialize();
$perPage = $mod_xy; $perPage = $mod_xy;
@@ -458,7 +467,7 @@ if (count($result->results()) > 0) {
$(document).ready(function(){ $(document).ready(function(){
p4.tot = <?php echo $result->available(); ?>; p4.tot = <?php echo $result->available(); ?>;
p4.tot_options = '<?php echo serialize($options) ?>'; p4.tot_options = '<?php echo $options->serialize() ?>';
p4.tot_query = '<?php echo $parm['qry'] ?>'; p4.tot_query = '<?php echo $parm['qry'] ?>';
}); });

View File

@@ -10,6 +10,7 @@
*/ */
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
/** /**
* *