Author: mj
Date: Tue Feb 7 13:38:57 2012
New Revision: 10815
Log:
Snapshot commit against issue 2199. Refactor mapping into object-specific
classes, add bulk load functionality, refactor results template. Stubs for
document mapping and facet retreival (in progress).
Added:
trunk/plugins/qtElasticSearchPlugin/lib/model/
trunk/plugins/qtElasticSearchPlugin/lib/model/QubitActorMapping.class.php
trunk/plugins/qtElasticSearchPlugin/lib/model/QubitInformationObjectMapping.class.php
Modified:
trunk/lib/QubitSearch.class.php
trunk/plugins/qtElasticSearchPlugin/config/qtElasticSearchPluginConfiguration.class.php
trunk/plugins/qtElasticSearchPlugin/lib/qtElasticSearchPlugin.class.php
trunk/plugins/qtElasticSearchPlugin/modules/search/actions/indexAction.class.php
trunk/plugins/qtElasticSearchPlugin/modules/search/templates/_searchResults.php
Modified: trunk/lib/QubitSearch.class.php
==============================================================================
--- trunk/lib/QubitSearch.class.php Tue Feb 7 00:27:13 2012 (r10814)
+++ trunk/lib/QubitSearch.class.php Tue Feb 7 13:38:57 2012 (r10815)
@@ -19,7 +19,6 @@
class QubitSearch extends xfIndexSingle
{
-
// allow disabling search index via boolean flag
public $disabled = false;
@@ -75,6 +74,11 @@
*/
protected function initialize()
{
+ if (self::$_instance instanceof qtElasticSearchPlugin)
+ {
+ return;
+ }
+
$this->setEngine(new
xfLuceneEngine(sfConfig::get('sf_data_dir').'/index'));
$this->getEngine()->open();
Zend_Search_Lucene_Search_QueryParser::setDefaultOperator(Zend_Search_Lucene_Search_QueryParser::B_AND);
@@ -87,6 +91,7 @@
{
if (self::getInstance() instanceof qtElasticSearchPlugin)
{
+ self::getInstance()->logger = $this->getLogger();
self::getInstance()->qubitPopulate($options);
return;
}
@@ -199,6 +204,11 @@
public function optimize()
{
+ if (self::getInstance() instanceof qtElasticSearchPlugin)
+ {
+ return;
+ }
+
$start = microtime(true);
$this->getLogger()->log('Optimizing index...', $this->getName());
$this->getEngine()->optimize();
Modified:
trunk/plugins/qtElasticSearchPlugin/config/qtElasticSearchPluginConfiguration.class.php
==============================================================================
---
trunk/plugins/qtElasticSearchPlugin/config/qtElasticSearchPluginConfiguration.class.php
Tue Feb 7 00:27:13 2012 (r10814)
+++
trunk/plugins/qtElasticSearchPlugin/config/qtElasticSearchPluginConfiguration.class.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -26,6 +26,9 @@
// this is the name of the index; must be unique within the server
$index = 'qubit',
+ // default number of documents to include in a batch (bulk) request
+ $batchSize = 500,
+
// server defaults to localhost:9200 if omitted
// can also be used to configure a cluster of ElasticSearch nodes
// see more info at: http://ruflin.github.com/Elastica/
Added: trunk/plugins/qtElasticSearchPlugin/lib/model/QubitActorMapping.class.php
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/plugins/qtElasticSearchPlugin/lib/model/QubitActorMapping.class.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -0,0 +1,90 @@
+<?php
+ /*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ /**
+ * This class is used to provide a model mapping for storing QubitActor
objects
+ * within an ElasticSearch document index.
+ *
+ * @package qtElasticSearchPlugin
+ * @author MJ Suhonos <[email protected]>
+ * @version svn:$Id: QubitActorMapping.class.php 10316 2011-11-14
22:40:18Z mj $
+ */
+
+ class QubitActorMapping
+ {
+ static function getProperties()
+ {
+ // TODO ...
+ return array();
+ }
+
+ static function serialize($object)
+ {
+ $serialized = array();
+
+ $objectI18ns = $object->actorI18ns->indexBy('culture');
+
+ // Add other forms of name
+ $criteria = new Criteria;
+ $criteria->addJoin(QubitOtherNameI18n::ID, QubitOtherName::ID);
+ $criteria->add(QubitOtherName::OBJECT_ID, $object->id);
+
+ $otherNames = QubitOtherNameI18n::get($criteria);
+
+ foreach ($otherNames as $otherName)
+ {
+ $serialized[$otherName->culture]['otherNames'][] = $otherName->name;
+ }
+
+ // get all i18n-ized versions of this object
+ foreach ($objectI18ns as $culture => $objectI18n)
+ {
+ // index all values on the i18n-ized object
+ foreach (self::getI18nFields(get_class($object)) as $method)
+ {
+ $value = call_user_func(array($objectI18n, 'get' . $method));
+ if (!is_null($value))
+ {
+ $serialized[$culture][lcfirst($method)] = $value;
+ }
+ }
+ }
+
+ return $serialized;
+ }
+
+ public static function getI18nFields($class)
+ {
+ // use reflection on i18n object to get property list from class
constants
+ if (class_exists($class . 'I18n'))
+ {
+ $reflect = new ReflectionClass($class . 'I18n');
+ $i18nFields = $reflect->getConstants();
+
+ // these constants cannot be accessed by __get()
+ unset($i18nFields['DATABASE_NAME']);
+ unset($i18nFields['TABLE_NAME']);
+
+ // id and culture are not used for indexing
+ unset($i18nFields['ID']);
+ unset($i18nFields['CULTURE']);
+
+ return array_map(array('sfInflector', 'camelize'),
array_map('strtolower', array_keys($i18nFields)));
+ }
+ }
+ }
\ No newline at end of file
Added:
trunk/plugins/qtElasticSearchPlugin/lib/model/QubitInformationObjectMapping.class.php
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++
trunk/plugins/qtElasticSearchPlugin/lib/model/QubitInformationObjectMapping.class.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -0,0 +1,185 @@
+<?php
+ /*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ /**
+ * This class is used to provide a model mapping for storing
QubitInformationObject objects
+ * within an ElasticSearch document index.
+ *
+ * @package qtElasticSearchPlugin
+ * @author MJ Suhonos <[email protected]>
+ * @version svn:$Id: QubitInformationObjectMapping.class.php 10316
2011-11-14 22:40:18Z mj $
+ */
+
+ class QubitInformationObjectMapping
+ {
+ static function getProperties()
+ {
+ // TODO ...
+ return array();
+ }
+
+ static function serialize($object)
+ {
+ $serialized = array();
+
+ $objectI18ns = $object->informationObjectI18ns->indexBy('culture');
+
+ // indexed?
+ $serialized['slug'] = $object->slug;
+ $serialized['referenceCode'] = $object->referenceCode;
+ $serialized['identifier'] = $object->identifier;
+
+ // used for filtering
+ $serialized['publicationStatusId'] =
$object->getPublicationStatus()->status->id;
+
+ // linking information; NOT indexed
+// $serialized['parent']['id'] = $object->parentId;
+// $serialized['parent']['slug'] = $object->parent->slug;
+
+ // use for linking "part of" in results; NOT indexed
+ $root = $object->getCollectionRoot();
+ $serialized['collectionRoot']['id'] = $root->id;
+ $serialized['collectionRoot']['slug'] = $root->slug;
+ $serialized['collectionRoot']['title'] = $root->title;
+
+ // repository information for linking and filtering; indexed
+ if ($repository = $object->getRepository(array('inherit' => true)))
+ {
+ $serialized['repository']['id'] = $repository->id;
+ $serialized['repository']['slug'] = $repository->slug;
+
+ foreach ($objectI18ns as $culture => $objectI18n)
+ {
+ $serialized['repository'][$culture]['name'] =
$repository->getAuthorizedFormOfName();
+ }
+ }
+
+ // embed digital object information; indexed?
+ if ($digitalObject = $object->getDigitalObject())
+ {
+ $serialized['digitalObject']['mediaTypeId'] =
$digitalObject->mediaTypeId;
+
+ if ($digitalObject->thumbnail)
+ {
+ $serialized['digitalObject']['thumbnail_FullPath'] =
$digitalObject->thumbnail->getFullPath();
+ }
+ }
+
+ // embed dates as an array
+ foreach ($object->getDates() as $date)
+ {
+ $save_date = array(
+ 'id' => $date->id,
+ 'rendered' =>
Qubit::renderDateStartEnd($date->getDate(array('cultureFallback' => true)),
$date->startDate, $date->endDate),
+ 'type' => $date->getType(array('cultureFallback' =>
true))->__toString()
+ );
+
+ if (isset($date->actor))
+ {
+ $save_date['actor'] = $date->actor->__toString();
+ }
+ $serialized['dates'][] = $save_date;
+ }
+
+ foreach ($objectI18ns as $culture => $objectI18n)
+ {
+ $serialized[$culture]['creator'] =
$object->getCreatorsNameString(array('culture' => $culture));
+ $serialized[$culture]['creatorHistory'] =
$object->getCreatorsHistoryString(array('culture' => $culture));
+
+ if ($level = $object->getLevelOfDescription())
+ {
+ $serialized[$culture]['levelOfDescription'] =
$level->getName(array('culture' => $culture));
+ }
+
+ foreach ($object->getNotes() as $note)
+ {
+ $serialized[$culture]['notes'][] = $note->getContent(array('culture'
=> $culture));
+ }
+
+ foreach ($object->getNameAccessPoints() as $name)
+ {
+ $serialized[$culture]['names'][] =
$name->object->getAuthorizedFormOfName(array('culture' => $culture));
+ }
+
+ foreach ($object->getSubjectAccessPoints() as $subject)
+ {
+ $term = $subject->getTerm();
+ $serialized[$culture]['subjects'][] = $term->getName(array('culture'
=> $culture));
+
+ if ($term->otherNames)
+ {
+ foreach ($term->otherNames as $otherName)
+ {
+ $serialized[$culture]['subjects'][] =
$otherName->getName(array('culture' => $culture));
+ }
+ }
+ }
+
+ foreach ($object->getPlaceAccessPoints() as $place)
+ {
+ $term = $place->getTerm();
+ $serialized[$culture]['places'][] = $term->getName(array('culture'
=> $culture));
+
+ if ($term->otherNames)
+ {
+ foreach ($term->otherNames as $otherName)
+ {
+ $serialized[$culture]['places'][] =
$otherName->getName(array('culture' => $culture));
+ }
+ }
+ }
+
+ }
+
+ // get all i18n-ized versions of this object
+ foreach ($objectI18ns as $culture => $objectI18n)
+ {
+ // index all values on the i18n-ized object
+ foreach (self::getI18nFields(get_class($object)) as $method)
+ {
+ $value = call_user_func(array($objectI18n, 'get' . $method));
+ if (!is_null($value))
+ {
+ $serialized[$culture][lcfirst($method)] = $value;
+ }
+ }
+ }
+
+ return $serialized;
+ }
+
+ public static function getI18nFields($class)
+ {
+ // use reflection on i18n object to get property list from class
constants
+ if (class_exists($class . 'I18n'))
+ {
+ $reflect = new ReflectionClass($class . 'I18n');
+ $i18nFields = $reflect->getConstants();
+
+ // these constants cannot be accessed by __get()
+ unset($i18nFields['DATABASE_NAME']);
+ unset($i18nFields['TABLE_NAME']);
+
+ // id and culture are not used for indexing
+ unset($i18nFields['ID']);
+ unset($i18nFields['CULTURE']);
+
+ return array_map(array('sfInflector', 'camelize'),
array_map('strtolower', array_keys($i18nFields)));
+ }
+ }
+ }
Modified:
trunk/plugins/qtElasticSearchPlugin/lib/qtElasticSearchPlugin.class.php
==============================================================================
--- trunk/plugins/qtElasticSearchPlugin/lib/qtElasticSearchPlugin.class.php
Tue Feb 7 00:27:13 2012 (r10814)
+++ trunk/plugins/qtElasticSearchPlugin/lib/qtElasticSearchPlugin.class.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -21,9 +21,9 @@
* This class is used to provide a singleton object that uses an ElasticSearch
instance for
* application indexing, querying, faceting, etc.
*
- * @package Qubit
+ * @package qtElasticSearchPlugin
* @author MJ Suhonos <[email protected]>
- * @version svn:$Id: qtElasticSearchPlugin.class.php 10316 2011-11-14
22:40:18Z david $
+ * @version svn:$Id: qtElasticSearchPlugin.class.php 10316 2011-11-14
22:40:18Z mj $
*/
class qtElasticSearchPlugin
@@ -31,8 +31,14 @@
// allow disabling search index via boolean flag
public $disabled = false;
+ // allow modifying the batch size
+ public $batchSize;
+
+ private $batchMode = false;
+ private $batchDocs = array();
+
public $index = null;
-
+
/*
* Enable singleton creation via getInstance()
*/
@@ -51,86 +57,149 @@
// constructor
public function __construct()
{
- //I don't understand how a heart is a spade
- //But somehow the vital connection is made
+ $this->batchSize = qtElasticSearchPluginConfiguration::$batchSize;
+
+ // I don't understand how a heart is a spade
+ // But somehow the vital connection is made
$client = new Elastica_Client(qtElasticSearchPluginConfiguration::$server);
$this->index =
$client->getIndex(qtElasticSearchPluginConfiguration::$index);
+
+ $this->initialize();
}
// destructor
public function __destruct()
{
+ // if there are still documents in the batch queue, send them
+ if ($this->batchMode && count($this->batchDocs) > 0)
+ {
+ $this->index->addDocuments($this->batchDocs);
+ }
+
// I don't understand how the last card is played
// But somehow the vital connection is made
$this->index->flush();
}
+ protected function initialize()
+ {
+ try {
+ $this->index->open();
+
+ } catch (Exception $e) {
+
+ // if the index has not been initialized, create it
+ if ($e instanceof Elastica_Exception_Response) {
+ $this->index->create(array(), true);
+ }
+ }
+
+/*
+ // apply type mappings for each indexed object type
+ // TODO: can load these dynamically from the ./model directory
+ foreach (array('QubitInformationObject', 'QubitActor') as $type)
+ {
+ $mapping = new Elastica_Type_Mapping();
+
+ $mapping->setType($this->index->getType($type));
+ $mapping->setProperties(call_user_func(array($type . 'Mapping',
'getProperties')));
+
+ $mapping->send();
+ }
+*/
+ }
+
/*
* Elastica methods
*/
public function save($object)
{
- $type = $this->index->getType(get_class($object));
+ // NB: serializing from the ORM is the most expensive part
+ $type = get_class($object);
- $document = new Elastica_Document($object->id, self::serialize($object));
- $type->addDocument($document);
+ $document = new Elastica_Document($object->id, $this->serialize($object));
+ $document->setType($type);
- $type->getIndex()->refresh();
- }
+ if ($this->batchMode)
+ {
+ // add this document to the batch queue
+ $this->batchDocs[] = $document;
- public function delete($object)
- {
- $type = $this->index->getType(get_class($object));
+ // if we have a full batch, send in bulk
+ if (count($this->batchDocs) >= $this->batchSize)
+ {
+ $this->index->addDocuments($this->batchDocs);
+ $this->index->refresh();
- $type->deleteById($object->id);
+ $this->batchDocs = array();
+ }
+ }
+ else
+ {
+ $this->index->getType($type)->addDocument($document);
+ $this->index->refresh();
+ }
}
- public function deleteById($id)
+ public function delete($object)
{
- // TODO: handle QubitInformationObject objects?
- $type = $this->index->getType('QubitActor');
-
- $type->deleteById($id);
+ $this->index->getType(get_class($object))->deleteById($object->id);
}
public function parse($querystring)
{
+ if (empty($querystring))
+ {
+ throw new Exception('No search terms specified.');
+ }
+
$query = new Elastica_Query_QueryString($querystring);
$query->setDefaultOperator('AND');
return $query;
}
+ /*
+ * ZSL compatibility methods
+ */
public function optimize()
{
$this->index->optimize();
}
- /*
- * ZSL compatibility methods
- */
public function enableBatch()
{
+ $this->batchMode = true;
}
public function disableBatch()
{
+ $this->batchMode = false;
}
+ public function deleteById($id)
+ {
+ // TODO: handle QubitInformationObject objects?
+ $this->index->getType('QubitActor')->deleteById($id);
+ }
+
+ /*
+ * ======= END ZSL compatibility methods
+ */
+
public function qubitPopulate($options)
{
sfContext::createInstance(sfProjectConfiguration::getApplicationConfiguration('qubit',
'cli', true));
- $conn = Propel::getConnection();
-
$start = microtime(true);
- sfContext::getInstance()->getLogger()->log('Populating index...',
sfLogger::EMERG);
+ $this->logger->log('Populating index...', 'qtElasticSearch');
// if we are using an offset to resume from a segfault, optimize the index
instead of deleting
if (!isset($options['actorOffset']) && !isset($options['ioOffset']))
{
$this->index->delete();
- sfContext::getInstance()->getLogger()->log('Index erased.',
sfLogger::EMERG);
+ $this->initialize();
+ $this->logger->log('Index erased.', 'qtElasticSearch');
}
else
{
@@ -138,7 +207,7 @@
}
// set buffering and updates to be batched for better performance
- self::getInstance()->enableBatch();
+ $this->enableBatch();
$actorOffset = intval($options['actorOffset']);
$ioOffset = intval($options['ioOffset']);
@@ -146,235 +215,84 @@
// index actors
if (-1 < $actorOffset)
{
- // Get count of all actors
- $sql = 'SELECT COUNT(*) from '.QubitActor::TABLE_NAME;
- $rs = $conn->query($sql);
- $rowcount = $rs->fetchColumn(0);
-
- // Get actors (with offset)
$criteria = new Criteria;
- QubitActor::addSelectColumns($criteria);
+ $criteria = QubitActor::addGetOnlyActorsCriteria($criteria);
+ $criteria->add(QubitActor::ID, QubitActor::ROOT_ID, Criteria::NOT_EQUAL);
if (0 < $actorOffset)
{
$criteria->setOffset($actorOffset);
+ $this->logger->log('Ignoring first '.$ioOffset.' actors.',
'qtElasticSearch');
}
$actors = QubitActor::get($criteria);
+ $rowcount = count($actors) + $ioOffset;
+ $total = $rowcount;
- // Loop through results, and add to search index
foreach ($actors as $key => $actor)
{
- if ($key == 0 && 0 < $actorOffset)
- {
- sfContext::getInstance()->getLogger()->log('Ignoring first
'.$actorOffset.' actors.');
- }
-
- self::save($actor);
-
- sfContext::getInstance()->getLogger()->log('"'.$actor->__toString().'"
inserted ('.round(microtime(true) - $start, 2).'s) ('.($key + $actorOffset +
1).'/'.$rowcount.')', sfLogger::EMERG);
+ $this->save($actor);
+ $this->logger->log('"'.$actor->__toString().'" inserted
('.round(microtime(true) - $start, 2).'s) ('.($key + $actorOffset +
1).'/'.$rowcount.')', 'qtElasticSearch');
}
}
else
{
- sfContext::getInstance()->getLogger()->log('Actors are ignored.');
+ $this->logger->log('Information objects are ignored.',
'qtElasticSearch');
}
// index information objects
if (-1 < $ioOffset)
{
- // Get count of all information objects
- $sql = 'SELECT COUNT(*) from '.QubitInformationObject::TABLE_NAME;
- $rs = $conn->query($sql);
- $rowcount = $rs->fetchColumn(0);
-
- // Get info objects (with offset)
$criteria = new Criteria;
- QubitInformationObject::addSelectColumns($criteria);
+ $criteria->add(QubitInformationObject::ID,
QubitInformationObject::ROOT_ID, Criteria::NOT_EQUAL);
if (0 < $ioOffset)
{
$criteria->setOffset($ioOffset);
+ $this->logger->log('Ignoring first '.$ioOffset.' information
objects.', 'qtElasticSearch');
}
$informationObjects = QubitInformationObject::get($criteria);
+ $rowcount = count($informationObjects) + $ioOffset;
+ $total = $total + $rowcount;
- // Loop through results, and add to search index
foreach ($informationObjects as $key => $informationObject)
{
- if ($key == 0 && 0 < $ioOffset)
- {
- sfContext::getInstance()->getLogger()->log('Ignoring first
'.$ioOffset.' information objects.');
- }
-
- self::save($informationObject);
-// self::addInformationObjectIndex($informationObject, $language,
$options);
-
-
sfContext::getInstance()->getLogger()->log('"'.$informationObject->__toString().'"
inserted ('.round(microtime(true) - $start, 2).'s) ('.($key + $ioOffset +
1).'/'.$rowcount.')', sfLogger::EMERG);
+ $this->save($informationObject);
+ $this->logger->log('"'.$informationObject->__toString().'" inserted
('.round(microtime(true) - $start, 2).'s) ('.($key + $ioOffset +
1).'/'.$rowcount.')', 'qtElasticSearch');
}
}
else
{
- sfContext::getInstance()->getLogger()->log('Information objects are
ignored.');
+ $this->logger->log('Information objects are ignored.',
'qtElasticSearch');
}
- sfContext::getInstance()->getLogger()->log('Index populated in
"'.round(microtime(true) - $start, 2).'" seconds.', sfLogger::EMERG);
+ // if there are still documents in the batch queue, send them
+ if ($this->batchMode && count($this->batchDocs) > 0)
+ {
+ $this->index->addDocuments($this->batchDocs);
+ $this->index->refresh();
+ $this->batchDocs = array();
+ }
+
+ $this->logger->log('Index populated with "'.($total).'" documents in
"'.round(microtime(true) - $start, 2).'" seconds.', 'qtElasticSearch');
}
/*
- * take an object and return an associative multidimensional array of
properties
- * TODO: object classes should implement a static ::serialize() method
- * NB: this is incompatible with the PHP Serializable interface, which
returns a string
- * http://php.net/manual/en/class.serializable.php
- */
+ * NB: object classes should implement a static ::serialize() method, which
returns a
+ * JSON-encoded string of the multi-array compatible with the PHP
Serializable interface
+ *
+ * http://php.net/manual/en/class.serializable.php
+ */
public function serialize($object)
{
- $serialized = array();
- $class = get_class($object);
-
- // use reflection on i18n object to get property list from class constants
- if (class_exists($class . 'I18n'))
- {
- $reflect = new ReflectionClass($class . 'I18n');
- $i18nMethods = $reflect->getConstants();
-
- // these constants cannot be accessed by __get()
- unset($i18nMethods['DATABASE_NAME']);
- unset($i18nMethods['TABLE_NAME']);
-
- $i18nMethods = array_map(array('sfInflector', 'camelize'),
array_map('strtolower', array_keys($i18nMethods)));
-
- foreach ($i18nMethods as $method)
- {
- // TODO: implement i18n fallback / default culture
- // NB: 'id' and 'culture' are default (ie. non-i18n) fields
- $value = call_user_func(array($object, 'get' . $method));
- if (!is_null($value))
- {
- $serialized[lcfirst($method)] = $value;
- }
- }
- }
-
- // general serializations
- $serialized['className'] = $class; // NB: not required with ES; use type
instead
-
- // class-specific serializations
- switch ($class)
+ // take an object and return an associative multidimensional array of
properties
+ if (class_exists(get_class($object) . 'Mapping'))
{
- case 'QubitInformationObject':
- $serialized['slug'] = $object->slug;
- $serialized['parentId'] = $object->parentId;
- $serialized['parent'] = $object->parent->slug;
-
- $root = $object->getCollectionRoot();
- $serialized['collectionRoot']['id'] = $root->id;
- $serialized['collectionRoot']['slug'] = $root->slug;
- $serialized['collectionRoot']['title'] = $root->title;
-
- $serialized['referenceCode'] = $object->referenceCode;
- $serialized['identifier'] = $object->getIdentifier();
- $serialized['creator'] = $object->getCreatorsNameString();
- $serialized['creatorHistory'] = $object->getCreatorsHistoryString();
- $serialized['publicationStatusId'] =
$object->getPublicationStatus()->status->id;
-
- if ($level = $object->getLevelOfDescription())
- {
- $serialized['levelOfDescription'] = $level->getName();
- }
-
- // embed repository information
- if ($repository = $object->getRepository(array('inherit' => true)))
- {
- $serialized['repository']['id'] = $repository->id;
- $serialized['repository']['slug'] = $repository->slug;
- $serialized['repository']['name'] =
$repository->getAuthorizedFormOfName();
- }
-
- // embed digital object information
- if ($digitalObject = $object->getDigitalObject())
- {
- $serialized['digitalObject']['mediaTypeId'] =
$digitalObject->mediaTypeId;
-
- if ($digitalObject->thumbnail)
- {
- $serialized['digitalObject']['thumbnail_FullPath'] =
$digitalObject->thumbnail->getFullPath();
- }
- }
-
- // embed dates as an array
- foreach ($object->getDates() as $date)
- {
- $save_date = array(
- 'id' => $date->id,
- 'rendered' =>
Qubit::renderDateStartEnd($date->getDate(array('cultureFallback' => true)),
$date->startDate, $date->endDate),
- 'type' => $date->getType(array('cultureFallback' =>
true))->__toString()
- );
-
- if (isset($date->actor))
- {
- $save_date['actor'] = $date->actor->__toString();
- }
- $serialized['dates'][] = $save_date;
- }
-
- foreach ($object->getNotes() as $note)
- {
- $serialized['notes'][] = $note->getContent();
- }
-
- foreach ($object->getNameAccessPoints() as $name)
- {
- $serialized['names'][] = $name->object->getAuthorizedFormOfName();
- }
-
- foreach ($object->getSubjectAccessPoints() as $subject)
- {
- $term = $subject->getTerm();
- $serialized['subjects'][] = $term->getName();
-
- if ($term->otherNames)
- {
- foreach ($term->otherNames as $otherName)
- {
- $serialized['subjects'][] = $otherName->getName();
- }
- }
- }
-
- foreach ($object->getPlaceAccessPoints() as $place)
- {
- $term = $place->getTerm();
- $serialized['places'][] = $term->getName();
-
- if ($term->otherNames)
- {
- foreach ($term->otherNames as $otherName)
- {
- $serialized['places'][] = $otherName->getName();
- }
- }
- }
-
- break;
-
- case 'QubitActor':
-
- // Add other forms of name
- $criteria = new Criteria;
- $criteria->addJoin(QubitOtherNameI18n::ID, QubitOtherName::ID);
- $criteria->add(QubitOtherName::OBJECT_ID, $object->id);
-
- $otherNames = QubitOtherNameI18n::get($criteria);
-
- foreach ($otherNames as $otherName)
- {
- $serialized['otherNames'][] = $otherName->name;
- }
-
- break;
+ $serialized = call_user_func_array(array(get_class($object) . 'Mapping',
'serialize'), array($object));
}
+ // TODO: trim empty/null/blank elements in the array
return $serialized;
}
Modified:
trunk/plugins/qtElasticSearchPlugin/modules/search/actions/indexAction.class.php
==============================================================================
---
trunk/plugins/qtElasticSearchPlugin/modules/search/actions/indexAction.class.php
Tue Feb 7 00:27:13 2012 (r10814)
+++
trunk/plugins/qtElasticSearchPlugin/modules/search/actions/indexAction.class.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -47,12 +47,12 @@
return;
}
-// TODO: not implemented yet
-// $query = $this->filterQuery($query);
+ $query = $this->filterQuery($query);
+ $query = $this->facetQuery($query);
try
{
- $hits =
QubitSearch::getInstance()->index->getType('QubitInformationObject')->search($query)->getResults();
+ $resultSet =
QubitSearch::getInstance()->index->getType('QubitInformationObject')->search($query);
}
catch (Exception $e)
{
@@ -61,21 +61,22 @@
return;
}
- if (!empty($hits))
+ if (0 < $resultSet->getTotalHits())
{
- $this->pager = new QubitArrayPager;
+ // mock up a QubitPager for partial template backward compatibility
+ $this->pager = new stdClass();
if ('print' != $request->getGetParameter('media'))
{
- $this->pager->setMaxPerPage($request->limit);
+ $this->pager->maxPerPage = $request->limit;
}
else
{
- $this->pager->setMaxPerPage(500); // force for print
+ $this->pager->maxPerPage = 500; // force for print
}
- $this->pager->hits = $hits;
- $this->pager->setPage($request->page);
+ $this->pager->resultSet = $resultSet;
+ $this->pager->page = $request->page ? $request->page : 1;
}
else if (empty($this->error))
{
@@ -98,18 +99,24 @@
return null;
}
-// $query = new Elastica_Query_Bool();
-// $query->addMust($queryParsed);
$query = new Elastica_Query($queryParsed);
- $query->setLimit(500);
+
+ // set paging for request
+ $query->setLimit($this->request->limit);
+
+ if (!empty($this->request->page))
+ {
+ $query->setFrom(($this->request->page - 1) * $this->request->limit);
+ }
return $query;
}
public function filterQuery($query)
{
+/*
// Limit search to current culture and info. objects
-
$query->addSubquery(QubitSearch::getInstance()->addTerm('QubitInformationObject',
'className'), true);
+//
$query->addSubquery(QubitSearch::getInstance()->addTerm('QubitInformationObject',
'className'), true);
$query->addSubquery(QubitSearch::getInstance()->addTerm($this->context->user->getCulture(),
'culture'), true);
$query = QubitAcl::searchFilterByRepository($query, 'read');
@@ -122,6 +129,22 @@
$this->title .= $this->context->i18n->__(' in %1%', array('%1%' =>
$this->getRoute()->resource->authorizedFormOfName));
}
+// $query = new Elastica_Query_Bool();
+// $query->addMust($queryParsed);
+
+*/
+ return $query;
+ }
+
+ public function facetQuery($query)
+ {
+/*
+ // TODO: add facets
+ $facet = new Elastica_Facet_Terms('en.title');
+ $facet->setField('en.title');
+ $facet->setSize(10);
+ // $query->addFacet($facet);
+*/
return $query;
}
}
Modified:
trunk/plugins/qtElasticSearchPlugin/modules/search/templates/_searchResults.php
==============================================================================
---
trunk/plugins/qtElasticSearchPlugin/modules/search/templates/_searchResults.php
Tue Feb 7 00:27:13 2012 (r10814)
+++
trunk/plugins/qtElasticSearchPlugin/modules/search/templates/_searchResults.php
Tue Feb 7 13:38:57 2012 (r10815)
@@ -1,26 +1,27 @@
<div id="search-stats">
- <?php if (0 < $pager->getNbResults()): ?>
+ <?php if (0 < $pager->resultSet->getTotalHits()): ?>
<?php echo __('Showing results %1% to %2% of %3% (%4% seconds)', array(
- '%1%' => $pager->getFirstIndice(),
- '%2%' => $pager->getLastIndice(),
- '%3%' => $pager->getNbResults(),
+ '%1%' => (1 + ($pager->page - 1) * $pager->maxPerPage),
+ '%2%' => min($pager->maxPerPage * $pager->page,
$pager->resultSet->getTotalHits()),
+ '%3%' => $pager->resultSet->getTotalHits(),
'%4%' => $timer->elapsed()
)) ?>
<?php else: ?>
<?php echo __('No results') ?>
<?php endif; ?>
- <?php if ('print' == $sf_request->getGetParameter('media') &&
$pager->haveToPaginate()): ?>
+ <?php if ('print' == $sf_request->getGetParameter('media') &&
$pager->resultSet->getTotalHits() > $pager->maxPerPage): ?>
<?php echo __('(Only showing first %1% results for performance reasons)',
array('%1%' => $pager->getMaxPerPage())) ?>
<?php endif; ?>
</div>
<div class="section">
- <?php foreach ($pager->getResults() as $hit): ?>
+ <?php foreach ($pager->resultSet->getResults() as $hit): ?>
<?php $doc = $hit->getData(); ?>
+
<div class="clearfix search-results <?php echo 0 == @++$row % 2 ? 'even' :
'odd' ?>">
- <?php if (isset($doc['digitalObject'])): ?>
+ <?php if (isset($doc['digitalObject'])):/* ?>
<?php if ($doc['digitalObject']['mediaTypeId']): ?>
<?php echo
link_to(image_tag(QubitDigitalObject::getGenericRepresentation($doc['digitalObject']['mediaTypeId'],
QubitTerm::THUMBNAIL_ID)->getFullPath(), array('alt' => $doc['title'])),
array('slug' => $doc['slug'], 'module' => 'informationobject')) ?>
<?php elseif (QubitTerm::AUDIO_ID ==
$doc['digitalObject']['mediaTypeId']): ?>
@@ -28,13 +29,13 @@
<?php elseif (null !== $doc['digitalObject']['thumbnail_FullPath']): ?>
<?php echo
link_to(image_tag(public_path($doc['digitalObject']['thumbnail_FullPath']),
array('alt' => $doc['title'])), array('slug' => $doc['slug'], 'module' =>
'informationobject')) ?>
<?php endif;?>
- <?php endif; ?>
+ <?php */ endif; ?>
- <h2><?php echo link_to($doc['title'], array('slug' => $doc['slug'],
'module' => 'informationobject')) ?><?php if
(QubitTerm::PUBLICATION_STATUS_DRAFT_ID == $doc['publicationStatusId']): ?>
<span class="publicationStatus">draft</span><?php endif; ?></h2>
+ <h2><?php echo link_to($doc['en']['title'], array('slug' =>
$doc['slug'], 'module' => 'informationobject')) ?><?php if
(QubitTerm::PUBLICATION_STATUS_DRAFT_ID == $doc['publicationStatusId']): ?>
<span class="publicationStatus">draft</span><?php endif; ?></h2>
- <?php if ($doc['scopeAndContent']): ?>
+ <?php if ($doc['en']['scopeAndContent']): ?>
<div class="field">
- <?php echo highlight_text(truncate_text($doc['scopeAndContent'],
256), $sf_request->query) ?>
+ <?php echo
highlight_text(truncate_text($doc['en']['scopeAndContent'], 256),
$sf_request->query) ?>
</div>
<?php endif; ?>
@@ -67,12 +68,12 @@
<?php echo render_show(__('Level of description'),
render_value($doc['levelOfDescription'])) ?>
<?php endif; ?>
- <?php if (sfConfig::get('app_multi_repository') && null !=
isset($doc['repositoryId'])): ?>
- <?php echo render_show(__('Repository'),
link_to($doc['repositoryName'], array('slug' => $doc['repositorySlug'],
'module' => 'repository'))) ?>
+ <?php if (sfConfig::get('app_multi_repository') && null !=
isset($doc['repository']['id'])): ?>
+ <?php echo render_show(__('Repository'),
link_to($doc['repository']['en']['name'], array('slug' =>
$doc['repository']['slug'], 'module' => 'repository'))) ?>
<?php endif; ?>
- <?php if (isset($doc['collectionRootId']) && $doc['collectionRootId']
!== $doc['id']): ?>
- <?php echo render_show(__('Part of'),
link_to($doc['collectionRootTitle'], array('slug' =>
$doc['collectionRootSlug'], 'module' => 'informationobject'))) ?>
+ <?php if (isset($doc['collectionRoot']['id']) &&
$doc['collectionRoot']['id'] !== $hit->getId()): ?>
+ <?php echo render_show(__('Part of'),
link_to($doc['collectionRoot']['title'], array('slug' =>
$doc['collectionRoot']['slug'], 'module' => 'informationobject'))) ?>
<?php endif; ?>
</div>
@@ -80,5 +81,66 @@
</div>
<?php if ('print' != $sf_request->getGetParameter('media')): ?>
- <?php echo get_partial('default/pager', array('pager' => $pager)) ?>
+
+<div class="result-count">
+ <?php if (0 < $pager->resultSet->getTotalHits()): ?>
+ <?php echo __('Results %1% to %2% of %3%',
+ array('%1%' => (1 + ($pager->page - 1) * $pager->maxPerPage),
+ '%2%' => min($pager->maxPerPage * $pager->page,
$pager->resultSet->getTotalHits()),
+ '%3%' => $pager->resultSet->getTotalHits())) ?>
+ <?php else: ?>
+ <?php echo __('No results') ?>
+ <?php endif; ?>
+</div>
+
+<?php if ($pager->resultSet->getTotalHits() > $pager->maxPerPage): ?>
+ <div class="pager section">
+
+ <h2 class="element-invisible"><?php echo __('Pages') ?></h2>
+
+ <div class="content">
+
+ <?php if (1 < $pager->page): ?>
+ <?php echo link_to(__('Previous'), array('page' => $pager->page - 1) +
$sf_request->getParameterHolder()->getAll(), array('rel' => 'prev', 'title' =>
__('Go to previous page'))) ?>
+ <?php endif; ?>
+
+ <ol>
+ <?php /*foreach ($pager->getLinks(10) as $page): ?>
+ <?php if ($pager->page == $page): ?>
+ <li class="active"><?php echo $page ?></li>
+ <?php else: ?>
+ <li><?php echo link_to($page, array('page' => $page) +
$sf_request->getParameterHolder()->getAll(), array('title' => __('Go to page
%1%', array('%1%' => $page)))) ?></li>
+ <?php endif; ?>
+ <?php endforeach */?>
+ </ol>
+
+ <?php if ($pager->resultSet->getTotalHits() > ($pager->maxPerPage *
$pager->page)): ?>
+ <?php echo link_to(__('Next'), array('page' => $pager->page + 1) +
$sf_request->getParameterHolder()->getAll(), array('rel' => 'next', 'title' =>
__('Go to next page'))) ?>
+ <?php endif; ?>
+
+ </div>
+
+ </div>
+ <?php endif; ?>
+
+<?php if (10 < $pager->resultSet->getTotalHits()): ?>
+ <div class="itemsPerPage section">
+ <?php ob_start() ?>
+ <ol>
+ <?php foreach (array(10, 50, 100, 500) as $limit): ?>
+
+ <?php if ($sf_request->limit == $limit): ?>
+ <li class="active"><?php echo $limit ?></li>
+ <?php else: ?>
+ <li><?php echo link_to($limit, array('limit' => $limit) +
$sf_request->getParameterHolder()->getAll(), array('title' => __('%1% results
per page', array('%1%' => $limit)))) ?></li>
+ <?php endif; ?>
+
+ <?php if ($pager->resultSet->getTotalHits() < $limit) break; ?>
+
+ <?php endforeach; ?>
+ </ol>
+ <?php echo __('%1% results per page', array('%1%' => ob_get_clean())) ?>
+ </div>
+ <?php endif; ?>
+
<?php endif; ?>
--
You received this message because you are subscribed to the Google Groups
"Qubit Toolkit Commits" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/qubit-commits?hl=en.