diff --git a/src/Aggregation/AbstractAggregation.php b/src/Aggregation/AbstractAggregation.php
index 8f0ad9c771d23d9f19ee56c41dd0ac50ff3cafcf..975cdaf54c2b5688577e12b518eb39dfae4c5617 100644
--- a/src/Aggregation/AbstractAggregation.php
+++ b/src/Aggregation/AbstractAggregation.php
@@ -11,18 +11,18 @@
 
 namespace ONGR\ElasticsearchDSL\Aggregation;
 
-use ONGR\ElasticsearchDSL\NamedBuilderBag;
-use ONGR\ElasticsearchDSL\NamedBuilderInterface;
+use ONGR\ElasticsearchDSL\BuilderBag;
+use ONGR\ElasticsearchDSL\BuilderInterface;
+use ONGR\ElasticsearchDSL\NameAwareTrait;
 use ONGR\ElasticsearchDSL\ParametersTrait;
 
 /**
  * AbstractAggregation class.
  */
-abstract class AbstractAggregation implements NamedBuilderInterface
+abstract class AbstractAggregation implements BuilderInterface
 {
     use ParametersTrait;
-
-    const PREFIX = 'agg_';
+    use NameAwareTrait;
 
     /**
      * @var string
@@ -30,20 +30,10 @@ abstract class AbstractAggregation implements NamedBuilderInterface
     private $field;
 
     /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @var NamedBuilderBag
+     * @var BuilderBag
      */
     private $aggregations;
 
-    /**
-     * @return string
-     */
-    abstract public function getType();
-
     /**
      * Abstract supportsNesting method.
      *
@@ -63,9 +53,7 @@ abstract class AbstractAggregation implements NamedBuilderInterface
      */
     public function __construct($name)
     {
-        $this->name = $name;
-
-        $this->aggregations = new NamedBuilderBag();
+        $this->setName($name);
     }
 
     /**
@@ -84,14 +72,6 @@ abstract class AbstractAggregation implements NamedBuilderInterface
         return $this->field;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return self::PREFIX . $this->name;
-    }
-
     /**
      * Adds a sub-aggregation.
      *
@@ -99,17 +79,25 @@ abstract class AbstractAggregation implements NamedBuilderInterface
      */
     public function addAggregation(AbstractAggregation $abstractAggregation)
     {
+        if (!$this->aggregations) {
+            $this->aggregations = $this->createBuilderBag();
+        }
+
         $this->aggregations->add($abstractAggregation);
     }
 
     /**
      * Returns all sub aggregations.
      *
-     * @return AbstractAggregation[]
+     * @return BuilderBag[]
      */
     public function getAggregations()
     {
-        return $this->aggregations->all();
+        if ($this->aggregations) {
+            return $this->aggregations->all();
+        } else {
+            return [];
+        }
     }
 
     /**
@@ -149,4 +137,14 @@ abstract class AbstractAggregation implements NamedBuilderInterface
 
         return $result;
     }
+
+    /**
+     * Creates BuilderBag new instance.
+     *
+     * @return BuilderBag
+     */
+    private function createBuilderBag()
+    {
+        return new BuilderBag();
+    }
 }
diff --git a/src/NamedBuilderBag.php b/src/BuilderBag.php
similarity index 60%
rename from src/NamedBuilderBag.php
rename to src/BuilderBag.php
index d1a22684a9013f6c2d0a48248453f64cc8e718c4..4894764905646915fa51f545818bd1a9c4ba47d7 100644
--- a/src/NamedBuilderBag.php
+++ b/src/BuilderBag.php
@@ -14,27 +14,17 @@ namespace ONGR\ElasticsearchDSL;
 /**
  * Container for named builders.
  */
