Creating a new Index and adding data in same step is working as seen in the docs.
But when i try to add data to an existing index I'm getting the following exception.
Ehann\RediSearch\Exception\FieldNotInSchemaException
The field is not a property in the index
Here is my code:
$adapter = new PredisAdapter();
$host = config('database.redis.default.host');
$port = config('database.redis.default.port');
$database = config('database.redis.default.database');
$password = config('database.redis.default.password');
$redis = $adapter->connect($host, $port, $database, $password);
$bookIndex = new Index($redis, 'books');
$bookIndex->add([
new TextField('title', 'Tale of Two Cities'),
new TextField('author', 'Charles Dickens'),
new NumericField('price', 9.99),
new NumericField('stock', 231),
]);
I've looked into the source code of redisearch-php
and it seems that there is no way this could work without always adding the fields with addTextField
or others. But this seem not very practical to me. I thought I had to add the fields once on index creation. Am I wrong?
The getFields
method (used in some methods like makeDocument
) gets the object vars from Index
but the field related object vars aren't in the instance if not added manually beforehand.
vendor/ethanhann/redisearch-php/src/Index.php
/**
* @return array
*/
protected function getFields(): array
{
$fields = [];
foreach (get_object_vars($this) as $field) {
if ($field instanceof FieldInterface) {
$fields[$field->getName()] = clone $field;
}
}
return $fields;
}
/**
* @param string $name
* @param float $weight
* @param bool $sortable
* @param bool $noindex
* @return IndexInterface
*/
public function addTextField(string $name, float $weight = 1.0, bool $sortable = false, bool $noindex = false): IndexInterface
{
$this->$name = (new TextField($name))->setSortable($sortable)->setNoindex($noindex)->setWeight($weight);
return $this;
}
/**
* @param string $name
* @param bool $sortable
* @param bool $noindex
* @return IndexInterface
*/
public function addNumericField(string $name, bool $sortable = false, bool $noindex = false): IndexInterface
{
$this->$name = (new NumericField($name))->setSortable($sortable)->setNoindex($noindex);
return $this;
}
/**
* @param string $name
* @return IndexInterface
*/
public function addGeoField(string $name, bool $noindex = false): IndexInterface
{
$this->$name = (new GeoField($name))->setNoindex($noindex);
return $this;
}
So, I played around with this today. I think you are correct. You have to redeclare the schema each time even if it already exists on redis. I can't see any functions that are used to pull the schema from redis which is kind of annyoying.
You can see the fields in the output from the $bookIndex->info() function but it doesn't set up the schema for you.
My solution to this problem would be to create a class that deals with setting up the index object so you don't have to keep doing it all the time. Something like this:
class book_index
{
private $index;
public function __construct($redis)
{
$this->index = new Index($redis);
$this->index->setIndexName("book");
$this->set_schema();
}
private function set_schema(){
$this->index->addTextField('title')
->addTextField('author')
->addNumericField('price')
->addNumericField('stock');
// attempt to create the index, I couldn't find any function to check if it already exists
// so catching the failure or using info() and catching the exception on that was the only way
try {
$this->index->create();
}catch (Ehann\RediSearch\Exceptions\RediSearchException $exception){
error_log("book index already exists.");
error_log( $exception->getMessage());
}
}
public function get_index(){
return $this->index;
}
public static function get($redis){
$obj = new self($redis);
return $obj->get_index();
}
}
Then your code would look like this:
include_once ("book_index.php"); // include the class file
$adapter = new PredisAdapter();
$host = config('database.redis.default.host');
$port = config('database.redis.default.port');
$database = config('database.redis.default.database');
$password = config('database.redis.default.password');
$redis = $adapter->connect($host, $port, $database, $password);
$bookIndex = book_index::get($redis);
$bookIndex->add([
new TextField('title', 'Tale of Two Cities'),
new TextField('author', 'Charles Dickens'),
new NumericField('price', 9.99),
new NumericField('stock', 231),
]);
You could move more functionality to the class but I just tried to keep it simple.