0

I'm developing a Zend Framework 2 application, that should provide:

  • a search in projects with a result list of projects
  • a single project view
  • a search in images with a result list of images (every image belongs to a project)

Also there are two main entities: image and project andthe first idea was to create two modules (Image and Project) and to give to each of them a separate search fuctionaly. But it would mean much of duplicated code. OK, maybe a separate Search module? But then this module would essentially depend on the other modules.

What would be a good approach for such case? Are there a best practice for this?

automatix
  • 14,018
  • 26
  • 105
  • 230

2 Answers2

1

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:

  1. The $object->getType() is checked
  2. For its type, the corresponding hydrator is fetched
  3. The hydrator's extract() is called to turn the $object into a $document
  4. 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:

  1. The repository's search() is called with given query
  2. The string is used for a ES query object and is executed
  3. The results are iterated
  4. For every result, the type is checked
  5. For its type, the corresponding hydrator is fetched
  6. The hydrator's hydrate() is called to turn the $result into an $object
  7. The collection of objects is returned
Jurian Sluiman
  • 13,498
  • 3
  • 67
  • 99
0

I would create some kind of "search interface" that each module would implement to search its own data. Then your search module could check all available modules if they contain this interface, and if they do, use it to search their data...

The drawback I guess is that search code is implemented in each module...

Richard87
  • 1,592
  • 3
  • 16
  • 29