Skip to content

Commit

Permalink
start implementing elasticsearch indexing
Browse files Browse the repository at this point in the history
  • Loading branch information
dpfaffenbauer committed Sep 9, 2016
1 parent 8264825 commit df432b8
Show file tree
Hide file tree
Showing 17 changed files with 698 additions and 37 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
}],
"require": {
"pimcore/installer-plugin": ">=1",
"florianv/swap": "^2.4"
"florianv/swap": "^2.4",
"elasticsearch/elasticsearch": "~2.0"
}
}
2 changes: 1 addition & 1 deletion controllers/Admin/IndexesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function getAction()
$group = Index::getById($id);

if ($group instanceof Index) {
$data = get_object_vars($group);
$data = $group->getObjectVars();
$data['classId'] = \CoreShop\Model\Product::classId();

$this->_helper->json(array('success' => true, 'data' => $data));
Expand Down
2 changes: 1 addition & 1 deletion lib/CoreShop/IndexService.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class IndexService
*
* @var array
*/
public static $types = array('mysql');
public static $types = array('mysql', 'elasticsearch');

/**
* IndexService.
Expand Down
12 changes: 7 additions & 5 deletions lib/CoreShop/IndexService/AbstractWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ public function __construct(Index $index)
* prepares Data for index.
*
* @param Product $object
* @param boolean $convertArrayToString
*
* @return array("data", "relation")
*
* @throws \CoreShop\Exception\UnsupportedException
*/
protected function prepareData(Product $object)
protected function prepareData(Product $object, $convertArrayToString = true)
{
$a = \Pimcore::inAdmin();
$b = AbstractObject::doGetInheritedValues();
Expand All @@ -82,6 +83,7 @@ protected function prepareData(Product $object)
}

ksort($categoryIds);
$categoryIds = array_values($categoryIds);

$virtualProductId = $object->getId();
$virtualProductActive = $object->getEnabled();
Expand All @@ -103,10 +105,10 @@ protected function prepareData(Product $object)
'o_virtualProductId' => $virtualProductId,
'o_virtualProductActive' => $virtualProductActive === null ? false : $virtualProductActive,
'o_type' => $object->getType(),
'categoryIds' => ','.implode(',', $categoryIds).',',
'parentCategoryIds' => ','.implode(',', $parentCategoryIds).',',
'categoryIds' => $convertArrayToString ? ','.implode(',', $categoryIds).',' : $categoryIds,
'parentCategoryIds' => $convertArrayToString ? ','.implode(',', $parentCategoryIds).',' : $parentCategoryIds,
'active' => $object->getEnabled() === null ? false : $object->getEnabled(),
'shops' => ','.@implode(',', $object->getShops()).','
'shops' => $convertArrayToString ? ','.@implode(',', $object->getShops()).',' : $object->getShops()
);

$relationData = array();
Expand Down Expand Up @@ -171,7 +173,7 @@ protected function prepareData(Product $object)
$data[$column->getName()] = $value;
}