-class NamedBuilderBag
+class BuilderBag
 {
     /**
-     * @var NamedBuilderInterface[]
+     * @var BuilderInterface[]
      */
     private $bag = [];
 
     /**
-     * @param NamedBuilderInterface[] $builders
+     * @param BuilderInterface[] $builders
      */
-    public function __construct(array $builders = [])
-    {
-        $this->set($builders);
-    }
-
-    /**
-     * Replaces builders with new ones.
-     *
-     * @param NamedBuilderInterface[] $builders
-     */
-    public function set(array $builders)
+    public function __construct($builders = [])
     {
         foreach ($builders as $builder) {
             $this->add($builder);
@@ -44,15 +34,25 @@ class NamedBuilderBag
     /**
      * Adds a builder.
      *
-     * @param NamedBuilderInterface $builder
+     * @param BuilderInterface $builder
+     *
+     * @return string
      */
-    public function add(NamedBuilderInterface $builder)
+    public function add(BuilderInterface $builder)
     {
-        $this->bag[$builder->getName()] = $builder;
+        if (method_exists($builder, 'getName')) {
+            $name = $builder->getName();
+        } else {
+            $name = uniqid();
+        }
+
+        $this->bag[$name] = $builder;
+
+        return $name;
     }
 
     /**
-     * Checks if builder is set by name.
+     * Checks if builder exists by a specific name.
      *
      * @param string $name Builder name.
      *
@@ -66,7 +66,7 @@ class NamedBuilderBag
     /**
      * Removes a builder by name.
      *
-     * @param string $name Builder name.
+     * @param string $name builder name.
      */
     public function remove($name)
     {
@@ -84,9 +84,9 @@ class NamedBuilderBag
     /**
      * Returns a builder by name.
      *
-     * @param string $name Builder name.
+     * @param string $name builder name.
      *
-     * @return NamedBuilderInterface
+     * @return BuilderInterface
      */
     public function get($name)
     {
@@ -96,16 +96,16 @@ class NamedBuilderBag
     /**
      * Returns all builders contained.
      *
-     * @param string|null $type Builder type.
+     * @param string|null $type builder type.
      *
-     * @return NamedBuilderInterface[]
+     * @return BuilderInterface[]
      */
     public function all($type = null)
     {
         return array_filter(
             $this->bag,
-            /** @var NamedBuilderInterface $builder */
-            function (NamedBuilderInterface $builder) use ($type) {
+            /** @var BuilderInterface $builder */
+            function (BuilderInterface $builder) use ($type) {
                 return $type === null || $builder->getType() == $type;
             }
         );
@@ -116,11 +116,10 @@ class NamedBuilderBag
      */
     public function toArray()
     {
-        $out = [];
+        $output = [];
         foreach ($this->all() as $builder) {
-            $out = array_merge($out, $builder->toArray());
+            $output = array_merge($output, $builder->toArray());
         }
-
-        return $out;
+        return $output;
     }
 }
diff --git a/src/Filter/PostFilter.php b/src/Filter/PostFilter.php
deleted file mode 100644
index b085e3be1ce7b18229ca2d554ee76e012e60fcb8..0000000000000000000000000000000000000000
--- a/src/Filter/PostFilter.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Filter;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-
-/**
- * Represents Elasticsearch "post_filter" filter.
- *
- * @link http://www.elastic.co/guide/en/elasticsearch/guide/current/_post_filter.html
- */
-class PostFilter implements BuilderInterface
-{
-    /**
-     * @var BuilderInterface
-     */
-    private $filter;
-
-    /**
-     * Sets a filter.
-     *
-     * @param BuilderInterface $filter
-     */
-    public function __construct(BuilderInterface $filter = null)
-    {
-        if ($this->filter !== null) {
-            $this->setFilter($filter);
-        }
-    }
-    
-    /**
-     * Checks if bool filter is relevant.
-     *
-     * @return bool
-     *
-     * @deprecated Will be removed in 1.0. Use getFilter() method.
-     */
-    public function isRelevant()
-    {
-        return isset($this->filter);
-    }
-
-    /**
-     * Returns filter.
-     *
-     * @return BuilderInterface
-     */
-    public function getFilter()
-    {
-        return $this->filter;
-    }
-
-    /**
-     * Sets filter.
-     *
-     * @param BuilderInterface $filter
-     */
-    public function setFilter(BuilderInterface $filter)
-    {
-        $this->filter = $filter;
-    }
-    
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        return [$this->getFilter()->getType() => $this->getFilter()->toArray()];
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return 'post_filter';
-    }
-}
diff --git a/src/Highlight/Field.php b/src/Highlight/Field.php
deleted file mode 100644
index 536240c1842d07479880b27c34c25ba8d43aeed8..0000000000000000000000000000000000000000
--- a/src/Highlight/Field.php
+++ /dev/null
@@ -1,211 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Highlight;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-use ONGR\ElasticsearchDSL\NamedBuilderInterface;
-
-/**
- * This class holds data for highlighting field.
- */
-class Field implements NamedBuilderInterface
-{
-    const TYPE_PLAIN = 'plain';
-    const TYPE_POSTINGS = 'postings';
-    const TYPE_FVH = 'fvh';
-
-    /**
-     * @var string Field name.
-     */
-    private $name;
-
-    /**
-     * @var string Highlighter type. By default 'plain'.
-     */
-    private $type;
-
-    /**
-     * @var int Size of the highlighted fragment in characters. By default 100.
-     */
-    private $fragmentSize;
-
-    /**
-     * @var int Maximum number of fragments to return. By default 5.
-     */
-    private $numberOfFragments;
-
-    /**
-     * @var array Combine matches on multiple fields to highlight a single field.
-     */
-    private $matchedFields;
-
-    /**
-     * @var array BuilderInterface query to highlight.
-     */
-    private $highlightQuery;
-
-    /**
-     * @var int Show part of string even if there are no matches to highlight. Defaults to 0.
-     */
-    private $noMatchSize;
-
-    /**
-     * @var bool Highlight fields based on the source.
-     */
-    private $forceSource;
-
-    /**
-     * Creates a highlight for a field.
-     *
-     * @param string $name Field name.
-     */
-    public function __construct($name)
-    {
-        $this->name = $name;
-        $this->setMatchedFields([$name]);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * Sets highlighter type (forces). Available options 'plain', 'postings', 'fvh'.
-     *
-     * @param string $type
-     *
-     * @return Field
-     */
-    public function setHighlighterType($type)
-    {
-        $reflection = new \ReflectionClass(__CLASS__);
-        if (in_array($type, $reflection->getConstants())) {
-            $this->type = $type;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Sets field fragment size.
-     *
-     * @param int $fragmentSize
-     *
-     * @return Field
-     */
-    public function setFragmentSize($fragmentSize)
-    {
-        $this->fragmentSize = $fragmentSize;
-
-        return $this;
-    }
-
-    /**
-     * Sets maximum number of fragments to return.
-     *
-     * @param int $numberOfFragments
-     *
-     * @return Field
-     */
-    public function setNumberOfFragments($numberOfFragments)
-    {
-        $this->numberOfFragments = $numberOfFragments;
-
-        return $this;
-    }
-
-    /**
-     * Set fields to match.
-     *
-     * @param array $matchedFields
-     *
-     * @return Field
-     */
-    public function setMatchedFields($matchedFields)
-    {
-        $this->matchedFields = $matchedFields;
-
-        return $this;
-    }
-
-    /**
-     * Set query to highlight.
-     *
-     * @param BuilderInterface $query
-     *
-     * @return Field
-     */
-    public function setHighlightQuery(BuilderInterface $query)
-    {
-        $this->highlightQuery = [$query->getType() => $query->toArray()];
-
-        return $this;
-    }
-
-    /**
-     * Shows set length of a string even if no matches found.
-     *
-     * @param int $noMatchSize
-     *
-     * @return Field
-     */
-    public function setNoMatchSize($noMatchSize)
-    {
-        $this->noMatchSize = $noMatchSize;
-
-        return $this;
-    }
-
-    /**
-     * Set to force highlighting from source.
-     *
-     * @param bool $forceSource
-     *
-     * @return Field
-     */
-    public function setForceSource($forceSource)
-    {
-        $this->forceSource = $forceSource;
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        return array_filter(
-            [
-                'fragment_size' => $this->fragmentSize,
-                'number_of_fragments' => $this->numberOfFragments,
-                'type' => $this->type,
-                'matched_fields' => $this->matchedFields,
-                'highlight_query' => $this->highlightQuery,
-                'no_match_size' => $this->noMatchSize,
-                'force_source' => $this->forceSource,
-            ]
-        );
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return $this->type;
-    }
-}
diff --git a/src/Highlight/Highlight.php b/src/Highlight/Highlight.php
index 13fa0d870e0574a67324a6161ba43d9cbe426a4f..7723ea042b2ee680984e62cc3cafc095f3835ac7 100644
--- a/src/Highlight/Highlight.php
+++ b/src/Highlight/Highlight.php
@@ -12,68 +12,34 @@
 namespace ONGR\ElasticsearchDSL\Highlight;
 
 use ONGR\ElasticsearchDSL\BuilderInterface;
-use ONGR\ElasticsearchDSL\NamedBuilderBag;
-use ONGR\ElasticsearchDSL\NamedBuilderInterface;
+use ONGR\ElasticsearchDSL\ParametersTrait;
 
 /**
  * Data holder for highlight api.
  */
-class Highlight extends NamedBuilderBag implements BuilderInterface
+class Highlight implements BuilderInterface
 {
-    const TYPE_PLAIN = 'plain';
-    const TYPE_POSTINGS = 'postings';
-    const TYPE_FVH = 'fvh';
+    use ParametersTrait;
 
     /**
-     * @var array Holds html tag name and class that highlight will be put in (default 'em' tag).
+     * @var array Holds fields for highlight.
      */
-    private $tags = [];
+    private $fields = [];
 
     /**
-     * @var string Holds tag schema name. 'styled' is the only option yet.
+     * @var array
      */
-    private $tagsSchema = null;
+    private $tags;
 
     /**
-     * @var string Fragments sort type.
-     */
-    private $order = null;
-
-    /**
-     * @var string Highlighter type. By default plain.
-     */
-    private $type = null;
-
-    /**
-     * @var int Size of the highlighted fragment in characters. By default 100.
-     */
-    private $fragmentSize = null;
-
-    /**
-     * @var int Maximum number of fragments to return. By default 5.
-     */
-    private $numberOfFragments = null;
-
-    /**
-     * {@inheritdoc}
-     *
-     * @return Highlight
-     */
-    public function add(NamedBuilderInterface $builder)
-    {
-        parent::add($builder);
-
-        return $this;
-    }
-
-    /**
-     * {@inheritdoc}
+     * @param $name
+     * @param array $params
      *
-     * @return Highlight
+     * @return $this
      */
-    public function set(array $builders)
+    public function addField($name, array $params = [])
     {
-        parent::set($builders);
+        $this->fields[$name] = $params;
 
         return $this;
     }
@@ -81,92 +47,15 @@ class Highlight extends NamedBuilderBag implements BuilderInterface
     /**
      * Sets html tag and its class used in highlighting.
      *
-     * @param string $tag
-     * @param string $class
+     * @param array $preTags
+     * @param array $postTags
      *
-     * @return Highlight
+     * @return $this
      */
-    public function setTag($tag, $class = null)
+    public function setTags(array $preTags, array $postTags)
     {
-        $this->tags[] = array_filter(
-            [
-                'tag' => $tag,
-                'class' => $class,
-            ]
-        );
-
-        return $this;
-    }
-
-    /**
-     * Sets html tag and its class used in highlighting.
-     *
-     * @param string $tagsSchema
-     *
-     * @return Highlight
-     */
-    public function setTagsSchema($tagsSchema)
-    {
-        $this->tagsSchema = $tagsSchema;
-
-        return $this;
-    }
-
-    /**
-     * Sets fragments sort order.
-     *
-     * @param string $order
-     *
-     * @return Highlight
-     */
-    public function setOrder($order)
-    {
-        $this->order = $order;
-
-        return $this;
-    }
-
-    /**
-     * Sets highlighter type (forces). Available options plain, postings, fvh.
-     *
-     * @param string $type
-     *
-     * @return Highlight
-     */
-    public function setHighlighterType($type)
-    {
-        $reflection = new \ReflectionClass(__CLASS__);
-        if (in_array($type, $reflection->getConstants())) {
-            $this->type = $type;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Sets field fragment size.
-     *
-     * @param int $fragmentSize
-     *
-     * @return Highlight
-     */
-    public function setFragmentSize($fragmentSize)
-    {
-        $this->fragmentSize = $fragmentSize;
-
-        return $this;
-    }
-
-    /**
-     * Sets maximum number of fragments to return.
-     *
-     * @param int $numberOfFragments
-     *
-     * @return Highlight
-     */
-    public function setNumberOfFragments($numberOfFragments)
-    {
-        $this->numberOfFragments = $numberOfFragments;
+        $this->tags['pre_tags'] = $preTags;
+        $this->tags['post_tags'] = $postTags;
 
         return $this;
     }
@@ -184,44 +73,18 @@ class Highlight extends NamedBuilderBag implements BuilderInterface
      */
     public function toArray()
     {
-        $highlight = array_filter(
-            [
-                'order' => $this->order,
-                'type' => $this->type,
-                'fragment_size' => $this->fragmentSize,
-                'number_of_fragments' => $this->numberOfFragments,
-                'tags_schema' => $this->tagsSchema,
-                'fields' => $this->getFields(),
-            ]
-        );
-
-        foreach ($this->tags as $tag) {
-            if (isset($tag['tag'])) {
-                $highlight['post_tags'][] = sprintf('</%s>', $tag['tag']);
-
-                if (isset($tag['class'])) {
-                    $highlight['pre_tags'][] = sprintf('<%s class="%s">', $tag['tag'], $tag['class']);
-                } else {
-                    $highlight['pre_tags'][] = sprintf('<%s>', $tag['tag']);
-                }
-            }
+        $output = [];
+
+        if (is_array($this->tags)) {
+            $output = $this->tags;
         }
 
-        return $highlight;
-    }
+        $output = $this->processArray($output);
 
-    /**
-     * Returns fields as array.
-     *
-     * @return array
-     */
-    private function getFields()
-    {
-        $out = [];
-        foreach ($this->all() as $builder) {
-            $out = array_merge($out, [$builder->getName() => $builder->toArray()]);
+        foreach ($this->fields as $field => $params) {
+            $output['fields'][$field] = count($params) ? $params : new \stdClass();
         }
 
-        return $out;
+        return $output;
     }
 }
diff --git a/src/NamedBuilderInterface.php b/src/NameAwareTrait.php
similarity index 53%
rename from src/NamedBuilderInterface.php
rename to src/NameAwareTrait.php
index fd6cac1a68e864da78d78151394f4f6eddfa86d9..ddf7d90b8e73d801d560a0fc6ba38baabd79308b 100644
--- a/src/NamedBuilderInterface.php
+++ b/src/NameAwareTrait.php
@@ -11,15 +11,23 @@
 
 namespace ONGR\ElasticsearchDSL;
 
-/**
- * Interface used by builders with names.
- */
-interface NamedBuilderInterface extends BuilderInterface
+trait NameAwareTrait
 {
+    private $name;
+
+    /**
+     * @return mixed
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
     /**
-     * Returns builder name.
-     *
-     * @return string
+     * @param mixed $name
      */
-    public function getName();
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
 }
diff --git a/src/ParametersTrait.php b/src/ParametersTrait.php
index 0a8e654fc856583979398eae0fd6d8881beade58..480f5d97c9eb4abdb63b250cb3699d8aed7518a7 100644
--- a/src/ParametersTrait.php
+++ b/src/ParametersTrait.php
@@ -33,6 +33,18 @@ trait ParametersTrait
         return isset($this->parameters[$name]);
     }
 
+    /**
+     * Removes parameter.
+     *
+     * @param string $name
+     */
+    public function removeParameter($name)
+    {
+        if ($this->hasParameter($name)) {
+            unset($this->parameters[$name]);
+        }
+    }
+
     /**
      * Returns one parameter by it's name.
      *
diff --git a/src/Query/BoolQuery.php b/src/Query/BoolQuery.php
index d8f9396ef23cbad3e4fc70df7832ea8343799afd..0c4ed3bad83c6851f1ec6dcb8dc0dfcb97cc2ed0 100644
--- a/src/Query/BoolQuery.php
+++ b/src/Query/BoolQuery.php
@@ -15,7 +15,7 @@ use ONGR\ElasticsearchDSL\BuilderInterface;
 use ONGR\ElasticsearchDSL\ParametersTrait;
 
 /**
- * Represents Elasticsearch "bool" filter.
+ * Represents Elasticsearch "bool" query.
  *
  * @link http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html
  */
@@ -32,6 +32,15 @@ class BoolQuery implements BuilderInterface
      */
     private $container = [];
 
+    public function __construct()
+    {
+        $this->container = [
+            self::MUST => [],
+            self::MUST_NOT => [],
+            self::SHOULD => [],
+        ];
+    }
+
     /**
      * Checks if bool expression is relevant.
      *
@@ -39,28 +48,48 @@ class BoolQuery implements BuilderInterface
      */
     public function isRelevant()
     {
-        return (bool)count($this->container);
+        return
+            (count($this->container[self::MUST_NOT]) + count($this->container[self::SHOULD])) === 0
+            && count($this->container[self::MUST]) == 1;
+    }
+
+    public function getQueries($boolType = null)
+    {
+        if ($boolType === null) {
+            return array_merge(
+                $this->container[self::MUST],
+                $this->container[self::MUST_NOT],
+                $this->container[self::SHOULD]
+            );
+        }
+
+        return $this->container[$boolType];
     }
 
     /**
      * Add BuilderInterface object to bool operator.
      *
-     * @param BuilderInterface $builder Query or a filter to add to bool.
-     * @param string           $type    Bool type. Available: must, must_not, should.
+     * @param BuilderInterface $query   Query add to the bool.
+     * @param string           $type    Bool type. Example: must, must_not, should.
+     * @param string           $key     Key that indicates a builder id.
      *
-     * @return BoolQuery
+     * @return string Key of added builder.
      *
      * @throws \UnexpectedValueException
      */
-    public function add(BuilderInterface $builder, $type = self::MUST)
+    public function add(BuilderInterface $query, $type = self::MUST, $key = null)
     {
         if (!in_array($type, (new \ReflectionObject($this))->getConstants())) {
             throw new \UnexpectedValueException(sprintf('The bool operator %s is not supported', $type));
         }
 
-        $this->container[$type][] = [$builder->getType() => $builder->toArray()];
+        if (!$key) {
+            $key = uniqid();
+        }
+
+        $this->container[$type][$key] = $query;
 
-        return $this;
+        return $key;
     }
 
     /**
@@ -68,7 +97,23 @@ class BoolQuery implements BuilderInterface
      */
     public function toArray()
     {
-        return $this->processArray($this->container);
+        $output = $this->processArray();
+
+        if ($this->isRelevant()) {
+            /** @var BuilderInterface $query */
+            $mustContainer = $this->container[self::MUST];
+            $query = array_shift($mustContainer);
+            return [$query->getType() => $query->toArray()];
+        }
+
+        foreach ($this->container as $boolType => $builders) {
+            /** @var BuilderInterface $builder */
+            foreach ($builders as $builder) {
+                $output[$boolType][] = [$builder->getType() => $builder->toArray()];
+            }
+        }
+
+        return $output;
     }
 
     /**
diff --git a/src/Query/Query.php b/src/Query/Query.php
deleted file mode 100644
index abc10a9027fcf3d7e5237fd9dcd4996131d133f9..0000000000000000000000000000000000000000
--- a/src/Query/Query.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Query;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-
-/**
- * Query class.
- *
- * @deprecated will remove in 1.0 Query container now is implemented directly in search.
- */
-class Query implements BuilderInterface
-{
-    /**
-     * @var BuilderInterface[]
-     */
-    private $queries;
-
-    /**
-     * @var NestedQuery
-     *
-     * @deprecated will remove in 1.0 Add NestedQuery to the Query as all other queries.
-     */
-    private $nested;
-
-    /**
-     * @param array $boolParams Possible values:
-     *                          - disable_coord => true
-     *                          - false
-     *                          - minimum_should_match
-     *                          - boost.
-     */
-    public function __construct($boolParams = [])
-    {
-        $this->queries = new BoolQuery();
-        $this->queries->setParameters($boolParams);
-    }
-
-    /**
-     * @param BuilderInterface $query    Query.
-     * @param string           $boolType Possible boolType values:
-     *                                   - must
-     *                                   - must_not
-     *                                   - should.
-     */
-    public function addQuery(BuilderInterface $query, $boolType = 'must')
-    {
-        $this->queries->add($query, $boolType);
-    }
-
-    /**
-     * Overrides query.
-     *
-     * @param BuilderInterface $query
-     */
-    public function setQuery(BuilderInterface $query)
-    {
-        $this->queries = $query;
-    }
-
-    /**
-     * @param array $boolParams Possible values:
-     *                          - disable_coord => true
-     *                          - false
-     *                          - minimum_should_match
-     *                          - boost.
-     */
-    public function setBoolParameters($boolParams)
-    {
-        $this->queries->setParameters($boolParams);
-    }
-
-    /**
-     * @param string           $path
-     * @param BuilderInterface $query
-     *
-     * @deprecated Will remove in 1.0 Add NestedQuery to the Query as all other queries.
-     */
-    public function addToNested($path, $query)
-    {
-        $this->nested = new NestedQuery($path, $query);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return 'query';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $output = [
-            $this->queries->getType() => $this->queries->toArray(),
-        ];
-
-        return $output;
-    }
-}
diff --git a/src/Search.php b/src/Search.php
index a8ba0d72182a25e8069b9e1aaea7038b2f33439b..e67cf4a8dbf934349bae6b3cc7aa5c84e985b1c7 100644
--- a/src/Search.php
+++ b/src/Search.php
@@ -12,13 +12,19 @@
 namespace ONGR\ElasticsearchDSL;
 
 use ONGR\ElasticsearchDSL\Aggregation\AbstractAggregation;
+use ONGR\ElasticsearchDSL\Filter\BoolFilter;
 use ONGR\ElasticsearchDSL\Highlight\Highlight;
+use ONGR\ElasticsearchDSL\Query\BoolQuery;
+use ONGR\ElasticsearchDSL\SearchEndpoint\AggregationsEndpoint;
+use ONGR\ElasticsearchDSL\SearchEndpoint\FilterEndpoint;
+use ONGR\ElasticsearchDSL\SearchEndpoint\HighlightEndpoint;
+use ONGR\ElasticsearchDSL\SearchEndpoint\PostFilterEndpoint;
+use ONGR\ElasticsearchDSL\SearchEndpoint\QueryEndpoint;
 use ONGR\ElasticsearchDSL\SearchEndpoint\SearchEndpointFactory;
 use ONGR\ElasticsearchDSL\SearchEndpoint\SearchEndpointInterface;
+use ONGR\ElasticsearchDSL\SearchEndpoint\SortEndpoint;
 use ONGR\ElasticsearchDSL\Serializer\Normalizer\CustomReferencedNormalizer;
 use ONGR\ElasticsearchDSL\Serializer\OrderedSerializer;
-use ONGR\ElasticsearchDSL\Sort\AbstractSort;
-use ONGR\ElasticsearchDSL\Sort\Sorts;
 use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
 
 /**
@@ -105,170 +111,142 @@ class Search
     }
 
     /**
-     * Adds query to search.
+     * Returns endpoint instance.
      *
-     * @param BuilderInterface $query
-     * @param string           $boolType
+     * @param string $type Endpoint type.
      *
-     * @return Search
+     * @return SearchEndpointInterface
      */
-    public function addQuery(BuilderInterface $query, $boolType = '')
+    public function getEndpoint($type)
     {
-        $this
-            ->getEndpoint('query')
-            ->addBuilder($query, ['bool_type' => $boolType]);
+        if (!array_key_exists($type, $this->endpoints)) {
+            $this->endpoints[$type] = SearchEndpointFactory::get($type);
+        }
 
-        return $this;
+        return $this->endpoints[$type];
     }
 
     /**
-     * Sets parameters for bool query.
-     *
-     * @param array $params Example values:
-     *                      - minimum_should_match => 1
-     *                      - boost => 1.
+     * Destroys search endpoint.
      *
-     * @return Search
+     * @param string $type Endpoint type.
      */
-    public function setBoolQueryParameters(array $params)
+    public function destroyEndpoint($type)
     {
-        $this
-            ->getEndpoint('query')
-            ->setParameters($params);
-
-        return $this;
+        unset($this->endpoints[$type]);
     }
 
     /**
-     * Returns contained query.
+     * Adds query to the search.
      *
-     * @return BuilderInterface
-     */
-    public function getQuery()
-    {
-        return $this
-            ->getEndpoint('query')
-            ->getBuilder();
-    }
-
-    /**
-     * Destroys query part.
+     * @param BuilderInterface $query
+     * @param string           $boolType
+     * @param string           $key
      *
-     * @return Search
+     * @return $this
      */
-    public function destroyQuery()
+    public function addQuery(BuilderInterface $query, $boolType = BoolQuery::MUST, $key = null)
     {
-        $this->destroyEndpoint('query');
+        $endpoint = $this->getEndpoint(QueryEndpoint::NAME);
+        $endpoint->addToBool($query, $boolType, $key);
 
         return $this;
     }
 
     /**
-     * Adds a filter to search.
+     * Adds a filter to the search.
      *
      * @param BuilderInterface $filter   Filter.
-     * @param string           $boolType Possible boolType values:
+     * @param string           $boolType Example boolType values:
      *                                   - must
      *                                   - must_not
      *                                   - should.
+     * @param string           $key
      *
-     * @return Search
+     * @return $this
      */
-    public function addFilter(BuilderInterface $filter, $boolType = '')
+    public function addFilter(BuilderInterface $filter, $boolType = BoolFilter::MUST, $key = null)
     {
-        $this->getEndpoint('query');
-
-        $this
-            ->getEndpoint('filter')
-            ->addBuilder($filter, ['bool_type' => $boolType]);
+        $this->getEndpoint(QueryEndpoint::NAME);
+        $endpoint = $this->getEndpoint(FilterEndpoint::NAME);
+        $endpoint->addToBool($filter, $boolType, $key);
 
         return $this;
     }
 
     /**
-     * Returns currently contained filters.
+     * Adds aggregation into search.
      *
-     * @return BuilderInterface
+     * @param AbstractAggregation $aggregation
+     *
+     * @return $this;
      */
-    public function getFilters()
+    public function addAggregation(AbstractAggregation $aggregation)
     {
-        return $this
-            ->getEndpoint('filter')
-            ->getBuilder();
+        $this->getEndpoint(AggregationsEndpoint::NAME)->add($aggregation, $aggregation->getName());
+
+        return $this;
     }
 
     /**
-     * Sets bool filter parameters.
+     * Adds a post filter to search.
      *
-     * @param array $params Possible values:
-     *                      _cache => true
-     *                      false.
+     * @param BuilderInterface $filter   Filter.
+     * @param string           $boolType Example boolType values:
+     *                                   - must
+     *                                   - must_not
+     *                                   - should.
+     * @param string           $key
      *
-     * @return Search
+     * @return int Key of post filter.
      */
-    public function setBoolFilterParameters($params)
+    public function addPostFilter(BuilderInterface $filter, $boolType = BoolFilter::MUST, $key = null)
     {
         $this
-            ->getEndpoint('filter')
-            ->setParameters($params);
+            ->getEndpoint(PostFilterEndpoint::NAME)
+            ->add($filter, $boolType, $key);
 
         return $this;
     }
 
     /**
-     * Destroys filter part.
-     */
-    public function destroyFilters()
-    {
-        $this->destroyEndpoint('filter');
-    }
-
-    /**
-     * Adds a post filter to search.
+     * Adds sort to search.
      *
-     * @param BuilderInterface $postFilter Post filter.
-     * @param string           $boolType   Possible boolType values:
-     *                                     - must
-     *                                     - must_not
-     *                                     - should.
+     * @param BuilderInterface $sort
      *
-     * @return Search
+     * @return $this
      */
-    public function addPostFilter(BuilderInterface $postFilter, $boolType = '')
+    public function addSort(BuilderInterface $sort)
     {
-        $this
-            ->getEndpoint('post_filter')
-            ->addBuilder($postFilter, ['bool_type' => $boolType]);
+        $this->getEndpoint(SortEndpoint::NAME)->add($sort);
 
         return $this;
     }
 
     /**
-     * Returns all contained post filters.
+     * Allows to highlight search results on one or more fields.
      *
-     * @return BuilderInterface
+     * @param Highlight $highlight
+     *
+     * @return int Key of highlight.
      */
-    public function getPostFilters()
+    public function addHighlight($highlight)
     {
-        return $this
-            ->getEndpoint('post_filter')
-            ->getBuilder();
+        $this->getEndpoint(HighlightEndpoint::NAME)->add($highlight);
+
+        return $this;
     }
 
     /**
-     * Sets bool post filter parameters.
+     * Exclude documents which have a _score less than the minimum specified.
      *
-     * @param array $params Possible values:
-     *                      _cache => true
-     *                      false.
+     * @param float $minScore
      *
-     * @return Search
+     * @return $this
      */
-    public function setBoolPostFilterParameters($params)
+    public function setMinScore($minScore)
     {
-        $this
-            ->getEndpoint('post_filter')
-            ->setParameters($params);
+        $this->minScore = $minScore;
 
         return $this;
     }
@@ -284,25 +262,11 @@ class Search
     }
 
     /**
-     * Exclude documents which have a _score less than the minimum specified.
-     *
-     * @param float $minScore
-     *
-     * @return Search
-     */
-    public function setMinScore($minScore)
-    {
-        $this->minScore = $minScore;
-
-        return $this;
-    }
-
-    /**
-     * Paginate reed removedlts from.
+     * Paginate reed removed lts from.
      *
      * @param int $from
      *
-     * @return Search
+     * @return $this
      */
     public function setFrom($from)
     {
@@ -326,7 +290,7 @@ class Search
      *
      * @param int $size
      *
-     * @return Search
+     * @return $this
      */
     public function setSize($size)
     {
@@ -345,40 +309,12 @@ class Search
         return $this->size;
     }
 
-    /**
-     * Adds sort to search.
-     *
-     * @param AbstractSort $sort
-     *
-     * @return Search
-     */
-    public function addSort(AbstractSort $sort)
-    {
-        $this
-            ->getEndpoint('sort')
-            ->addBuilder($sort);
-
-        return $this;
-    }
-
-    /**
-     * Returns sorts object.
-     *
-     * @return Sorts
-     */
-    public function getSorts()
-    {
-        return $this
-            ->getEndpoint('sort')
-            ->getBuilder();
-    }
-
     /**
      * Allows to control how the _source field is returned with every hit.
      *
      * @param array|bool|string $source
      *
-     * @return Search
+     * @return $this
      */
     public function setSource($source)
     {
@@ -402,7 +338,7 @@ class Search
      *
      * @param array $fields
      *
-     * @return Search
+     * @return $this
      */
     public function setFields(array $fields)
     {
@@ -426,7 +362,7 @@ class Search
      *
      * @param array $scriptFields
      *
-     * @return Search
+     * @return $this
      */
     public function setScriptFields($scriptFields)
     {
@@ -445,40 +381,12 @@ class Search
         return $this->scriptFields;
     }
 
-    /**
-     * Allows to highlight search results on one or more fields.
-     *
-     * @param Highlight $highlight
-     *
-     * @return Search
-     */
-    public function setHighlight($highlight)
-    {
-        $this
-            ->getEndpoint('highlight')
-            ->addBuilder($highlight);
-
-        return $this;
-    }
-
-    /**
-     * Returns containing highlight object.
-     *
-     * @return Highlight
-     */
-    public function getHighlight()
-    {
-        return $this
-            ->getEndpoint('highlight')
-            ->getBuilder();
-    }
-
     /**
      * Sets explain property in request body search.
      *
      * @param bool $explain
      *
-     * @return Search
+     * @return $this
      */
     public function setExplain($explain)
     {
@@ -502,7 +410,7 @@ class Search
      *
      * @param array $stats
      *
-     * @return Search
+     * @return $this
      */
     public function setStats($stats)
     {
@@ -521,40 +429,12 @@ class Search
         return $this->stats;
     }
 
-    /**
-     * Adds aggregation into search.
-     *
-     * @param AbstractAggregation $aggregation
-     *
-     * @return Search
-     */
-    public function addAggregation(AbstractAggregation $aggregation)
-    {
-        $this
-            ->getEndpoint('aggregations')
-            ->addBuilder($aggregation);
-
-        return $this;
-    }
-
-    /**
-     * Returns contained aggregations.
-     *
-     * @return AbstractAggregation[]
-     */
-    public function getAggregations()
-    {
-        return $this
-            ->getEndpoint('aggregations')
-            ->getBuilder();
-    }
-
     /**
      * Setter for scroll duration, effectively setting if search is scrolled or not.
      *
      * @param string|null $duration
      *
-     * @return Search
+     * @return $this
      */
     public function setScroll($duration = '5m')
     {
@@ -578,7 +458,7 @@ class Search
      *
      * @param string $searchType
      *
-     * @return Search
+     * @return $this
      */
     public function setSearchType($searchType)
     {
@@ -602,7 +482,7 @@ class Search
      *
      * Controls which shard replicas to execute the search request on.
      *
-     * @param mixed $preferenceParams Possible values:
+     * @param mixed $preferenceParams Example values:
      *                                _primary
      *                                _primary_first
      *                                _local
@@ -612,7 +492,7 @@ class Search
      *                                custom value
      *                                string[] combination of params.
      *
-     * @return Search
+     * @return $this
      */
     public function setPreference($preferenceParams)
     {
@@ -679,30 +559,4 @@ class Search
 
         return $output;
     }
-
-    /**
-     * Returns endpoint instance.
-     *
-     * @param string $type Endpoint type.
-     *
-     * @return SearchEndpointInterface
-     */
-    private function getEndpoint($type)
-    {
-        if (!array_key_exists($type, $this->endpoints)) {
-            $this->endpoints[$type] = SearchEndpointFactory::get($type);
-        }
-
-        return $this->endpoints[$type];
-    }
-
-    /**
-     * Destroys search endpoint.
-     *
-     * @param string $type Endpoint type.
-     */
-    private function destroyEndpoint($type)
-    {
-        unset($this->endpoints[$type]);
-    }
 }
diff --git a/src/SearchEndpoint/AbstractSearchEndpoint.php b/src/SearchEndpoint/AbstractSearchEndpoint.php
index c7ef15e379064bb8d3b57cd8a12a23df804d39e6..d6bdc9662604c6eb16309df078de5db533ecbc87 100644
--- a/src/SearchEndpoint/AbstractSearchEndpoint.php
+++ b/src/SearchEndpoint/AbstractSearchEndpoint.php
@@ -11,6 +11,8 @@
 
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
+use ONGR\ElasticsearchDSL\BuilderInterface;
+use ONGR\ElasticsearchDSL\ParametersTrait;
 use ONGR\ElasticsearchDSL\Serializer\Normalizer\AbstractNormalizable;
 
 /**
@@ -18,4 +20,79 @@ use ONGR\ElasticsearchDSL\Serializer\Normalizer\AbstractNormalizable;
  */
 abstract class AbstractSearchEndpoint extends AbstractNormalizable implements SearchEndpointInterface
 {
+    use ParametersTrait;
+
+    /**
+     * @var BuilderInterface[]
+     */
+    private $container = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add(BuilderInterface $builder, $key = null)
+    {
+        if (array_key_exists($key, $this->container)) {
+            throw new \OverflowException(sprintf('Builder with %s name for endpoint has already been added!', $key));
+        }
+
+        if (!$key) {
+            $key = uniqid();
+        }
+
+        $this->container[$key] = $builder;
+
+        return $key;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addToBool(BuilderInterface $builder, $boolType = null, $key = null)
+    {
+        throw new \BadFunctionCallException(sprintf("Endpoint %s doesn't support bool statements", static::NAME));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($key)
+    {
+        if ($this->has($key)) {
+            unset($this->container[$key]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Checks if builder with specific key exists.
+     *
+     * @param $key
+     * @return bool
+     */
+    public function has($key)
+    {
+        return array_key_exists($key, $this->container);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($key)
+    {
+        if ($this->has($key)) {
+            return $this->container[$key];
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAll($boolType = null)
+    {
+        return $this->container;
+    }
 }
diff --git a/src/SearchEndpoint/AggregationsEndpoint.php b/src/SearchEndpoint/AggregationsEndpoint.php
index 7c54f2d8e1fad18838beeb7febde711166e1d6bd..444066df949ed2ef3274a7611535cb175870fdf7 100644
--- a/src/SearchEndpoint/AggregationsEndpoint.php
+++ b/src/SearchEndpoint/AggregationsEndpoint.php
@@ -11,54 +11,31 @@
 
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
-use ONGR\ElasticsearchDSL\BuilderInterface;
-use ONGR\ElasticsearchDSL\NamedBuilderBag;
-use ONGR\ElasticsearchDSL\NamedBuilderInterface;
+use ONGR\ElasticsearchDSL\BuilderBag;
 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 
 /**
  * Search aggregations dsl endpoint.
  */
-class AggregationsEndpoint implements SearchEndpointInterface
+class AggregationsEndpoint extends AbstractSearchEndpoint
 {
     /**
-     * @var NamedBuilderBag
+     * Endpoint name
      */
-    private $bag;
-
-    /**
-     * Initialized aggregations bag.
-     */
-    public function __construct()
-    {
-        $this->bag = new NamedBuilderBag();
-    }
+    CONST NAME = 'aggregations';
 
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        if (count($this->bag->all()) > 0) {
-            return $this->bag->toArray();
+        if (count($this->getAll()) > 0) {
+            $output = [];
+            foreach ($this->getAll() as $aggregation) {
+                $output[] = $aggregation->toArray();
+            }
         }
-    }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function addBuilder(BuilderInterface $builder, $parameters = [])
-    {
-        if ($builder instanceof NamedBuilderInterface) {
-            $this->bag->add($builder);
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getBuilder()
-    {
-        return $this->bag->all();
+        return $output;
     }
 }
diff --git a/src/SearchEndpoint/FilterEndpoint.php b/src/SearchEndpoint/FilterEndpoint.php
index e898c40ed718c721c2305245512742915ef8b8fb..ef53e00d82ee63dc9d75218c8e87adae1c78e0f9 100644
--- a/src/SearchEndpoint/FilterEndpoint.php
+++ b/src/SearchEndpoint/FilterEndpoint.php
@@ -20,17 +20,30 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  */
 class FilterEndpoint extends QueryEndpoint
 {
+    /**
+     * Endpoint name
+     */
+    CONST NAME = 'filter';
+
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        if ($this->getBuilder()) {
-            $query = new FilteredQuery();
-            !$this->isBool() ? : $this->getBuilder()->setParameters($this->getParameters());
-            $query->setFilter($this->getBuilder());
-            $this->addReference('filtered_query', $query);
+        if (!$this->getBool()) {
+            return null;
         }
+
+        $query = new FilteredQuery();
+        if ($this->getBool()->isRelevant()) {
+            $filters = $this->getBool()->getQueries(BoolFilter::MUST);
+            $filter = array_shift($filters);
+        } else {
+            $filter = $this->getBool();
+        }
+
+        $query->setFilter($filter);
+        $this->addReference('filtered_query', $query);
     }
 
     /**
@@ -42,7 +55,9 @@ class FilterEndpoint extends QueryEndpoint
     }
 
     /**
-     * {@inheritdoc}
+     * Returns bool instance for this endpoint case.
+     *
+     * @return BoolFilter
      */
     protected function getBoolInstance()
     {
diff --git a/src/SearchEndpoint/HighlightEndpoint.php b/src/SearchEndpoint/HighlightEndpoint.php
index eeaf5b7d34940c2f5310ae3c3c08d275d01b27d8..ba4ff9c3552bd079cda25553604e7a4b8ca95428 100644
--- a/src/SearchEndpoint/HighlightEndpoint.php
+++ b/src/SearchEndpoint/HighlightEndpoint.php
@@ -17,36 +17,53 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 /**
  * Search highlight dsl endpoint.
  */
-class HighlightEndpoint implements SearchEndpointInterface
+class HighlightEndpoint extends AbstractSearchEndpoint
 {
+    /**
+     * Endpoint name
+     */
+    CONST NAME = 'highlight';
+
     /**
      * @var BuilderInterface
      */
     private $highlight;
 
+    /**
+     * @var string Key for highlight storing.
+     */
+    private $key;
+
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        if ($this->getBuilder()) {
-            return $this->getBuilder()->toArray();
+        if ($this->highlight) {
+            return $this->highlight->toArray();
         }
+
+        return null;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function addBuilder(BuilderInterface $builder, $parameters = [])
+    public function add(BuilderInterface $builder, $key = null)
     {
+        if ($this->highlight) {
+            throw new \OverflowException('Only one highlight can be set');
+        }
+
+        $this->key = $key;
         $this->highlight = $builder;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getBuilder()
+    public function getAll($boolType = null)
     {
-        return $this->highlight;
+        return [$this->key => $this->highlight];
     }
 }
diff --git a/src/SearchEndpoint/PostFilterEndpoint.php b/src/SearchEndpoint/PostFilterEndpoint.php
index 3b82a8ac3cf5556eae44a73b88d0832e8a16ac23..88bfc57f5c179d4365461a65ede2bdb52e88fd83 100644
--- a/src/SearchEndpoint/PostFilterEndpoint.php
+++ b/src/SearchEndpoint/PostFilterEndpoint.php
@@ -11,7 +11,7 @@
 
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
-use ONGR\ElasticsearchDSL\Filter\PostFilter;
+use ONGR\ElasticsearchDSL\Filter\BoolFilter;
 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 
 /**
@@ -19,19 +19,35 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  */
 class PostFilterEndpoint extends FilterEndpoint
 {
+    /**
+     * Endpoint name
+     */
+    CONST NAME = 'post_filter';
+
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        if ($this->getBuilder()) {
-            $postFilter = new PostFilter();
-            !$this->isBool() ? : $this->getBuilder()->setParameters($this->getParameters());
-            $postFilter->setFilter($this->getBuilder());
+        if (!$this->getBool()) {
+            return null;
+        }
 
-            return $postFilter->toArray();
+        if ($this->getBool()->isRelevant()) {
+            $filters = $this->getBool()->getQueries(BoolFilter::MUST);
+            $filter = array_shift($filters);
+        } else {
+            $filter = $this->getBool();
         }
 
-        return null;
+        return [$filter->getType() => $filter->toArray()];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOrder()
+    {
+        return 2;
     }
 }
diff --git a/src/SearchEndpoint/QueryEndpoint.php b/src/SearchEndpoint/QueryEndpoint.php
index 80db1e9be84bb2d692cb04c024088898614411f7..89f5ec940199f3f958e5b041c17f27576ba032ce 100644
--- a/src/SearchEndpoint/QueryEndpoint.php
+++ b/src/SearchEndpoint/QueryEndpoint.php
@@ -12,6 +12,7 @@
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
 use ONGR\ElasticsearchDSL\BuilderInterface;
+use ONGR\ElasticsearchDSL\Filter\BoolFilter;
 use ONGR\ElasticsearchDSL\ParametersTrait;
 use ONGR\ElasticsearchDSL\Query\BoolQuery;
 use ONGR\ElasticsearchDSL\Query\FilteredQuery;
@@ -24,140 +25,91 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  */
 class QueryEndpoint extends AbstractSearchEndpoint implements OrderedNormalizerInterface
 {
-    use ParametersTrait;
-
     /**
-     * @var BuilderInterface|BoolQuery
+     * Endpoint name
      */
-    private $query;
+    CONST NAME = 'query';
 
     /**
-     * @var OptionsResolver
+     * @var BoolQuery
      */
-    private $resolver;
-
-    /**
-     * Instantiates resolver.
-     */
-    public function __construct()
-    {
-        $this->resolver = new OptionsResolver();
-        $this->configureResolver($this->resolver);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function addBuilder(BuilderInterface $builder, $parameters = [])
-    {
-        if (!$this->query && !(array_key_exists('bool_type', $parameters) && !empty($parameters['bool_type']))) {
-            $this->setBuilder($builder);
-        } else {
-            $parameters = $this->resolver->resolve(array_filter($parameters));
-            $this->isBool() ? : $this->convertToBool();
-            $this->query->add($builder, $parameters['bool_type']);
-        }
-
-        return $this;
-    }
+    private $bool;
 
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        $isRelevant = false;
-
         if ($this->hasReference('filtered_query')) {
-            /** @var FilteredQuery $query */
-            $query = $this->getReference('filtered_query');
-            $this->addBuilder($query);
-            $isRelevant = true;
+            /** @var FilteredQuery $filteredQuery */
+            $filteredQuery = $this->getReference('filtered_query');
+            $this->add($filteredQuery);
+        }
+
+        if (!$this->bool) {
+            return null;
         }
 
-        if ($this->getBuilder()) {
-            if (method_exists($this->getBuilder(), 'setParameters') && count($this->getParameters()) > 0) {
-                $this
-                    ->getBuilder()
-                    ->setParameters($this->processArray($this->getBuilder()->getParameters()));
-            }
+        $queryArray = $this->bool->toArray();
 
-            $isRelevant = true;
+        if (!$this->bool->isRelevant()) {
+            $queryArray = [$this->bool->getType() => $queryArray];
         }
 
-        return $isRelevant ? [$this->getBuilder()->getType() => $this->getBuilder()->toArray()] : null;
+        return $queryArray;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getOrder()
+    public function add(BuilderInterface $builder, $key = null)
     {
-        return 2;
+        return $this->addToBool($builder, BoolQuery::MUST, $key);
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getBuilder()
+    public function addToBool(BuilderInterface $builder, $boolType = null, $key = null)
     {
-        return $this->query;
-    }
+        if (!$this->bool) {
+            $this->bool = $this->createBoolInstance();
+        }
 
-    /**
-     * Sets builder.
-     *
-     * @param BuilderInterface $builder
-     */
-    protected function setBuilder(BuilderInterface $builder)
-    {
-        $this->query = $builder;
+        $this->bool->add($builder, $boolType, $key);
     }
 
     /**
-     * Default properties for query.
-     *
-     * @param OptionsResolver $resolver
+     * {@inheritdoc}
      */
-    protected function configureResolver(OptionsResolver $resolver)
+    public function getOrder()
     {
-        $resolver
-            ->setDefaults(
-                ['bool_type' => BoolQuery::MUST]
-            );
+        return 3;
     }
 
     /**
-     * Returns true if query is bool.
-     *
-     * @return bool
+     * @return BoolQuery
      */
-    protected function isBool()
+    public function getBool()
     {
-        return $this->getBuilder() instanceof BoolQuery;
+        return $this->bool;
     }
 
     /**
-     * Returns bool instance for this endpoint case.
+     * Returns new bool instance for the endpoint.
      *
      * @return BoolQuery
      */
-    protected function getBoolInstance()
+    protected function createBoolInstance()
     {
         return new BoolQuery();
     }
 
     /**
-     * Converts query to bool.
+     * {@inheritdoc}
      */
-    private function convertToBool()
+    public function getAll($boolType = null)
     {
-        $bool = $this->getBoolInstance();
-
-        if ($this->query !== null) {
-            $bool->add($this->query);
-        }
-
-        $this->query = $bool;
+        return $this->bool->getQueries($boolType);
     }
 }
diff --git a/src/SearchEndpoint/SearchEndpointInterface.php b/src/SearchEndpoint/SearchEndpointInterface.php
index bdd5dc8cd6bd064483eb081105dc3410d513e91f..13991125fab1c4fda8c5b24a18809336b34fd9f4 100644
--- a/src/SearchEndpoint/SearchEndpointInterface.php
+++ b/src/SearchEndpoint/SearchEndpointInterface.php
@@ -12,6 +12,7 @@
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
 use ONGR\ElasticsearchDSL\BuilderInterface;
+use ONGR\ElasticsearchDSL\Query\BoolQuery;
 use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
 
 /**
@@ -23,16 +24,48 @@ interface SearchEndpointInterface extends NormalizableInterface
      * Adds builder to search endpoint.
      *
      * @param BuilderInterface $builder    Builder to add.
-     * @param array            $parameters Additional parameters relevant to builder.
+     * @param array            $key        Additional parameters relevant to builder.
      *
-     * @return SearchEndpointInterface
+     * @return string Key of added builder.
      */
-    public function addBuilder(BuilderInterface $builder, $parameters = []);
+    public function add(BuilderInterface $builder, $key = null);
 
     /**
-     * Returns contained builder.
+     * Adds builder to search endpoint's specific bool type container.
      *
-     * @return BuilderInterface|BuilderInterface[]
+     * @param BuilderInterface $builder    Builder to add.
+     * @param array            $boolType   Bool type for query or filter. If bool type is left null
+     *                                         it will be treated as MUST.
+     * @param array            $key        Additional parameters relevant to builder.
+     *
+     * @return string Key of added builder.
+     */
+    public function addToBool(BuilderInterface $builder, $boolType = null, $key = null);
+
+    /**
+     * Removes contained builder.
+     *
+     * @param int $key
+     *
+     * @return $this
+     */
+    public function remove($key);
+
+    /**
+     * Returns contained builder or null if Builder is not found.
+     *
+     * @param int $key
+     *
+     * @return BuilderInterface|null
+     */
+    public function get($key);
+
+    /**
+     * Returns contained builder or null if Builder is not found.
+     *
+     * @param string|null $boolType If bool type is left null it will return all builders from container.
+     *
+     * @return array
      */
-    public function getBuilder();
+    public function getAll($boolType = null);
 }
diff --git a/src/SearchEndpoint/SortEndpoint.php b/src/SearchEndpoint/SortEndpoint.php
index 578e63a126a976db1a17d2763f554dcc0dd6d582..13eaf6a828bbaa52dd6576bd3f950c6456b34546 100644
--- a/src/SearchEndpoint/SortEndpoint.php
+++ b/src/SearchEndpoint/SortEndpoint.php
@@ -12,50 +12,31 @@
 namespace ONGR\ElasticsearchDSL\SearchEndpoint;
 
 use ONGR\ElasticsearchDSL\BuilderInterface;
+use ONGR\ElasticsearchDSL\Sort\AbstractSort;
 use ONGR\ElasticsearchDSL\Sort\Sorts;
 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 
 /**
  * Search sort dsl endpoint.
  */
-class SortEndpoint implements SearchEndpointInterface
+class SortEndpoint extends AbstractSearchEndpoint
 {
     /**
-     * @var Sorts
+     * Endpoint name
      */
-    protected $sorts;
-
-    /**
-     * Initializes Sorts object.
-     */
-    public function __construct()
-    {
-        $this->sorts = new Sorts();
-    }
+    CONST NAME = 'sort';
 
     /**
      * {@inheritdoc}
      */
     public function normalize(NormalizerInterface $normalizer, $format = null, array $context = [])
     {
-        if ($this->sorts->isRelevant()) {
-            return $this->sorts->toArray();
-        }
-    }
+        $output = [];
 
-    /**
-     * {@inheritdoc}
-     */
-    public function addBuilder(BuilderInterface $builder, $parameters = [])
-    {
-        $this->sorts->addSort($builder);
-    }
+        foreach($this->getAll() as $sort) {
+            $output[] = $sort->toArray();
+        }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getBuilder()
-    {
-        return $this->sorts;
+        return $output;
     }
 }
diff --git a/src/Serializer/Normalizer/AbstractNormalizable.php b/src/Serializer/Normalizer/AbstractNormalizable.php
index b706090e3c94a0ebeaffb9f1124c2ffdee97a5d7..f3569d65081d53c6423aa2cf29121eff9affcce3 100644
--- a/src/Serializer/Normalizer/AbstractNormalizable.php
+++ b/src/Serializer/Normalizer/AbstractNormalizable.php
@@ -24,6 +24,7 @@ abstract class AbstractNormalizable implements NormalizableInterface
         ParametersTrait::getParameter as getReference;
         ParametersTrait::getParameters as getReferences;
         ParametersTrait::addParameter as addReference;
+        ParametersTrait::removeParameter as removeReference;
         ParametersTrait::setParameters as setReferences;
     }
 }
diff --git a/src/Sort/AbstractSort.php b/src/Sort/AbstractSort.php
deleted file mode 100644
index 0e1c1cde09d4afa81c8e479e81d75d6fdf3a5261..0000000000000000000000000000000000000000
--- a/src/Sort/AbstractSort.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Sort;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-
-/**
- * Abstract class for sorting.
- */
-abstract class AbstractSort implements BuilderInterface
-{
-    /**
-     * @const ORDER_ASC Sort in ascending order.
-     */
-    const ORDER_ASC = 'asc';
-
-    /**
-     * @const ORDER_DESC Sort in descending order.
-     */
-    const ORDER_DESC = 'desc';
-
-    /**
-     * @const MODE_MIN Pick the lowest value in multi-valued field.
-     */
-    const MODE_MIN = 'min';
-
-    /**
-     * @const MODE_MAX Pick the highest value in multi-valued field.
-     */
-    const MODE_MAX = 'max';
-
-    /**
-     * @const MODE_AVG Use the sum of all values as sort value. Only applicable for number based array fields.
-     */
-    const MODE_AVG = 'avg';
-
-    /**
-     * @const MODE_SUM Use the average of all values as sort value. Only applicable for number based array fields.
-     */
-    const MODE_SUM = 'sum';
-
-    /**
-     * @var string
-     */
-    private $order = self::ORDER_ASC;
-
-    /**
-     * @var string
-     */
-    private $mode;
-
-    /**
-     * @var string
-     */
-    private $field;
-
-    /**
-     * @param string $field Field name.
-     * @param string $order Order direction.
-     * @param string $mode  Multi-valued field sorting mode [MODE_MIN, MODE_MAX, MODE_AVG, MODE_SUM].
-     */
-    public function __construct($field, $order, $mode)
-    {
-        $this->setField($field);
-        $this->setOrder($order);
-        $this->setMode($mode);
-    }
-
-    /**
-     * Set multi-valued field sorting mode [MODE_MIN, MODE_MAX, MODE_AVG, MODE_SUM].
-     *
-     * @param string $mode
-     */
-    public function setMode($mode)
-    {
-        $this->mode = $mode;
-    }
-
-    /**
-     * Returns mode.
-     *
-     * @return string
-     */
-    public function getMode()
-    {
-        return $this->mode;
-    }
-
-    /**
-     * Set order direction.
-     *
-     * @param string $order
-     */
-    public function setOrder($order)
-    {
-        $this->order = $order;
-    }
-
-    /**
-     * @return string
-     */
-    public function getOrder()
-    {
-        return $this->order;
-    }
-
-    /**
-     * @param string $field
-     */
-    public function setField($field)
-    {
-        $this->field = $field;
-    }
-
-    /**
-     * @return string
-     */
-    public function getField()
-    {
-        return $this->field;
-    }
-
-    /**
-     * @return string
-     */
-    abstract public function getType();
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $value = [];
-
-        if ($this->getOrder()) {
-            $value['order'] = $this->getOrder();
-        }
-
-        if ($this->getMode() !== null) {
-            $value['mode'] = $this->getMode();
-        }
-
-        return $value;
-    }
-}
diff --git a/src/Sort/FieldSort.php b/src/Sort/FieldSort.php
new file mode 100644
index 0000000000000000000000000000000000000000..394affc768d57ecc0f2e5b80b959e7ddfd15680f
--- /dev/null
+++ b/src/Sort/FieldSort.php
@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Sort;
+
+use ONGR\ElasticsearchDSL\BuilderInterface;
+
+/**
+ * Holds all the values required for basic sorting.
+ */
+class FieldSort implements BuilderInterface
+{
+    CONST ASC = 'asc';
+    CONST DESC = 'desc';
+
+    /**
+     * @var string.
+     */
+    private $field;
+
+    /**
+     * @var array
+     */
+    private $params;
+
+    /**
+     * @var BuilderInterface
+     */
+    private $nestedFilter;
+
+    /**
+     * @param string    $field  Field name.
+     * @param array     $params Params that can be set to field sort.
+     */
+    public function __construct($field, $params = [])
+    {
+        $this->field = $field;
+        $this->params = $params;
+    }
+
+    /**
+     * @return BuilderInterface
+     */
+    public function getNestedFilter()
+    {
+        return $this->nestedFilter;
+    }
+
+    /**
+     * @param BuilderInterface $nestedFilter
+     */
+    public function setNestedFilter(BuilderInterface $nestedFilter)
+    {
+        $this->nestedFilter = $nestedFilter;
+    }
+
+    /**
+     * Returns element type.
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return 'sort';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toArray()
+    {
+        if ($this->nestedFilter) {
+            $fieldValues = array_merge(
+                $this->params,
+                ['nested_filter' => [
+                    $this->nestedFilter->getType() => $this->nestedFilter->toArray(),
+                    ]
+                ]
+            );
+        } else {
+            $fieldValues = $this->params;
+        }
+
+        $output = [
+            $this->field => $fieldValues,
+        ];
+
+        return $output;
+    }
+}
diff --git a/src/Sort/GeoSort.php b/src/Sort/GeoSort.php
deleted file mode 100644
index 9f4fd523b742b98ec81f5a1bda8a80270bde6342..0000000000000000000000000000000000000000
--- a/src/Sort/GeoSort.php
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Sort;
-
-/**
- * A special type of sorting by distance.
- */
-class GeoSort extends AbstractSort
-{
-    /**
-     * Possible types.
-     *
-     * Examples:
-     * [-70, 40]
-     * ["lat" : 40, "lon" : -70]
-     * "-70,40"
-     *
-     * @var string|array
-     */
-    protected $location;
-
-    /**
-     * @var string Units in which to measure distance.
-     */
-    protected $unit;
-
-    /**
-     * Constructor for geo sort.
-     *
-     * @param string       $field    Field name.
-     * @param array|string $location Possible types examples:
-     *                               [-70, 40]
-     *                               ["lat" : 40, "lon" : -70]
-     *                               "-70,40".
-     * @param string       $order    Order.
-     * @param string       $unit     Units for measuring the distance.
-     * @param string       $mode     Mode.
-     */
-    public function __construct($field, $location, $order = self::ORDER_DESC, $unit = null, $mode = null)
-    {
-        $this->setLocation($location);
-        $this->setUnit($unit);
-        parent::__construct($field, $order, $mode);
-    }
-
-    /**
-     * @param string $unit
-     */
-    public function setUnit($unit)
-    {
-        $this->unit = $unit;
-    }
-
-    /**
-     * @return string
-     */
-    public function getUnit()
-    {
-        return $this->unit;
-    }
-
-    /**
-     * @param array|string $location
-     */
-    public function setLocation($location)
-    {
-        $this->location = $location;
-    }
-
-    /**
-     * @return array|string
-     */
-    public function getLocation()
-    {
-        return $this->location;
-    }
-
-    /**
-     * @return string
-     */
-    final public function getType()
-    {
-        return '_geo_distance';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $value = parent::toArray();
-
-        if ($this->getUnit() !== null) {
-            $value['unit'] = $this->getUnit();
-        }
-
-        $value[$this->getField()] = $this->getLocation();
-
-        return $value;
-    }
-}
diff --git a/src/Sort/ScriptSort.php b/src/Sort/ScriptSort.php
deleted file mode 100644
index 9106dbfd5da38f7c374c7b9afdc288e850b0153b..0000000000000000000000000000000000000000
--- a/src/Sort/ScriptSort.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Sort;
-
-/**
- * Sort based on custom scripts.
- *
- * Note, it is recommended, for single custom based script based sorting, to use function_score query instead.
- * Sorting based on score is faster.
- */
-class ScriptSort extends AbstractSort
-{
-    /**
-     * @var string Script to execute.
-     */
-    private $script;
-
-    /**
-     * @var string Type returned (number, string).
-     */
-    private $returnType;
-
-    /**
-     * Associative array of custom params with values.
-     *
-     * Example: ['factor' => 1.5]
-     *
-     * @var array
-     */
-    private $params;
-
-    /**
-     * Initializes script sort.
-     *
-     * @param string $script
-     * @param string $returnType
-     * @param array  $params
-     * @param string $order
-     */
-    public function __construct($script, $returnType, $params = null, $order = self::ORDER_DESC)
-    {
-        if ($params) {
-            $this->setParams($params);
-        }
-        $this->setScript($script);
-        $this->setOrder($order);
-        $this->setReturnType($returnType);
-    }
-
-    /**
-     * @return string
-     */
-    public function getReturnType()
-    {
-        return $this->returnType;
-    }
-
-    /**
-     * @param string $returnType
-     */
-    public function setReturnType($returnType)
-    {
-        $this->returnType = $returnType;
-    }
-
-    /**
-     * @return array
-     */
-    public function getParams()
-    {
-        return $this->params;
-    }
-
-    /**
-     * @param array $params
-     */
-    public function setParams($params)
-    {
-        $this->params = $params;
-    }
-
-    /**
-     * @return string
-     */
-    public function getScript()
-    {
-        return $this->script;
-    }
-
-    /**
-     * @param string $script
-     */
-    public function setScript($script)
-    {
-        $this->script = $script;
-    }
-
-    /**
-     * @return string
-     */
-    final public function getType()
-    {
-        return '_script';
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $value = [];
-
-        if ($this->getOrder()) {
-            $value['order'] = $this->getOrder();
-        }
-
-        $value['script'] = $this->getScript();
-
-        if ($this->getParams()) {
-            $value['params'] = $this->getParams();
-        }
-
-        $value['type'] = $this->getReturnType();
-
-        return $value;
-    }
-}
diff --git a/src/Sort/Sort.php b/src/Sort/Sort.php
deleted file mode 100644
index 50068bd93d94df7a39b210ff7aa16c3978338aec..0000000000000000000000000000000000000000
--- a/src/Sort/Sort.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Sort;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-
-/**
- * Holds all the values required for basic sorting.
- */
-class Sort extends AbstractSort
-{
-    /**
-     * @var BuilderInterface Filter for sorting.
-     */
-    private $nestedFilter;
-
-    /**
-     * @param string           $field        Field name.
-     * @param string           $order        Order direction.
-     * @param BuilderInterface $nestedFilter Filter for sorting.
-     * @param string           $mode         Multi-valued field sorting mode [MODE_MIN, MODE_MAX, MODE_AVG, MODE_SUM].
-     */
-    public function __construct($field, $order = self::ORDER_ASC, BuilderInterface $nestedFilter = null, $mode = null)
-    {
-        parent::__construct($field, $order, $mode);
-        if ($nestedFilter) {
-            $this->setNestedFilter($nestedFilter);
-        }
-    }
-
-    /**
-     * Sets nested filter.
-     *
-     * @param BuilderInterface $nestedFilter
-     */
-    public function setNestedFilter($nestedFilter)
-    {
-        $this->nestedFilter = $nestedFilter;
-    }
-
-    /**
-     * Returns nested filter.
-     *
-     * @return BuilderInterface
-     */
-    public function getNestedFilter()
-    {
-        return $this->nestedFilter;
-    }
-
-    /**
-     * @return string
-     */
-    public function getType()
-    {
-        return $this->getField();
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $value = parent::toArray();
-        if ($this->getNestedFilter() !== null) {
-            $value['nested_filter'][$this->getNestedFilter()->getType()] = $this->getNestedFilter()->toArray();
-        }
-
-        return $value;
-    }
-}
diff --git a/src/Sort/Sorts.php b/src/Sort/Sorts.php
deleted file mode 100644
index da1a9b04196cf34d8258b94d156d634b82b524b2..0000000000000000000000000000000000000000
--- a/src/Sort/Sorts.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Sort;
-
-use ONGR\ElasticsearchDSL\BuilderInterface;
-
-/**
- * Container for sorts.
- */
-class Sorts implements BuilderInterface
-{
-    /**
-     * @var AbstractSort[] Sorts collection.
-     */
-    private $sorts = [];
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getType()
-    {
-        return 'sort';
-    }
-
-    /**
-     * @param AbstractSort $sort
-     */
-    public function addSort(AbstractSort $sort)
-    {
-        $this->sorts[$sort->getType()] = $sort;
-    }
-
-    /**
-     * Check if we have any sorting set.
-     *
-     * @return bool
-     */
-    public function isRelevant()
-    {
-        return !empty($this->sorts);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function toArray()
-    {
-        $value = [];
-        foreach ($this->sorts as $sort) {
-            $value[$sort->getType()] = $sort->toArray();
-        }
-
-        return $value;
-    }
-}
diff --git a/tests/Aggregation/FilterAggregationTest.php b/tests/Aggregation/FilterAggregationTest.php
index 753551c8de0c67c6c20b327d7b0494675505e7ca..64cf4ac7cbe6957c329d0f80c86c147f2f7203f5 100644
--- a/tests/Aggregation/FilterAggregationTest.php
+++ b/tests/Aggregation/FilterAggregationTest.php
@@ -42,7 +42,7 @@ class FilterAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->setFilter($filter);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'filter' => [
                     'test_filter' => [
                         'test_field' => ['test_value' => 'test'],
@@ -66,22 +66,22 @@ class FilterAggregationTest extends \PHPUnit_Framework_TestCase
             ->getMockForAbstractClass();
         $aggregation2->expects($this->any())
             ->method('toArray')
-            ->willReturn(['agg_test_agg2' => ['avg' => []]]);
+            ->willReturn(['test_agg2' => ['avg' => []]]);
         $aggregation2->expects($this->any())
             ->method('getName')
-            ->willReturn('agg_test_agg2');
+            ->willReturn('test_agg2');
 
         $aggregation->addAggregation($aggregation2);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'filter' => [
                     'test_filter' => [
                         'test_field' => ['test_value' => 'test'],
                     ],
                 ],
                 'aggregations' => [
-                    'agg_test_agg2' => [
+                    'test_agg2' => [
                         'avg' => [],
                     ],
                 ],
@@ -154,7 +154,7 @@ class FilterAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new FilterAggregation('test', $builderInterface);
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'filter' => [null => null],
                 ],
             ],
diff --git a/tests/Aggregation/FiltersAggregationTest.php b/tests/Aggregation/FiltersAggregationTest.php
index af8c21f149c63ede67bc4be8dcaef1fd7036f8ac..667fbbde1a9e5683cfcd5a7dacdc6a5c11b5ed83 100644
--- a/tests/Aggregation/FiltersAggregationTest.php
+++ b/tests/Aggregation/FiltersAggregationTest.php
@@ -75,7 +75,7 @@ class FiltersAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addFilter($filter, 'second');
         $results = $aggregation->toArray();
         $expected = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'filters' => [
                     'filters' => [
                         'first' => [
@@ -121,7 +121,7 @@ class FiltersAggregationTest extends \PHPUnit_Framework_TestCase
 
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'filters' => [
                         'filters' => [
                             'filter1' => ['type1' => null],
@@ -144,7 +144,7 @@ class FiltersAggregationTest extends \PHPUnit_Framework_TestCase
 
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'filters' => [
                         'filters' => [
                             ['type1' => null],
diff --git a/tests/Aggregation/GeoBoundsAggregationTest.php b/tests/Aggregation/GeoBoundsAggregationTest.php
index d38aa25fd3151a5a50fee6dbed83a36526ca2e26..8d8e258b0c0e1ca181e22fc01e0cf0e1cea82372 100644
--- a/tests/Aggregation/GeoBoundsAggregationTest.php
+++ b/tests/Aggregation/GeoBoundsAggregationTest.php
@@ -48,7 +48,7 @@ class GeoBoundsAggregationTest extends \PHPUnit_Framework_TestCase
         $agg->setField('bar');
         $agg->setWrapLongitude(true);
         $result = [
-            'agg_foo' => [
+            'foo' => [
                 'geo_bounds' => [
                     'field' => 'bar',
                     'wrap_longitude' => true,
@@ -59,7 +59,7 @@ class GeoBoundsAggregationTest extends \PHPUnit_Framework_TestCase
 
         $agg->setWrapLongitude(false);
         $result = [
-            'agg_foo' => [
+            'foo' => [
                 'geo_bounds' => [
                     'field' => 'bar',
                     'wrap_longitude' => false,
diff --git a/tests/Aggregation/GeoDistanceAggregationTest.php b/tests/Aggregation/GeoDistanceAggregationTest.php
index 19ab6994c388e75d5cff3090665d2a64855f4fa8..a610f7b00cf3e0f741e9002bc17f32592f629c34 100644
--- a/tests/Aggregation/GeoDistanceAggregationTest.php
+++ b/tests/Aggregation/GeoDistanceAggregationTest.php
@@ -133,7 +133,7 @@ class GeoDistanceAggregationTest extends \PHPUnit_Framework_TestCase
 
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'geo_distance' => [
                         'field' => 'fieldName',
                         'origin' => 'originValue',
diff --git a/tests/Aggregation/GlobalAggregationTest.php b/tests/Aggregation/GlobalAggregationTest.php
index 49ae84b47b5a6f621bb33d216cf2cc5662da2719..b425af00d1de5ebfb38dba4b30df2d9b83cf74d2 100644
--- a/tests/Aggregation/GlobalAggregationTest.php
+++ b/tests/Aggregation/GlobalAggregationTest.php
@@ -28,7 +28,7 @@ class GlobalAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new GlobalAggregation('test_agg');
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'global' => new \stdClass(),
             ],
         ];
@@ -44,10 +44,10 @@ class GlobalAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addAggregation($aggregation2);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'global' => new \stdClass(),
                 'aggregations' => [
-                    'agg_test_agg_2' => [
+                    'test_agg_2' => [
                         'global' => new \stdClass(),
                     ],
                 ],
diff --git a/tests/Aggregation/Ipv4RangeAggregationTest.php b/tests/Aggregation/Ipv4RangeAggregationTest.php
index 7542584b5e035a454026dfc9d5450ef719af75cf..3f33dd17b2a73a5b1da82901ce31fe6686fc517c 100644
--- a/tests/Aggregation/Ipv4RangeAggregationTest.php
+++ b/tests/Aggregation/Ipv4RangeAggregationTest.php
@@ -34,7 +34,7 @@ class Ipv4RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new Ipv4RangeAggregation('test', 'fieldName', [['from' => 'fromValue']]);
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'ip_range' => [
                         'field' => 'fieldName',
                         'ranges' => [['from' => 'fromValue']],
@@ -47,7 +47,7 @@ class Ipv4RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new Ipv4RangeAggregation('test', 'fieldName', ['maskValue']);
         $this->assertSame(
             [
-                'agg_test' => [
+                'test' => [
                     'ip_range' => [
                         'field' => 'fieldName',
                         'ranges' => [['mask' => 'maskValue']],
diff --git a/tests/Aggregation/NestedAggregationTest.php b/tests/Aggregation/NestedAggregationTest.php
index 941060d162ea524eb178cb77b386d1fbe0d35a2b..166a5c914fad122df024a91ac6db34e07c906a1f 100644
--- a/tests/Aggregation/NestedAggregationTest.php
+++ b/tests/Aggregation/NestedAggregationTest.php
@@ -26,7 +26,7 @@ class NestedAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->setPath('test_path');
 
         $expectedResult = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'nested' => ['path' => 'test_path'],
             ],
         ];
@@ -54,7 +54,7 @@ class NestedAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addAggregation($termMock);
 
         $expectedResult = [
-            'agg_test_nested_agg' => [
+            'test_nested_agg' => [
                 'nested' => ['path' => 'test_path'],
                 'aggregations' => [
                     'terms' => [],
diff --git a/tests/Aggregation/PercentileRanksAggregationTest.php b/tests/Aggregation/PercentileRanksAggregationTest.php
index 94ce741f69db060974bc22fc50eff59d94e285f7..b96f21637fbe3aafca57cb7974295d8e41ca7caf 100644
--- a/tests/Aggregation/PercentileRanksAggregationTest.php
+++ b/tests/Aggregation/PercentileRanksAggregationTest.php
@@ -80,7 +80,7 @@ class PercentileRanksAggregationTest extends \PHPUnit_Framework_TestCase
         $this->agg->setValues(['bar']);
         $this->assertSame(
             [
-                'agg_foo' => [
+                'foo' => [
                     'percentile_ranks' => [
                         'field' => 'bar',
                         'values' => ['bar'],
diff --git a/tests/Aggregation/RangeAggregationTest.php b/tests/Aggregation/RangeAggregationTest.php
index c66244d0811bdfe58b7ac11e09ad6d84b41a25c9..5debcce7a7506fa8259958bfcfb2ee0fb74807d8 100644
--- a/tests/Aggregation/RangeAggregationTest.php
+++ b/tests/Aggregation/RangeAggregationTest.php
@@ -25,7 +25,7 @@ class RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addRange('10', 20);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'range' => [
                     'field' => 'test_field',
                     'ranges' => [
@@ -54,7 +54,7 @@ class RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addRange(null, '20', 'range_2');
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'range' => [
                     'field' => 'test_field',
                     'ranges' => [
@@ -90,7 +90,7 @@ class RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addAggregation($aggregation2);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'range' => [
                     'field' => 'test_field',
                     'ranges' => [
@@ -102,7 +102,7 @@ class RangeAggregationTest extends \PHPUnit_Framework_TestCase
                     'keyed' => false,
                 ],
                 'aggregations' => [
-                    'agg_test_agg_2' => [
+                    'test_agg_2' => [
                         'range' => [
                             'ranges' => [
                                 [
@@ -208,7 +208,7 @@ class RangeAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new RangeAggregation('foo', 'fieldValue', [['from' => 'now', 'key' => 'nowkey']], true);
         $this->assertSame(
             [
-                'agg_foo' => [
+                'foo' => [
                     'range' => [
                         'keyed' => true,
                         'ranges' => [
diff --git a/tests/Aggregation/ReverseNestedAggregationTest.php b/tests/Aggregation/ReverseNestedAggregationTest.php
index 2b401ec06d6b3cd31c8920440c8b42e966f2e6e1..6cb8caa5fe26de2ec58a34121c9903c20dd022d1 100644
--- a/tests/Aggregation/ReverseNestedAggregationTest.php
+++ b/tests/Aggregation/ReverseNestedAggregationTest.php
@@ -26,7 +26,7 @@ class ReverseNestedAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->setPath('test_path');
 
         $expectedResult = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'reverse_nested' => ['path' => 'test_path'],
             ],
         ];
@@ -54,7 +54,7 @@ class ReverseNestedAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addAggregation($termMock);
 
         $expectedResult = [
-            'agg_test_nested_agg' => [
+            'test_nested_agg' => [
                 'reverse_nested' => ['path' => 'test_path'],
                 'aggregations' => [
                     'terms' => [],
@@ -84,7 +84,7 @@ class ReverseNestedAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addAggregation($termMock);
 
         $expectedResult = [
-            'agg_test_nested_agg' => [
+            'test_nested_agg' => [
                 'reverse_nested' => new \stdClass(),
                 'aggregations' => [
                     'terms' => [],
diff --git a/tests/Aggregation/StatsAggregationTest.php b/tests/Aggregation/StatsAggregationTest.php
index f826b397310bbb89a08c4cfda0155737f420b8b3..21b6cfc3791e28315166fad3bcb18a61f26997aa 100644
--- a/tests/Aggregation/StatsAggregationTest.php
+++ b/tests/Aggregation/StatsAggregationTest.php
@@ -24,7 +24,7 @@ class StatsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->setField('test_field');
 
         $expectedResult = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'stats' => ['field' => 'test_field'],
             ],
         ];
@@ -40,7 +40,7 @@ class StatsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation = new StatsAggregation('foo', 'fieldValue', 'scriptValue');
         $this->assertSame(
             [
-                'agg_foo' => [
+                'foo' => [
                     'stats' => [
                         'field' => 'fieldValue',
                         'script' => 'scriptValue',
diff --git a/tests/Aggregation/TermsAggregationTest.php b/tests/Aggregation/TermsAggregationTest.php
index c96ebfe0d4d249873de28cb4adb62e591e70fb76..41df7ec359d18e5ebdc5033ffee3af4f6ea6e2ce 100644
--- a/tests/Aggregation/TermsAggregationTest.php
+++ b/tests/Aggregation/TermsAggregationTest.php
@@ -25,7 +25,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->setField('test_field');
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => ['field' => 'test_field'],
             ],
         ];
@@ -44,7 +44,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('size', 1);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'size' => 1,
@@ -60,7 +60,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('size', 0);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'size' => 0,
@@ -83,7 +83,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('min_doc_count', 10);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'size' => 1,
@@ -107,7 +107,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('exclude', 'pizza_.*');
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'include' => 'test_.*',
@@ -143,7 +143,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         );
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'include' => [
@@ -172,7 +172,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('order', ['_count' => 'asc']);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'order' => ['_count' => 'asc'],
@@ -194,7 +194,7 @@ class TermsAggregationTest extends \PHPUnit_Framework_TestCase
         $aggregation->addParameter('order', ['_term' => 'desc']);
 
         $result = [
-            'agg_test_agg' => [
+            'test_agg' => [
                 'terms' => [
                     'field' => 'test_field',
                     'order' => ['_term' => 'desc'],
diff --git a/tests/Aggregation/TopHitsAggregationTest.php b/tests/Aggregation/TopHitsAggregationTest.php
index a1975eb647aa3b80436fa30128eadda20b816ad9..cd63b502d2b0932580cc935b5e917ce02cb467e9 100644
--- a/tests/Aggregation/TopHitsAggregationTest.php
+++ b/tests/Aggregation/TopHitsAggregationTest.php
@@ -12,6 +12,7 @@
 namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Aggregation;
 
 use ONGR\ElasticsearchDSL\Aggregation\TopHitsAggregation;
+use ONGR\ElasticsearchDSL\Sort\FieldSort;
 use ONGR\ElasticsearchDSL\Sort\Sorts;
 
 /**
@@ -24,15 +25,15 @@ class TopHitsAggregationTest extends \PHPUnit_Framework_TestCase
      */
     public function testToArray()
     {
-        $sorts = new Sorts();
-        $aggregation = new TopHitsAggregation('test', 0, 1);
+        $sort = new FieldSort('acme');
+        $aggregation = new TopHitsAggregation('acme', 0, 1, $sort);
 
         $expectedAgg = new \stdClass();
         $expectedAgg->size = 0;
         $expectedAgg->from = 1;
-        $expectedAgg->sort = $sorts->toArray();
+        $expectedAgg->sort = $sort->toArray();
         $expected = [
-            'agg_test' => [
+            'acme' => [
                 'top_hits' => $expectedAgg,
             ],
         ];
diff --git a/tests/NamedBuilderBagTest.php b/tests/BuilderBagTest.php
similarity index 54%
rename from tests/NamedBuilderBagTest.php
rename to tests/BuilderBagTest.php
index 8dd7d9874394dd5d72df0101565319fc217c751a..b2f7a9569c134b55e377f131c447e598a1508611 100644
--- a/tests/NamedBuilderBagTest.php
+++ b/tests/BuilderBagTest.php
@@ -11,22 +11,20 @@
 
 namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL;
 
-use ONGR\ElasticsearchDSL\NamedBuilderBag;
-use ONGR\ElasticsearchDSL\NamedBuilderInterface;
+use ONGR\ElasticsearchDSL\BuilderBag;
+use ONGR\ElasticsearchDSL\BuilderInterface;
 
-class NamedBuilderBagTest extends \PHPUnit_Framework_TestCase
+class BuilderBagTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * Tests if bag knows if he has a builder.
      */
     public function testHas()
     {
-        $bag = new NamedBuilderBag(
-            [
-                $this->getBuilder('foo'),
-            ]
-        );
-        $this->assertTrue($bag->has('foo'));
+        $bag = new BuilderBag();
+        $fooBuilder = $this->getBuilder('foo');
+        $builderName = $bag->add($fooBuilder);
+        $this->assertTrue($bag->has($builderName));
     }
 
     /**
@@ -34,17 +32,18 @@ class NamedBuilderBagTest extends \PHPUnit_Framework_TestCase
      */
     public function testRemove()
     {
-        $bag = new NamedBuilderBag(
-            [
-                $this->getBuilder('foo'),
-                $this->getBuilder('baz'),
-            ]
-        );
 
-        $bag->remove('foo');
+        $bag = new BuilderBag();
+        $fooBuilder = $this->getBuilder('foo');
+        $acmeBuilder = $this->getBuilder('acme');
+        $fooBuilderName = $bag->add($fooBuilder);
+        $acmeBuilderName = $bag->add($acmeBuilder);
+
+        $bag->remove($fooBuilderName);
 
-        $this->assertFalse($bag->has('foo'), 'Foo builder should not exist anymore.');
-        $this->assertTrue($bag->has('baz'), 'Baz builder should exist.');
+        $this->assertFalse($bag->has($fooBuilderName), 'Foo builder should not exist anymore.');
+        $this->assertTrue($bag->has($acmeBuilderName), 'Acme builder should exist.');
+        $this->assertCount(1, $bag->all());
     }
 
     /**
@@ -52,7 +51,7 @@ class NamedBuilderBagTest extends \PHPUnit_Framework_TestCase
      */
     public function testClear()
     {
-        $bag = new NamedBuilderBag(
+        $bag = new BuilderBag(
             [
                 $this->getBuilder('foo'),
                 $this->getBuilder('baz'),
@@ -69,13 +68,11 @@ class NamedBuilderBagTest extends \PHPUnit_Framework_TestCase
      */
     public function testGet()
     {
-        $bag = new NamedBuilderBag(
-            [
-                $this->getBuilder('baz'),
-            ]
-        );
+        $bag = new BuilderBag();
+        $bazBuilder = $this->getBuilder('baz');
+        $builderName = $bag->add($bazBuilder);
 
-        $this->assertNotEmpty($bag->get('baz'));
+        $this->assertNotEmpty($bag->get($builderName));
     }
 
     /**
@@ -83,14 +80,14 @@ class NamedBuilderBagTest extends \PHPUnit_Framework_TestCase
      *
      * @param string $name
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|NamedBuilderInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject|BuilderInterface
      */
     private function getBuilder($name)
     {
-        $friendlyBuilderMock = $this->getMock('ONGR\ElasticsearchDSL\NamedBuilderInterface');
+        $friendlyBuilderMock = $this->getMock('ONGR\ElasticsearchDSL\BuilderInterface');
 
         $friendlyBuilderMock
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getName')
             ->will($this->returnValue($name));
 
diff --git a/tests/Filter/PostFilterTest.php b/tests/Filter/PostFilterTest.php
deleted file mode 100644
index ecc354d26b649e9e85d1b347338b4e22c056c62b..0000000000000000000000000000000000000000
--- a/tests/Filter/PostFilterTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Filter;
-
-use ONGR\ElasticsearchDSL\Filter\PostFilter;
-
-class PostFilterTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Tests GetType method.
-     */
-    public function testIfGetType()
-    {
-        $postFilter = new PostFilter();
-        $this->assertEquals('post_filter', $postFilter->getType());
-    }
-
-    /**
-     * Test if function is returning False.
-     */
-    public function testIfIsRelevantFunctionIsReturningFalse()
-    {
-        $postFilter = new PostFilter();
-        $this->assertFalse($postFilter->isRelevant());
-    }
-
-    /**
-     * Test addFilter method.
-     */
-    public function testAddFilter()
-    {
-        $missingFilterMock = $this->getMockBuilder('ONGR\ElasticsearchDSL\Filter\MissingFilter')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $missingFilterMock
-            ->expects($this->once())
-            ->method('toArray')
-            ->willReturn([]);
-        $missingFilterMock
-            ->expects($this->once())
-            ->method('getType')
-            ->willReturn('test_type');
-
-        $postFilter = new PostFilter();
-        $postFilter->setFilter($missingFilterMock);
-        $this->assertEquals(['test_type' => []], $postFilter->toArray());
-    }
-}
diff --git a/tests/Highlight/FieldTest.php b/tests/Highlight/FieldTest.php
deleted file mode 100644
index 26d58983c7dab75eea5db8cb9253e467c0527815..0000000000000000000000000000000000000000
--- a/tests/Highlight/FieldTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Highlight;
-
-use ONGR\ElasticsearchDSL\Filter\TermFilter;
-use ONGR\ElasticsearchDSL\Highlight\Field;
-
-/**
- * Unit test for Field.
- */
-class FieldTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Tests getType method.
-     */
-    public function testGetType()
-    {
-        $field = new Field('test');
-
-        $field->setHighlighterType(Field::TYPE_FVH);
-        $this->assertEquals(Field::TYPE_FVH, $field->getType());
-
-        $field->setHighlighterType(Field::TYPE_PLAIN);
-        $this->assertEquals(Field::TYPE_PLAIN, $field->getType());
-
-        $field->setHighlighterType(Field::TYPE_POSTINGS);
-        $this->assertEquals(Field::TYPE_POSTINGS, $field->getType());
-
-        $initValue = $field->getType();
-
-        $field->setHighlighterType('wrongValue');
-        $this->assertEquals($initValue, $field->getType());
-    }
-
-    /**
-     * Tests toArray method.
-     */
-    public function testFieldToArray()
-    {
-        $field = new Field('test');
-        $field->setFragmentSize(5);
-        $field->setNumberOfFragments(5);
-        $field->setHighlightQuery(new TermFilter('key1', 'value1'));
-        $field->setNoMatchSize(3);
-        $field->setForceSource(true);
-
-        $result = [
-            'fragment_size' => 5,
-            'number_of_fragments' => 5,
-            'matched_fields' => ['test'],
-            'highlight_query' => [
-                'term' => [
-                    'key1' => 'value1',
-                ],
-            ],
-            'no_match_size' => 3,
-            'force_source' => true,
-        ];
-        $this->assertEquals($result, $field->toArray());
-    }
-
-    /**
-     * Tests getName method.
-     */
-    public function testFieldGetName()
-    {
-        $field = new Field('test');
-        $result = $field->getName();
-        $this->assertEquals('test', $result);
-    }
-}
diff --git a/tests/Highlight/HighlightTest.php b/tests/Highlight/HighlightTest.php
deleted file mode 100644
index ae3bb159545e31fd7f5549005022c7265e47de20..0000000000000000000000000000000000000000
--- a/tests/Highlight/HighlightTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Highlight;
-
-use ONGR\ElasticsearchDSL\Highlight\Field;
-use ONGR\ElasticsearchDSL\Highlight\Highlight;
-
-/**
- * Unit test for Highlight.
- */
-class HighlightTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Tests toArray method.
-     */
-    public function testHighlightToArray()
-    {
-        $highlight = new Highlight([new Field('name')]);
-        $highlight->setOrder('test');
-        $highlight->setHighlighterType('postings');
-        $highlight->setFragmentSize(5);
-        $highlight->setNumberOfFragments(5);
-        $highlight->setTagsSchema('styled');
-        $highlight->setTag('tag', 'class');
-        $highlight->setTag('only_tag');
-
-        $result = [
-            'order' => 'test',
-            'type' => 'postings',
-            'fragment_size' => 5,
-            'number_of_fragments' => 5,
-            'tags_schema' => 'styled',
-            'post_tags' => ['</tag>', '</only_tag>'],
-            'pre_tags' => ['<tag class="class">', '<only_tag>'],
-            'fields' => [
-                'name' => [
-                    'matched_fields' => ['name'],
-                ],
-            ],
-        ];
-        $this->assertEquals($result, $highlight->toArray());
-    }
-}
diff --git a/tests/Query/IndicesQueryTest.php b/tests/Query/IndicesQueryTest.php
deleted file mode 100644
index 3d12065bec11f16b10aad88b7aa64a9cf2e962dd..0000000000000000000000000000000000000000
--- a/tests/Query/IndicesQueryTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Query;
-
-use ONGR\ElasticsearchDSL\Query\FilteredQuery;
-use ONGR\ElasticsearchDSL\Query\IndicesQuery;
-use ONGR\ElasticsearchDSL\Test\EncapsulationTestAwareTrait;
-
-class IndicesQueryTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Data provider for testToArrayManyIndices function.
-     *
-     * @return array
-     */
-    public function getArrayWithManyIndicesDataProvider()
-    {
-        $queryMock = $this->getMockBuilder('ONGR\ElasticsearchDSL\Query\Query')
-            ->getMock();
-        $queryMock->expects($this->any())
-            ->method('toArray')
-            ->willReturn(['testKey' => 'testValue']);
-        $queryMock->expects($this->any())
-            ->method('getType')
-            ->willReturn('testType');
-
-        return [
-            [
-                $queryMock,
-                [
-                    'test_indice1',
-                    'test_indice2',
-                ],
-                [
-                    'indices' => [
-                        'test_indice1',
-                        'test_indice2',
-                    ],
-                    'query' => [
-                        'testType' => [
-                            'testKey' => 'testValue',
-                        ],
-                    ],
-                ],
-            ],
-        ];
-    }
-
-    /**
-     * Test toArray() method when the number of indices > 1.
-     *
-     * @param Query $query      Query for testing.
-     * @param array $parameters Optional parameters.
-     * @param array $expected   Expected values.
-     *
-     * @dataProvider getArrayWithManyIndicesDataProvider
-     */
-    public function testToArrayWithManyIndices($query, $parameters, $expected)
-    {
-        $query = new IndicesQuery($parameters, $query);
-        $this->assertEquals($expected, $query->toArray());
-    }
-}
diff --git a/tests/Query/QueryTest.php b/tests/Query/QueryTest.php
deleted file mode 100644
index c27a031532a6c076f09efc3bc8f0a314b002bd98..0000000000000000000000000000000000000000
--- a/tests/Query/QueryTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/*
- * This file is part of the ONGR package.
- *
- * (c) NFQ Technologies UAB <info@nfq.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace ONGR\ElasticsearchDSL\Tests\Unit\DSL\Query;
-
-use ONGR\ElasticsearchDSL\Query\Query;
-
-class QueryTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * Tests setBoolParameters method.
-     */
-    public function testSetBoolParameters()
-    {
-        $missingFilterMock = $this->getMockBuilder('ONGR\ElasticsearchDSL\Filter\MissingFilter')
-            ->setConstructorArgs(['test_field'])
-            ->getMock();
-        $missingFilterMock->expects($this->once())
-            ->method('setParameters');
-
-        $query = new Query();
-        $query->setQuery($missingFilterMock);
-        $query->setBoolParameters([false]);
-    }
-
-    /**
-     * Tests addQuery method.
-     */
-    public function testAddQuery()
-    {
-        $missingFilterMock = $this
-            ->getMockBuilder('ONGR\ElasticsearchDSL\Filter\MissingFilter')
-            ->disableOriginalConstructor()
-            ->setMethods(['add'])
-            ->getMock();
-        $missingFilterMock
-            ->expects($this->once())
-            ->method('add')
-            ->withAnyParameters();
-        $postFilterMock = $this
-            ->getMockBuilder('ONGR\ElasticsearchDSL\Filter\PostFilter')
-            ->disableOriginalConstructor()
-            ->getMock();
-
-        $query = new Query();
-        $query->setQuery($missingFilterMock);
-        $query->addQuery($postFilterMock);
-    }
-
-    /**
-     * Tests getType method.
-     */
-    public function testGetType()
-    {
-        $query = new Query();
-        $this->assertEquals('query', $query->getType());
-    }
-
-    /**
-     * Tests toArray method.
-     */
-    public function testToArray()
-    {
-        $missingFilterMock = $this->getMockBuilder('ONGR\ElasticsearchDSL\Filter\MissingFilter')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $missingFilterMock->expects($this->once())
-            ->method('getType')
-            ->willReturn('test_type');
-        $missingFilterMock->expects($this->once())
-            ->method('toArray')
-            ->willReturn('test_array');
-
-        $query = new Query();
-        $query->setQuery($missingFilterMock);
-        $this->assertEquals(['test_type' => 'test_array'], $query->toArray());
-    }
-}
diff --git a/tests/SearchEndpoint/AggregationsEndpointTest.php b/tests/SearchEndpoint/AggregationsEndpointTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..510421b7b5ece916e1ca8bce1ba4a721a6b8c1cb
--- /dev/null
+++ b/tests/SearchEndpoint/AggregationsEndpointTest.php
@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
+
+use ONGR\ElasticsearchDSL\Aggregation\MissingAggregation;
+use ONGR\ElasticsearchDSL\SearchEndpoint\AggregationsEndpoint;
+
+/**
+ * Class AggregationsEndpointTest.
+ */
+class AggregationsEndpointTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests constructor.
+     */
+    public function testItCanBeInstantiated()
+    {
+        $this->assertInstanceOf(
+            'ONGR\ElasticsearchDSL\SearchEndpoint\AggregationsEndpoint',
+            new AggregationsEndpoint()
+        );
+    }
+
+    public function testEndpointGetter()
+    {
+        $aggName = 'acme_agg';
+        $agg = new MissingAggregation('acme');
+        $endpoint = new AggregationsEndpoint();
+        $endpoint->add($agg, $aggName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($agg, $builders[$aggName]);
+    }
+}
diff --git a/tests/SearchEndpoint/FilterEndpointTest.php b/tests/SearchEndpoint/FilterEndpointTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..69d2921b52995fffdf725a659cfb01a32eb33b72
--- /dev/null
+++ b/tests/SearchEndpoint/FilterEndpointTest.php
@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
+
+use ONGR\ElasticsearchDSL\Filter\MatchAllFilter;
+use ONGR\ElasticsearchDSL\Query\FilteredQuery;
+use ONGR\ElasticsearchDSL\SearchEndpoint\FilterEndpoint;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class FilterEndpointTest.
+ */
+class FilterEndpointTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests constructor.
+     */
+    public function testItCanBeInstantiated()
+    {
+        $this->assertInstanceOf(
+            'ONGR\ElasticsearchDSL\SearchEndpoint\FilterEndpoint',
+            new FilterEndpoint()
+        );
+    }
+
+    /**
+     * Tests if correct order is returned. It's very important that filters must be executed first.
+     */
+    public function testGetOrder()
+    {
+        $instance = new FilterEndpoint();
+        $this->assertEquals(1, $instance->getOrder());
+    }
+
+    /**
+     * Test normalization.
+     */
+    public function testNormalization()
+    {
+        $instance = new FilterEndpoint();
+        /** @var NormalizerInterface|MockObject $normalizerInterface */
+        $normalizerInterface = $this->getMockForAbstractClass(
+            'Symfony\Component\Serializer\Normalizer\NormalizerInterface'
+        );
+        $this->assertNull($instance->normalize($normalizerInterface));
+        $this->assertFalse($instance->hasReference('filtered_query'));
+
+        $matchAllFilter = new MatchAllFilter();
+        $instance->add($matchAllFilter);
+
+        $this->assertNull($instance->normalize($normalizerInterface));
+        $this->assertTrue($instance->hasReference('filtered_query'));
+
+        /** @var FilteredQuery $reference */
+        $reference = $instance->getReference('filtered_query');
+        $this->assertInstanceOf('ONGR\ElasticsearchDSL\Query\FilteredQuery', $reference);
+        $this->assertSame($matchAllFilter, $reference->getFilter());
+    }
+
+    public function testEndpointGetter()
+    {
+        $filterName = 'acme_filter';
+        $filter = new MatchAllFilter();
+        $endpoint = new FilterEndpoint();
+        $endpoint->add($filter, $filterName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($filter, $builders[$filterName]);
+    }
+}
diff --git a/tests/SearchEndpoint/HighlightEndpointTest.php b/tests/SearchEndpoint/HighlightEndpointTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..85200ae0141b5f7aaec125f275a061ac0537fcf2
--- /dev/null
+++ b/tests/SearchEndpoint/HighlightEndpointTest.php
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
+
+use ONGR\ElasticsearchDSL\Highlight\Highlight;
+use ONGR\ElasticsearchDSL\SearchEndpoint\HighlightEndpoint;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class HighlightEndpointTest.
+ */
+class HighlightEndpointTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests constructor.
+     */
+    public function testItCanBeInstantiated()
+    {
+        $this->assertInstanceOf('ONGR\ElasticsearchDSL\SearchEndpoint\HighlightEndpoint', new HighlightEndpoint());
+    }
+
+    /**
+     * Tests adding builder.
+     */
+    public function testNormalization()
+    {
+        $instance = new HighlightEndpoint();
+        /** @var NormalizerInterface|MockObject $normalizerInterface */
+        $normalizerInterface = $this->getMockForAbstractClass(
+            'Symfony\Component\Serializer\Normalizer\NormalizerInterface'
+        );
+
+        $this->assertNull($instance->normalize($normalizerInterface));
+
+        $highlight = new Highlight();
+        $highlight->addField('acme');
+        $instance->add($highlight);
+
+        $this->assertEquals(
+            json_encode($highlight->toArray()),
+            json_encode($instance->normalize($normalizerInterface))
+        );
+    }
+
+    public function testEndpointGetter()
+    {
+        $highlightName = 'acme_highlight';
+        $highlight = new Highlight();
+        $highlight->addField('acme');
+
+        $endpoint = new HighlightEndpoint();
+        $endpoint->add($highlight, $highlightName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($highlight, $builders[$highlightName]);
+    }
+}
diff --git a/tests/SearchEndpoint/PostFilterEndpointTest.php b/tests/SearchEndpoint/PostFilterEndpointTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3490ba7b6b7d7b1c0fe938afc8d9d3f8723a8e1f
--- /dev/null
+++ b/tests/SearchEndpoint/PostFilterEndpointTest.php
@@ -0,0 +1,74 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
+
+use ONGR\ElasticsearchDSL\Filter\MatchAllFilter;
+use ONGR\ElasticsearchDSL\SearchEndpoint\PostFilterEndpoint;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * Class PostFilterEndpointTest.
+ */
+class PostFilterEndpointTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests constructor.
+     */
+    public function testItCanBeInstantiated()
+    {
+        $this->assertInstanceOf('ONGR\ElasticsearchDSL\SearchEndpoint\PostFilterEndpoint', new PostFilterEndpoint());
+    }
+
+    /**
+     * Tests if correct order is returned. It's very important that filters must be executed second.
+     */
+    public function testGetOrder()
+    {
+        $instance = new PostFilterEndpoint();
+        $this->assertEquals(2, $instance->getOrder());
+    }
+
+    /**
+     * Test normalization.
+     */
+    public function testNormalization()
+    {
+        $instance = new PostFilterEndpoint();
+        /** @var NormalizerInterface|MockObject $normalizerInterface */
+        $normalizerInterface = $this->getMockForAbstractClass(
+            'Symfony\Component\Serializer\Normalizer\NormalizerInterface'
+        );
+        $this->assertNull($instance->normalize($normalizerInterface));
+
+        $matchAll = new MatchAllFilter();
+        $instance->add($matchAll);
+
+        $this->assertEquals(
+            json_encode([$matchAll->getType() => $matchAll->toArray()]),
+            json_encode($instance->normalize($normalizerInterface))
+        );
+    }
+
+    public function testEndpointGetter()
+    {
+        $filterName = 'acme_post_filter';
+        $filter = new MatchAllFilter();
+
+        $endpoint = new PostFilterEndpoint();
+        $endpoint->add($filter, $filterName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($filter, $builders[$filterName]);
+    }
+}
diff --git a/tests/SearchEndpoint/QueryEndpointTest.php b/tests/SearchEndpoint/QueryEndpointTest.php
index 52fe8c09441477798593629afa9dad94516b296f..0a17c703c7e51c891c19ee29d8ec909f9574ab57 100644
--- a/tests/SearchEndpoint/QueryEndpointTest.php
+++ b/tests/SearchEndpoint/QueryEndpointTest.php
@@ -11,7 +11,10 @@
 
 namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
 
+use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
 use ONGR\ElasticsearchDSL\SearchEndpoint\QueryEndpoint;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 
 /**
  * Unit test class for the QueryEndpoint.
@@ -25,4 +28,47 @@ class QueryEndpointTest extends \PHPUnit_Framework_TestCase
     {
         $this->assertInstanceOf('ONGR\ElasticsearchDSL\SearchEndpoint\QueryEndpoint', new QueryEndpoint());
     }
+
+    /**
+     * Tests if correct order is returned. Query must be executed after filter and post filter.
+     */
+    public function testGetOrder()
+    {
+        $instance = new QueryEndpoint();
+        $this->assertEquals(3, $instance->getOrder());
+    }
+
+    /**
+     * Tests if endpoint return correct normalized data.
+     */
+    public function testEndpoint()
+    {
+        $instance = new QueryEndpoint();
+        /** @var NormalizerInterface|MockObject $normalizerInterface */
+        $normalizerInterface = $this->getMockForAbstractClass(
+            'Symfony\Component\Serializer\Normalizer\NormalizerInterface'
+        );
+
+        $this->assertNull($instance->normalize($normalizerInterface));
+
+        $matchAll = new MatchAllQuery();
+        $instance->add($matchAll);
+
+        $this->assertEquals(
+            [$matchAll->getType() => $matchAll->toArray()],
+            $instance->normalize($normalizerInterface)
+        );
+    }
+
+    public function testEndpointGetter()
+    {
+        $queryName = 'acme_query';
+        $query = new MatchAllQuery();
+        $endpoint = new QueryEndpoint();
+        $endpoint->add($query, $queryName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($query, $builders[$queryName]);
+    }
 }
diff --git a/tests/SearchEndpoint/SearchEndpointFactoryTest.php b/tests/SearchEndpoint/SearchEndpointFactoryTest.php
index 5f62d2376a0c9088154e1e279f465894e28ac72b..9d134dc9d1f209f746e66ac8a00c8f6d946f5d73 100644
--- a/tests/SearchEndpoint/SearchEndpointFactoryTest.php
+++ b/tests/SearchEndpoint/SearchEndpointFactoryTest.php
@@ -11,6 +11,7 @@
 
 namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
 
+use ONGR\ElasticsearchDSL\SearchEndpoint\AggregationsEndpoint;
 use ONGR\ElasticsearchDSL\SearchEndpoint\SearchEndpointFactory;
 
 /**
@@ -27,4 +28,12 @@ class SearchEndpointFactoryTest extends \PHPUnit_Framework_TestCase
     {
         SearchEndpointFactory::get('foo');
     }
+
+    /**
+     * Tests if factory can create endpoint.
+     */
+    public function testFactory()
+    {
+        SearchEndpointFactory::get(AggregationsEndpoint::NAME);
+    }
 }
diff --git a/tests/SearchEndpoint/SortEndpointTest.php b/tests/SearchEndpoint/SortEndpointTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c34758347fc84fc81da32c54264971496e8f3956
--- /dev/null
+++ b/tests/SearchEndpoint/SortEndpointTest.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the ONGR package.
+ *
+ * (c) NFQ Technologies UAB <info@nfq.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace ONGR\ElasticsearchDSL\Tests\Unit\SearchEndpoint;
+
+use ONGR\ElasticsearchDSL\SearchEndpoint\SortEndpoint;
+use ONGR\ElasticsearchDSL\Sort\FieldSort;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+
+/**
+ * Class SortEndpointTest.
+ */
+class SortEndpointTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests constructor.
+     */
+    public function testItCanBeInstantiated()
+    {
+        $this->assertInstanceOf('ONGR\ElasticsearchDSL\SearchEndpoint\SortEndpoint', new SortEndpoint());
+    }
+
+    /**
+     * Tests endpoint normalization.
+     */
+    public function testNormalize()
+    {
+        $instance = new SortEndpoint();
+
+        /** @var NormalizerInterface|MockObject $normalizerInterface */
+        $normalizerInterface = $this->getMockForAbstractClass(
+            'Symfony\Component\Serializer\Normalizer\NormalizerInterface'
+        );
+
+        $sort = new FieldSort('acme', ['order' => FieldSort::ASC]);
+        $instance->add($sort);
+
+        $this->assertEquals(
+            [$sort->toArray()],
+            $instance->normalize($normalizerInterface)
+        );
+    }
+
+    public function testEndpointGetter()
+    {
+        $sortName = 'acme_sort';
+        $sort = new FieldSort('acme');
+        $endpoint = new SortEndpoint();
+        $endpoint->add($sort, $sortName);
+        $builders = $endpoint->getAll();
+
+        $this->assertCount(1, $builders);
+        $this->assertSame($sort, $builders[$sortName]);
+    }
+}