I have written a ZF2 elastic search module but it is closed source. It enables modules to put searchable data into ES. I can't post the code, but I can explain how it works.
The central module is Search
. The search contains two main interfaces: Search\Repository\ItemRepositoryInterface
and Search\Service\ItemServiceInterface
. The repository is for searching items, the service for storing/removing items.
interface ItemRepositoryInterface
{
public function search($query, $limit = 10);
}
interface ItemServiceInterface
{
public function insert(SearchableInterface $object);
public function remove(SearchableInterface $object);
}
SearchableInterface
is an interface my models/entities can use to "make" it searchable. It let's ES set the ES id and grabs the type. Usually, every entity gets its own type (so I can search all images and projects, or query only for image types).
I have implemented this for a blog/event system. The service class which persists the blog article into the database triggers events and ElasticSearch is one of the listeners:
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$em = $app->getSharedManager();
$em->attach('Blog\Service\ArticleService', 'create', function($e) use ($sm) {
$searchService = $sm->get('Search\Service\ItemService');
$article = $e->getArticle();
$searchService->insert($article);
});
}
Because Article
implements SearchableInterface
, this works great. Now my blog doesn't have to deal with search and search doesn't have to deal with the blog. But, how is the Article transformed into a search document, you wonder?
I have a hydrator mechanism which works like the ZF2 hydrator. It does not convert any object into an array (and vice versa). It transforms a SearchableInterface
object into an Elastic Search Document
(for storing the object) and it transforms an Elastic Search Result
(which is returned after a search query) into a SearchableInterface
object again.
interface HydratorInterface
{
public function extract(SearchableInterface $object);
public function hydrate(Result $object);
}
Every type has its own hydrator registered. All these different hydrators are collected into a HydratorManager
which is basically a Zend\ServiceManager\AbstractPluginManager
. This plugin manager is injected into the repository and service.
So, inside the service, the following happens:
- The
$object->getType()
is checked
- For its type, the corresponding hydrator is fetched
- The hydrator's
extract()
is called to turn the $object
into a $document
- The underlying ES client is used to persist the document (either it is added or updated, depending on the result of
$object->getElasticSearchId()
For the repository, given a query type:image name:Mountain
, the following happens:
- The repository's
search()
is called with given query
- The string is used for a ES query object and is executed
- The results are iterated
- For every result, the type is checked
- For its type, the corresponding hydrator is fetched
- The hydrator's
hydrate()
is called to turn the $result
into an $object
- The collection of objects is returned