Description
This package allows you to interact with Elasticsearch as you interact with Eloquent models in Laravel.
Elasticsearch Eloquent alternatives and similar libraries
Based on the "Database" category.
Alternatively, view Elasticsearch Eloquent alternatives based on common mentions on social networks and blogs.
-
Medoo
The lightweight PHP database framework to accelerate the development. -
Doctrine Extensions
Doctrine2 behavioral extensions, Translatable, Sluggable, Tree-NestedSet, Timestampable, Loggable, Sortable -
ProxyManager
🎩✨🌈 OOP Proxy wrappers/utilities - generates and manages proxies of your objects -
Eloquent
[READ ONLY] Subtree split of the Illuminate Database component (see laravel/framework) -
Baum
Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM. -
RedBean
ORM layer that creates models, config and database on the fly -
Idiorm
A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5. -
Propel
Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP -
Doctrine2 Behaviors
Doctrine2 behavior traits that help handling Blameable, Loggable, Sluggable, SoftDeletable, Uuidable, Timestampable, Translatable, Tree behavior -
Spot2
Spot v2.x DataMapper built on top of Doctrine's Database Abstraction Layer -
Aura.SqlQuery
Independent query builders for MySQL, PostgreSQL, SQLite, and Microsoft SQL Server. -
Atlas.Orm
A data mapper implementation for your persistence model in PHP. -
ATK Data
Data Access PHP Framework for SQL & high-latency databases -
Cake ORM
[READ-ONLY] A flexible, lightweight and powerful Object-Relational Mapper for PHP, implemented using the DataMapper pattern. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp -
Simple MySQLi Class
:gem: Simple MySQLi Abstraction Layer + Doctrine/DBAL support -
LDBA
High-performance, low-memory-footprint, single-file embedded database for key/value storage -
PHP library access Firebase RESTful API
A working Firebase http client -
Symfony-RDM
Helps with the use of domain driven design & rich domain model in symfony / doctrine applications. -
LazyRecord
The fast ORM for PHP. (This is a history repo, please visit maghead/maghead)
Static code analysis for 29 languages.
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of Elasticsearch Eloquent or a related project?
README
Persimmon / Elasticsearch Eloquent
This package allows you to interact with Elasticsearch as you interact with Eloquent models in Laravel.
Feel free to improve the project.
Install
Via Composer
$ composer require isswp101/elasticsearch-eloquent
Usage
Configure dependencies
Warning! First of all you should create a base model and inherit from it their models.
use Elasticsearch\Client;
use Isswp101\Persimmon\DAL\ElasticsearchDAL;
use Isswp101\Persimmon\ElasticsearchModel as Model;
use Isswp101\Persimmon\Event\EventEmitter;
class ElasticsearchModel extends Model
{
public function __construct(array $attributes = [])
{
$dal = new ElasticsearchDAL($this, app(Client::class), app(EventEmitter::class));
parent::__construct($dal, $attributes);
}
public static function createInstance()
{
return new static();
}
}
In this example we use Laravel IoC Container to resolve Elasticsearch\Client
dependency as app(Client::class)
.
Create a new model
You must override static variables index
and type
to determine the document path.
class Product extends ElasticsearchModel
{
protected static $_index = 'test';
protected static $_type = 'test';
public $name;
public $price = 0;
}
Here name
and price
are fields which will be stored in Elasticsearch.
Warning! Don't use field names starting with underscore
$_*
, for example$_name
.
Use the static create()
method to create document in Elasticsearch:
$product = Product::create(['id' => 3, 'name' => 'Product 3', 'price' => 30]);
Save the model
$product = new Product();
$product->id = 1;
$product->name = 'Product 1';
$product->price = 20;
$product->save();
Use save()
method to store model data in Elasticsearch. Let's see how this looks in Elasticsearch:
{
"_index": "test",
"_type": "test",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "Product 1",
"price": 10,
"id": 1,
"user_id": null,
"created_at": "2016-06-03 08:11:08",
"updated_at": "2016-06-03 08:11:08"
}
}
Fields created_at
and updated_at
were created automatically. The user_id
field is persistent field to store user id.
Find existing model
$product = Product::find(1);
If you have big data in Elasticsearch you can specify certain fields to retrieve:
$product = Product::find(1, ['name']);
In this case the price
field equals 0
because it's populated as the default value that you specified in the model.
There are the following methods:
findOrFail()
returnsModelNotFoundException
exception if no result found.findOrNew()
returns a new model if no result found.
Model cache
There is a smart model cache when you use methods like find()
, findOrFail()
and so on.
$product = Product::find(1, ['name']); // will be retrieved from the elasticsearch
$product = Product::find(1, ['name']); // will be retrieved from the cache
$product = Product::find(1, ['price']); // elasticsearch
$product = Product::find(1, ['price']); // cache
$product = Product::find(1, ['name']); // cache
$product = Product::findOrFail(1); // elasticsearch
$product = Product::find(1); // cache
$product = Product::find(1, ['name']); // cache
$product = Product::find(1, ['price']); // cache
Partial update
You can use partial update to update specific fields quickly.
$product = Product::find(1, ['name']);
$product->name = 'Product 3';
$product->save('name');
Delete models
$product = Product::find(1);
$product->delete();
You can use the static method:
Product::destroy(1);
Model events
Out of the box you are provided with a simple implementation of events.
You can override the following methods to define events:
saving()
is called before saving, updating, creating the modelsaved()
is called after saving, updating, creating the modeldeleting()
is called before deleting the modeldeleted()
is called after deleting the model
For example:
class Product extends ElasticsearchModel
{
public static $_index = 'test';
public static $_type = 'test';
public $name;
public $price = 0;
protected function saving()
{
if ($this->price <= 0) {
return false;
}
return true;
}
protected function deleting()
{
if (!$this->canDelete()) {
throw new LogicException('No permissions to delete the model');
}
return true;
}
}
Basic search
There are helpers to search documents:
The first($query)
method returns the first document according to the query or null
.
$product = Product::first($query);
The firstOrFail($query)
method returns ModelNotFoundException
exception if first($query)
returns null
.
$product = Product::firstOrFail($query);
The search($query)
method returns documents (default 50 items) according to the query.
$products = Product::search($query);
The map($query, callable $callback)
method returns all documents (default 50 items per request) according to the query.
$total = Product::map([], function (Product $product) {
// ...
});
The all($query)
method returns all documents according to the query.
$products = Product::all($query);
If $query
is not passed the query will be as match_all
query.
Query Builder
use Isswp101\Persimmon\QueryBuilder\QueryBuilder;
$query = new QueryBuilder();
Simple usage:
$query = new QueryBuilder(['query' => ['match' => ['name' => 'Product']]]);
$products = Product::search($query);
The match
query:
$query = new QueryBuilder();
$query->match('name', 'Product');
$products = Product::search($query);
The range
query:
$query = new QueryBuilder();
$query->betweenOrEquals('price', 20, 30)->greaterThan('price', 15);
$products = Product::search($query);
Filters
Feel free to add your own filters.
The TermFilter
filter:
$query = new QueryBuilder();
$query->filter(new TermFilter('name', '2'));
$products = Product::search($query);
The IdsFilter
filter:
$query = new QueryBuilder();
$query->filter(new IdsFilter([1, 3]));
$products = Product::search($query);
The RangeOrExistFilter
filter:
$query = new QueryBuilder();
$query->filter(new RangeOrExistFilter('price', ['gte' => 20]));
$products = Product::search($query);
Aggregations
Feel free to add your own aggregations.
$query = new QueryBuilder();
$query->aggregation(new TermsAggregation('name'));
$products = Product::search($query);
$buckets = $products->getAggregation('name');
// Usage: $buckets[0]->getKey() and $buckets[0]->getCount()
Parent-Child Relationship
The parent-child relationship is similar in nature to the nested model: both allow you to associate one entity with another. The difference is that, with nested objects, all entities live within the same document while, with parent-child, the parent and children are completely separate documents.
Let's create two models:
PurchaseOrder
has manyPurchaseOrderLine
modelsPurchaseOrderLine
belongs toPurchaseOrder
model
class PurchaseOrder extends ElasticsearchModel
{
protected static $_index = 'test_parent_child_rel';
protected static $_type = 'orders';
public $name;
public function lines()
{
return $this->hasMany(PurchaseOrderLine::class);
}
}
class PurchaseOrderLine extends ElasticsearchModel
{
protected static $_index = 'test_parent_child_rel';
protected static $_type = 'lines';
protected static $_parentType = 'orders';
public $name;
public function po()
{
return $this->belongsTo(PurchaseOrder::class);
}
}
To save()
models you can use the following code:
$po = new PurchaseOrder(['id' => 1, 'name' => 'PO1']);
$line = new PurchaseOrderLine(['id' => 1, 'name' => 'Line1']);
$po->save();
$po->lines()->save($line);
You can use the associate()
method to save models:
$po = new PurchaseOrder(['id' => 1, 'name' => 'PO1']);
$line = new PurchaseOrderLine(['id' => 1, 'name' => 'Line1']);
$po->save();
$line->po()->associate($po);
$line->save();
To get parent you can use the following code:
$line = PurchaseOrderLine::findWithParentId(1, 1);
$po = $line->po()->get();
To get children you can use the following code:
$po = PurchaseOrder::findOrFail(1);
$line = $po->lines()->find(1); // by id
$lines = $po->lines()->get(); // all children
Inner hits
The parent/child and nested features allow the return of documents that have matches in a different scope. In the parent/child case, parent document are returned based on matches in child documents or child document are returned based on matches in parent documents. In the nested case, documents are returned based on matches in nested inner objects.
You can get parent model using only one request with InnerHitsFilter
filter:
$query = new QueryBuilder();
$query->filter(new InnerHitsFilter(PurchaseOrderLine::getParentType()));
$line = PurchaseOrderLine::search($query)->first();
$po = $line->po()->get(); // will be retrieved from inner_hits cache
Logging and data access layer events
To debug all elasticsearch queries to search you can use own DALEmitter
class:
use Isswp101\Persimmon\DAL\DALEvents;
use Isswp101\Persimmon\Event\EventEmitter;
class DALEmitter extends EventEmitter
{
public function __construct()
{
$this->on(DALEvents::BEFORE_SEARCH, function (array $params) {
Log::debug('Elasticsearch query', $params);
});
}
}
And configure it in your service provider:
use Elasticsearch\Client;
use Isswp101\Persimmon\DAL\ElasticsearchDAL;
use Isswp101\Persimmon\ElasticsearchModel as Model;
use Isswp101\Persimmon\Test\Models\Events\DALEmitter;
class ElasticsearchModel extends Model
{
public function __construct(array $attributes = [])
{
$dal = new ElasticsearchDAL($this, app(Client::class), app(DALEmitter::class));
parent::__construct($dal, $attributes);
}
// ...
}
There are the following events:
DALEvents::BEFORE_SEARCH
is triggered before any search.DALEvents::AFTER_SEARCH
is triggered after any search.
TO BE CONTINUED...
@TODO:
- Add documentation about filters
Change log
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
Testing
$ composer test
Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details.
Security
If you discover any security related issues, please email [email protected] instead of using the issue tracker.
Credits
- Sergey Sorokin
- [All Contributors][link-contributors]
License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
*Note that all licence references and agreements mentioned in the Elasticsearch Eloquent README section above
are relevant to that project's source code only.