Skip to content

Commit

Permalink
Initial import, hello world 👋
Browse files Browse the repository at this point in the history
  • Loading branch information
damienalexandre committed Apr 29, 2019
0 parents commit 4361bd7
Show file tree
Hide file tree
Showing 20 changed files with 1,146 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4

# Tab indentation (no size specified)
[Makefile]
indent_style = tab
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor/
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
test: ## Run test suite
./vendor/bin/phpunit --bootstrap vendor/autoload.php --testdox --colors=always tests

start: ## Start testing tools (Elasticsearch)
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.0

kibana: ## Start debug tools
docker run -e "ELASTICSEARCH_HOSTS=http://127.0.0.1:9200/" --network host docker.elastic.co/kibana/kibana-oss:7.0.0

cs: ## Fix PHP CS
./vendor/bin/php-cs-fixer fix --verbose --rules=@Symfony,ordered_imports src/
./vendor/bin/php-cs-fixer fix --verbose --rules=@Symfony,ordered_imports tests/

.PHONY: help

help: ## Display this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

.DEFAULT_GOAL := help
156 changes: 156 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Elastically, **Elastica** based framework

*This project is a work in progress.*

![Under Construction](https://jolicode.com/media/original/2019/construction.gif "Optional title")

**Feedback welcome!**

Opinionated [Elastica](https://github.com/ruflin/Elastica) based framework to bootstrap PHP and Elasticsearch implementations.

- DTO are first class citizen, you send object for documents, and get objects back, **like an ODM**;
- All indexes are versioned / aliased;
- Mappings are done in YAML;
- Analysis is separated from mappings;
- 100% compatibility with [ruflin/elastica](https://github.com/ruflin/Elastica);
- Designed for Elasticsearch 7+ (no types);
- Extra commands to monitor, update mapping, reindex... Commonly implemented tasks.

## Demo

Quick example of what the library do on top of Elastica:

```php
<?php

// Your own DTO, or one generated by Jane (see below)
class AwesomeDTO
{
public $foo;
public $bar;
}

// Building the Index from a mapping config
use JoliCode\Elastically\Client;
use Elastica\Document;

// New Client object with new options
new Client([
// Where to find the mappings
Client::CONFIG_MAPPINGS_DIRECTORY => './configs',
// What object to find in each index
Client::CONFIG_INDEX_CLASS_MAPPING => [
'beers' => AwesomeDTO::class,
],
]);

// Service to build Indexes
$indexBuilder = $client->getIndexBuilder();

// Create the Index in Elasticsearch
$index = $indexBuilder->createIndex('beers');

// Set the proper aliases
$indexBuilder->markAsLive($index, 'beers');

// Service to index DTO in an Index
$indexer = $client->getIndexer();

$dto = new AwesomeDTO();
$dto->bar = 'American Pale Ale';
$dto->foo = 'Hops from Alsace, France';

// Add a document to the queue
$indexer->scheduleIndex('beers', new Document('123', $dto));
$indexer->flush();

// Force index refresh if needed
$indexer->refresh('beers');

// Get the Document (new!)
$results = $client->getIndex('beers')->getDocument('123');

// Get the DTO (new!)
$results = $client->getIndex('beers')->getModel('123');

// Perform a search
$results = $client->getIndex('beers')->search('alsace');

// Get the Elastic Document
$results->getDocuments()[0];

// Get the Elastica compatible Result
$results->getResults()[0];

// Get the DTO 🎉 (new!)
$results->getResults()[0]->getModel();
```

*configs/beers.yaml*

```yaml
# Anything you want, no validation
mappings:
properties:
foo:
type: text
analyzer: english
fields:
keyword:
type: keyword
```
## Configuration
This library add custom configurations on top of Elastica's:
### Client::CONFIG_MAPPINGS_DIRECTORY
The directory Elastically is going to look for YAML.
When creating a `foobar` index, a `foobar.yaml` file is expected.

If an `analyzers.yaml` file is present, **all** the indices will get it.

### Client::CONFIG_INDEX_CLASS_MAPPING

An array of index name to class FQN.

```php
[
'indexName' => '\My\AwesomeDTO'
]
```

### Client::CONFIG_SERIALIZER (optional)

A `SerializerInterface` and `DenormalizerInterface` compatible object that will by used both on indexation and search.

Default to Symfony Object Normalizer which can be slow. See below for Jane usage.

*Todo: add custom demo?*

### Client::CONFIG_BULK_SIZE (optional)

When running indexation of lots of documents, this setting allow you to fine-tune the number of document threshold. Default to 100.

## Using Jane for DTO and fast Normalizers

To write.

## To be done

- some "todo" in the code
- optional Doctrine connector
- check Symfony 3.4 compatibility
- optional Symfony integration (DIC)
- scripts / commands for common tasks:
- auto-reindex when the mapping change, handle the aliases and everything
- micro monitoring for cluster / indexes
- health-check method

## Sponsors

[![JoliCode](https://jolicode.com/images/logo.svg)](https://jolicode.com)

Open Source time sponsored by JoliCode.
40 changes: 40 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "jolicode/elastically",
"description": "Opinionated Elastica based framework to bootstrap PHP and Elasticsearch implementations.",
"keywords": [
"Elasticsearch",
"Elastica",
"Symfony",
"Search"
],
"license": "MIT",
"authors": [
{
"name": "Damien Alexandre",
"homepage": "https://jolicode.com/"
}
],
"require": {
"ruflin/elastica": "^6.1",
"symfony/yaml": "^4.2",
"symfony/serializer": "^4.2",
"symfony/property-access": "^4.2",
"symfony/property-info": "^4.2",
"ext-json": "*",
"php": "^7.2"
},
"autoload": {
"psr-4": {
"JoliCode\\Elastically\\": "./src"
}
},
"autoload-dev": {
"psr-4": {
"JoliCode\\Elastically\\Tests\\": "./tests"
}
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.14",
"phpunit/phpunit": "^8"
}
}
56 changes: 56 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace JoliCode\Elastically;

use Elastica\Client as ElasticaClient;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;

class Client extends ElasticaClient
{
/* Elastically config keys */
const CONFIG_MAPPINGS_DIRECTORY = 'elastically_mappings_directory';
const CONFIG_INDEX_CLASS_MAPPING = 'elastically_index_class_mapping';
const CONFIG_SERIALIZER = 'elastically_serializer';
const CONFIG_BULK_SIZE = 'elastically_bulk_size';

protected $indexer;

public function getIndexBuilder(): IndexBuilder
{
return new IndexBuilder($this, $this->getConfig(self::CONFIG_MAPPINGS_DIRECTORY));
}

public function getIndexer(): Indexer
{
if (!$this->indexer) {
$this->indexer = new Indexer($this, $this->getSerializer(), $this->getConfigValue(self::CONFIG_BULK_SIZE, 100));
}

return $this->indexer;
}

public function getIndex($name): Index
{
return new Index($this, $name);
}

public function getSerializer(): SerializerInterface
{
$configSerializer = $this->getConfigValue(self::CONFIG_SERIALIZER, null);
if ($configSerializer) {
return $configSerializer;
}

// Default
return new Serializer([
new ArrayDenormalizer(),
new ObjectNormalizer(),
], [
new JsonEncoder(),
]);
}
}
42 changes: 42 additions & 0 deletions src/Index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace JoliCode\Elastically;

use Elastica\Index as ElasticaIndex;
use Elastica\ResultSet\BuilderInterface;

class Index extends ElasticaIndex
{
protected $builder;

/*
* Compatibility shortcut, types are no longer needed.
*/
public function getDocument($id)
{
return $this->getType('_doc')->getDocument($id);
}

public function getModel($id)
{
$document = $this->getDocument($id);

return $this->getBuilder()->buildModelFromIndexAndData($document->getIndex(), $document->getData());
}

public function createSearch($query = '', $options = null, BuilderInterface $builder = null)
{
$builder = $builder ?? $this->getBuilder();

return parent::createSearch($query, $options, $builder);
}

public function getBuilder(): ResultSetBuilder
{
if (!$this->builder) {
$this->builder = new ResultSetBuilder($this->getClient());
}

return $this->builder;
}
}
Loading

0 comments on commit 4361bd7

Please sign in to comment.