This class implements the ActiveRecord pattern for the fulltext search and data storage
elasticsearch.
For defining a record a subclass should at least implement the ActiveRecord::attributes method to define
attributes.
The primary key (the _id field in elasticsearch terms) is represented by getId() and setId().
The primary key is not part of the attributes.
The following is an example model called Customer:
php
class Customer extends \yii\elasticsearch\ActiveRecord
{
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
}
You may override ActiveRecord::index and ActiveRecord::type to define the index and type this record represents.
public function down() { $index = \gromver\platform\common\models\elasticsearch\ActiveDocument::index(); \yii\elasticsearch\ActiveRecord::getDb()->createCommand()->deleteIndex($index); //->deleteAllIndexes();// echo "Index {$index} are deleted successfully."; }
/** * @param $documentClass \gromver\platform\common\models\elasticsearch\ActiveDocument * @return int * @throws \yii\elasticsearch\Exception */ public function upload($documentClass) { $bulk = ''; /** @var \yii\db\ActiveRecord|string $modelClass */ $modelClass = $documentClass::model(); /** @var \gromver\platform\common\models\elasticsearch\ActiveDocument $document */ $document = new $documentClass(); $uploaded = 0; foreach ($modelClass::find()->each() as $model) { /** @var \yii\db\ActiveRecord $model */ $action = Json::encode(["index" => ["_id" => $model->getPrimaryKey(), "_type" => $documentClass::type(), "_index" => $documentClass::index()]]); $document->loadModel($model); $data = Json::encode($document->toArray()); $bulk .= $action . "\n" . $data . "\n"; $uploaded++; } $url = [$documentClass::index(), $documentClass::type(), '_bulk']; $response = ActiveRecord::getDb()->post($url, [], $bulk); $n = 0; $errors = []; foreach ($response['items'] as $item) { if (isset($item['index']['status']) && ($item['index']['status'] == 201 || $item['index']['status'] == 200)) { $n++; } else { $errors[] = $item['index']; } } if (!empty($errors) || isset($response['errors']) && $response['errors']) { throw new Exception(__METHOD__ . ' failed inserting ' . $modelClass . ' model records.', $errors); } return $n; }
/** * @inheritdoc */ public function beforeSave($insert) { if (!parent::beforeSave($insert)) { return false; } $this->refreshFromEmbedded(); return true; }
public function down() { if (!($index = Index::index())) { throw new Exception(Index::className() . '::index must be set.'); } ActiveRecord::getDb()->createCommand()->deleteIndex($index); //->deleteAllIndexes();// echo "Index {$index} are deleted successfully."; }
/** * @param $class \yii\db\ActiveRecord|string * @return int * @throws \yii\elasticsearch\Exception */ public function upload($class) { $bulk = ''; $index = Index::index(); $type = Index::type(); $timestamp = time(); /** @var \yii\base\Behavior[] */ $behaviors = (new $class())->behaviors; $query = $class::find(); array_walk($behaviors, function ($behavior) use($query) { if ($behavior instanceof TaggableBehavior) { $query->with('tags'); } }); foreach ($query->each() as $model) { /** @var \yii\db\ActiveRecord|\gromver\platform\core\interfaces\model\SearchableInterface|\gromver\platform\core\interfaces\model\ViewableInterface $model */ $action = Json::encode(["index" => ["_id" => $model->getPrimaryKey(), "_type" => $type, "_index" => $index]]); $indexModel = Index::findOne(['model_id' => $model->getPrimaryKey(), 'model_class' => $model->className()]) or $indexModel = new Index(); $indexModel->model_id = $model->getPrimaryKey(); $indexModel->model_class = $model->className(); $indexModel->title = $model->getSearchTitle(); $indexModel->content = $model->getSearchContent(); $indexModel->tags = $model->getSearchTags(); $indexModel->url_backend = $model->getBackendViewLink(); $indexModel->url_frontend = $model->getFrontendViewLink(); $indexModel->updated_at = $timestamp; ModuleEvent::trigger(Module::EVENT_BEFORE_CREATE_INDEX . $model->className(), new ElasticIndexEvent(['model' => $model, 'index' => $indexModel, 'sender' => $this->module])); if ($indexModel->validate()) { $bulk .= $action . "\n" . Json::encode($indexModel->toArray()) . "\n"; } } $url = [$index, $type, '_bulk']; $response = ActiveRecord::getDb()->post($url, [], $bulk); $n = 0; $errors = []; foreach ($response['items'] as $item) { if (isset($item['index']['status']) && ($item['index']['status'] == 201 || $item['index']['status'] == 200)) { $n++; } else { $errors[] = $item['index']; } } if (!empty($errors) || isset($response['errors']) && $response['errors']) { throw new Exception(__METHOD__ . ' failed inserting ' . $class . ' model records.', $errors); } return $n; }
/** * @inheritdoc * * @param ActiveRecord $record the record to be populated. In most cases this will be an instance * created by [[instantiate()]] beforehand. * @param array $row attribute values (name => value) */ public static function populateRecord($record, $row) { $attributes = []; if (isset($row['_source'])) { $attributes = $row['_source']; } if (isset($row['fields'])) { // reset fields in case it is scalar value $arrayAttributes = $record->arrayAttributes(); foreach ($row['fields'] as $key => $value) { if (!isset($arrayAttributes[$key]) && count($value) == 1) { $row['fields'][$key] = reset($value); } } $attributes = array_merge($attributes, $row['fields']); } parent::populateRecord($record, $attributes); $pk = static::primaryKey()[0]; //TODO should always set ID in case of fields are not returned if ($pk === '_id') { $record->_id = $row['_id']; } $record->_highlight = isset($row['highlight']) ? $row['highlight'] : null; $record->_score = isset($row['_score']) ? $row['_score'] : null; $record->_version = isset($row['_version']) ? $row['_version'] : null; // TODO version should always be available... }
public function behaviors() { return array_merge(parent::behaviors(), [TimestampBehavior::className()]); }