if (is_array($data[$column->getName()])) {
if (is_array($data[$column->getName()]) && $convertArrayToString) {
$data[$column->getName()] = ','.implode($data[$column->getName()], ',').',';
}
} catch (\Exception $e) {
Expand Down
305 changes: 305 additions & 0 deletions lib/CoreShop/IndexService/Elasticsearch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
<?php
/**
* CoreShop.
*
* LICENSE
*
* This source file is subject to the GNU General Public License version 3 (GPLv3)
* For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
* files that are distributed with this source code.
*
* @copyright Copyright (c) 2015-2016 Dominik Pfaffenbauer (https://www.pfaffenbauer.at)
* @license https://www.coreshop.org/license GNU General Public License version 3 (GPLv3)
*/

namespace CoreShop\IndexService;

use CoreShop\Model\Index;
use CoreShop\Model\Product;
use CoreShop\Model\Index\Config\Column\Mysql as Column;
use Elasticsearch\Client;
use Elasticsearch\ClientBuilder;
use Pimcore\Db;

/**
* Class Elasticsearch
* @package CoreShop\IndexService
*/
class Elasticsearch extends AbstractWorker
{
/**
* Database.
*
* @var \Zend_Db_Adapter_Abstract
*/
protected $db;

/**
* @var Index\Config\Elasticsearch
*/
protected $config;

/**
* @var Client
*/
protected $client;

/**
* Mysql constructor.
*
* @param Index $index
*/
public function __construct(Index $index)
{
parent::__construct($index);

$this->db = Db::get();
$this->config = $index->getConfig();
}

/**
* @return Client
*/
protected function getElasticsearchClient() {
if(is_null($this->client)) {
$builder = ClientBuilder::create();
$builder->setHosts(explode(",", $this->config->getHosts()));
$this->client = $builder->build();
}

return $this->client;
}

protected function createTableStructure() {
$this->db->query('CREATE TABLE IF NOT EXISTS `'.$this->getTablename()."` (
`o_id` int(11) NOT NULL default '0',
`o_classId` int(11) NOT NULL,
`o_type` varchar(20) NOT NULL,
`categoryIds` varchar(255) NOT NULL,
`parentCategoryIds` varchar(255) NOT NULL,
`active` TINYINT(1) NOT NULL,
`shops` varchar(255) NOT NULL,
PRIMARY KEY (`o_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
}

/**
* Create Database index table.
*/
public function createOrUpdateIndexStructures()
{
try {
$params = ['index' => $this->getIndex()->getName()];

$this->getElasticsearchClient()->indices()->delete($params);
}
catch(\Exception $ex) {

}

$result = $this->getElasticsearchClient()->indices()->exists(['index' => $this->getIndex()->getName()]);
if(!$result) {
$result = $this->getElasticsearchClient()->indices()->create(['index' => $this->getIndex()->getName(), 'body' => ['settings' => ["number_of_shards" => 5, "number_of_replicas" => 0]]]); //TODO: add to ui
\Logger::info('Creating new Index. Name: ' . $this->getIndex()->getName());
if(!$result['acknowledged']) {
throw new \Exception("Index creation failed. IndexName: " . $this->getIndex()->getName());
}

//index didn't exist -> reset index queue to make sure all products get reindexed
//$this->resetIndexingQueue();
}


$properties = [];

$systemColumns = $this->getSystemAttributes();
$columnConfig = $this->getColumnsConfiguration();

foreach($systemColumns as $column => $type) {
$properties[$column] = array(
'type' => $type
);
}

foreach ($columnConfig as $column) {
$properties[$column->getName()] = array(
'type' => $column->getColumnType()
);
}

$params = [
'index' => $this->getIndex()->getName(),
'type' => "coreshop",
'body' => [
'coreshop' => [
'properties' => $properties
]
]
];

try {
$result = $this->getElasticsearchClient()->indices()->putMapping($params);
} catch(\Exception $e) {
\Logger::info($e->getMessage());

}
}

/**
* deletes necessary index structuers (like database tables).
*
* @return mixed
*/
public function deleteIndexStructures()
{
$this->getElasticsearchClient()->indices()->delete([
'index' => $this->getIndex()->getName()
]);
}

/**
* Delete Product from index.
*
* @param Product $object
*/
public function deleteFromIndex(Product $object)
{
$params = [
'index' => $this->getIndex()->getName(),
'type' => 'coreshop',
'id' => $object->getId()
];

$this->getElasticsearchClient()->delete($params);
}

/**
* Update or create product in index.
*
* @param Product $object
*/
public function updateIndex(Product $object)
{
if ($object->getDoIndex()) {
$preparedData = $this->prepareData($object, false);

try {
$params = [
'index' => $this->getIndex()->getName(),
'type' => 'coreshop',
'id' => $object->getId(),
'body' => $preparedData['data']
];


$this->getElasticsearchClient()->index($params);
} catch (\Exception $e) {
\Logger::warn('Error during updating index table: '.$e);
}

/*try {
$this->db->delete($this->getRelationTablename(), 'src = '.$this->db->quote($object->getId()));
foreach ($preparedData['relation'] as $rd) {
$this->db->insert($this->getRelationTablename(), $rd);
}
} catch (\Exception $e) {
\Logger::warn('Error during updating index relation table: '.$e->getMessage(), $e);
}*/
} else {
\Logger::info("Don't adding product ".$object->getId().' to index.');

$this->deleteFromIndex($object);
}
}

/**
* Renders a condition to MySql
*
* @param Condition $condition
* @return string
* @throws \Exception
*/
public function renderCondition(Condition $condition) {
switch($condition->getType()) {

case "in":
$inValues = [];

foreach ($condition->getValues() as $c => $value) {
$inValues[] = Db::get()->quote($value);
}

$rendered = 'TRIM(`'.$condition->getFieldName().'`) IN ('.implode(',', $inValues).')';
break;

case "match":
$rendered = 'TRIM(`'.$condition->getFieldName().'`) = '.Db::get()->quote($condition->getValues());
break;

case "range":
$values = $condition->getValues();

$rendered = 'TRIM(`'.$condition->getFieldName().'`) >= '.$values['from'].' AND TRIM(`'.$condition->getFieldName().'`) <= '.$values['to'];
break;

case "concat":

$values = $condition->getValues();
$conditions = [];

foreach ($values['conditions'] as $cond) {
$conditions[] = $this->renderCondition($cond);
}

$rendered = implode($values['operator'], $conditions);


break;

default:
throw new \Exception($condition->getType() . " is not supported yet");
}

return $rendered;
}

/**
* Return Productlist.
*
* @return Product\Listing\Mysql
*/
public function getProductList()
{
return new Product\Listing\Mysql($this->getIndex());
}

/**
* get table name.
*
* @return string
*/
public function getTablename()
{
return 'coreshop_index_mysql_'.$this->getIndex()->getName();
}

/**
* get tablename for relations.
*
* @return string
*/
public function getRelationTablename()
{
return 'coreshop_index_mysql_relations_'.$this->getIndex()->getName();
}

/**
* Get System Attributes.
*
* @return array
*/
protected function getSystemAttributes()
{
return array('o_id' => "long", 'o_classId' => "string", 'o_type' => "string", 'categoryIds' => "long", 'parentCategoryIds' => "long", 'active' => "boolean", 'shops' => "long");
}
}
Loading

0 comments on commit df432b8

Please sign in to comment.