diff --git a/src/Decorator/ProductIndexFieldsProvider.php b/src/Decorator/ProductIndexFieldsProvider.php
index e4d2c2b..361957f 100644
--- a/src/Decorator/ProductIndexFieldsProvider.php
+++ b/src/Decorator/ProductIndexFieldsProvider.php
@@ -14,12 +14,15 @@
 
 namespace Gally\OroPlugin\Decorator;
 
+use Gally\OroPlugin\Engine\SearchEngine;
 use Oro\Bundle\ProductBundle\Search\ProductIndexAttributeProviderInterface;
+use Oro\Bundle\SearchBundle\Engine\EngineParameters;
 
 class ProductIndexFieldsProvider implements ProductIndexAttributeProviderInterface
 {
     public function __construct(
         private ProductIndexAttributeProviderInterface $productIndexAttributeProvider,
+        private EngineParameters $engineParameters,
     ) {
     }
 
@@ -30,7 +33,7 @@ public function addForceIndexed(string $field): void
 
     public function isForceIndexed(string $field): bool
     {
-        // Todo check search engine
-        return true || $this->productIndexAttributeProvider->isForceIndexed($field);
+        return SearchEngine::ENGINE_NAME === $this->engineParameters->getEngineName()
+            || $this->productIndexAttributeProvider->isForceIndexed($field);
     }
 }
diff --git a/src/Engine/ExpressionVisitor.php b/src/Engine/ExpressionVisitor.php
index f7b4e18..c1f3a52 100644
--- a/src/Engine/ExpressionVisitor.php
+++ b/src/Engine/ExpressionVisitor.php
@@ -60,7 +60,7 @@ public function walkComparison(Comparison $comparison): ?array
         }
 
         if ('category_path' === $field) {
-            $this->currentCategoryId = 'node_' . basename(str_replace('_', '/', $value));
+//            $this->currentCategoryId = 'node_' . basename(str_replace('_', '/', $value)); // todo this is wrong, the current category should contain content node id !
 
             return null;
         }
diff --git a/src/Engine/SearchEngine.php b/src/Engine/SearchEngine.php
index f1ef959..0701193 100644
--- a/src/Engine/SearchEngine.php
+++ b/src/Engine/SearchEngine.php
@@ -42,6 +42,7 @@ public function __construct(
         private SearchManager $searchManager,
         private GallyRequestBuilder $requestBuilder,
         private SearchRegistry $registry,
+        private array $attributeMapping,
     ) {
         parent::__construct($eventDispatcher, $queryPlaceholderResolver, $mappingProvider);
     }
@@ -60,9 +61,10 @@ protected function doSearch(Query $query, array $context = [])
         $results = [];
         foreach ($response->getCollection() as $item) {
             $item['id'] = (int) basename($item['id']);
-            $item['system_entity_id'] = $item['id']; // todo manage attributes
-            $item['names'] = $item['name'];
-            $item['descriptions'] = $item['description'] ?? '';
+
+            foreach ($this->attributeMapping as $oroAttribute => $gallyAttribute) {
+                $item[$oroAttribute] = $item[$gallyAttribute] ?? null;
+            }
 
             $results[] = new Item(
                 'product', // Todo manage other entity
diff --git a/src/Extension/GallyDataGridExtension.php b/src/Extension/GallyDataGridExtension.php
index cac8632..adc1d52 100644
--- a/src/Extension/GallyDataGridExtension.php
+++ b/src/Extension/GallyDataGridExtension.php
@@ -14,6 +14,7 @@
 
 namespace Gally\OroPlugin\Extension;
 
+use Gally\OroPlugin\Engine\SearchEngine;
 use Gally\OroPlugin\Registry\SearchRegistry;
 use Gally\Sdk\Entity\Metadata;
 use Gally\Sdk\Entity\SourceField;
@@ -25,6 +26,7 @@
 use Oro\Bundle\DataGridBundle\Extension\AbstractExtension;
 use Oro\Bundle\EntityExtendBundle\Form\Util\EnumTypeHelper;
 use Oro\Bundle\ProductBundle\Entity\Product;
+use Oro\Bundle\SearchBundle\Engine\EngineParameters;
 
 /**
  * Adapt data grid for result managed by Gally.
@@ -32,6 +34,7 @@
 class GallyDataGridExtension extends AbstractExtension
 {
     public function __construct(
+        private EngineParameters $engineParameters,
         private SearchManager $searchManager,
         private SearchRegistry $registry,
         private EnumTypeHelper $enumTypeHelper,
@@ -40,8 +43,8 @@ public function __construct(
 
     public function isApplicable(DatagridConfiguration $config): bool
     {
-        // Todo it is gally search engine
-        return 'frontend-product-search-grid' === $config->getName();
+        return SearchEngine::ENGINE_NAME === $this->engineParameters->getEngineName()
+            && 'frontend-product-search-grid' === $config->getName();
     }
 
     public function visitDatasource(DatagridConfiguration $config, DatasourceInterface $datasource)
diff --git a/src/Resources/config/services/indexer.yml b/src/Resources/config/services/indexer.yml
index 06f2647..9c383e2 100644
--- a/src/Resources/config/services/indexer.yml
+++ b/src/Resources/config/services/indexer.yml
@@ -3,6 +3,7 @@ services:
         decorates: oro_product.provider.index_fields
         arguments:
             - '@.inner'
+            - '@oro_website_search.engine.parameters'
 
     Gally\OroPlugin\Indexer\Provider\CatalogProvider:
         arguments:
diff --git a/src/Resources/config/services/sdk.yml b/src/Resources/config/services/sdk.yml
index 8c1dcef..a7832d9 100644
--- a/src/Resources/config/services/sdk.yml
+++ b/src/Resources/config/services/sdk.yml
@@ -1,6 +1,7 @@
 services:
     Gally\Sdk\Client\Configuration:
         factory: ['\Gally\OroPlugin\Factory\ConfigurationFactory', 'create']
+        lazy: true
         arguments:
             - '@oro_website_search.engine.parameters'
 
diff --git a/src/Resources/config/services/search.yml b/src/Resources/config/services/search.yml
index 1f49879..306080f 100644
--- a/src/Resources/config/services/search.yml
+++ b/src/Resources/config/services/search.yml
@@ -26,6 +26,7 @@ services:
             - '@Gally\Sdk\Service\SearchManager'
             - '@Gally\OroPlugin\RequestBuilder\GallyRequestBuilder'
             - '@Gally\OroPlugin\Registry\SearchRegistry'
+            - '%gally_config.attribute_mapping%'
         calls:
             - ['setMapper', ['@oro_website_search.engine.mapper']]
         tags:
@@ -33,6 +34,7 @@ services:
 
     Gally\OroPlugin\Extension\GallyDataGridExtension:
         arguments:
+            - '@oro_website_search.engine.parameters'
             - '@Gally\Sdk\Service\SearchManager'
             - '@Gally\OroPlugin\Registry\SearchRegistry'
             - '@oro_entity_extend.enum_type_helper'
diff --git a/src/Search/ContextProvider.php b/src/Search/ContextProvider.php
new file mode 100644
index 0000000..9573268
--- /dev/null
+++ b/src/Search/ContextProvider.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
+ *
+ * @package   Gally
+ * @author    Gally Team <elasticsuite@smile.fr>
+ * @copyright 2024-present Smile
+ * @license   Open Software License v. 3.0 (OSL-3.0)
+ */
+
+declare(strict_types=1);
+
+namespace Gally\OroPlugin\Search;
+
+use Gally\OroPlugin\Indexer\Provider\CatalogProvider;
+use Gally\Sdk\Entity\LocalizedCatalog;
+use Oro\Bundle\CatalogBundle\Handler\RequestProductHandler;
+use Oro\Bundle\LocaleBundle\Entity\Localization;
+use Oro\Bundle\LocaleBundle\Helper\LocalizationHelper;
+use Oro\Bundle\WebCatalogBundle\Entity\ContentNode;
+use Oro\Bundle\WebCatalogBundle\Provider\RequestWebContentVariantProvider;
+use Oro\Bundle\WebsiteBundle\Entity\Website;
+use Oro\Bundle\WebsiteBundle\Manager\WebsiteManager;
+
+class ContextProvider
+{
+    public function __construct(
+        private WebsiteManager $websiteManager,
+        private LocalizationHelper $localizationHelper,
+        private CatalogProvider $catalogProvider,
+        private RequestWebContentVariantProvider $requestWebContentVariantProvider,
+    ) {
+    }
+
+    public function getCurrentWebsite(): Website
+    {
+        return $this->websiteManager->getCurrentWebsite();
+    }
+
+    public function getCurrentLocalization(): Localization
+    {
+        return $this->localizationHelper->getCurrentLocalization();
+    }
+
+    public function getCurrentLocalizedCatalog(): LocalizedCatalog
+    {
+        return $this->catalogProvider->buildLocalizedCatalog(
+            $this->getCurrentWebsite(),
+            $this->getCurrentLocalization(),
+        );
+    }
+
+    public function getCurrentContentNode(): ?ContentNode
+    {
+        return $this->requestWebContentVariantProvider->getContentVariant()?->getNode();
+    }
+}
diff --git a/src/Search/ExpressionVisitor.php b/src/Search/ExpressionVisitor.php
new file mode 100644
index 0000000..cceceb9
--- /dev/null
+++ b/src/Search/ExpressionVisitor.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
+ *
+ * @package   Gally
+ * @author    Gally Team <elasticsuite@smile.fr>
+ * @copyright 2024-present Smile
+ * @license   Open Software License v. 3.0 (OSL-3.0)
+ */
+
+declare(strict_types=1);
+
+namespace Gally\OroPlugin\Search;
+
+use Doctrine\Common\Collections\Expr\Comparison;
+use Doctrine\Common\Collections\Expr\CompositeExpression;
+use Doctrine\Common\Collections\Expr\Expression;
+use Doctrine\Common\Collections\Expr\ExpressionVisitor as BaseExpressionVisitor;
+use Doctrine\Common\Collections\Expr\Value;
+use Gally\Sdk\GraphQl\Request;
+use Oro\Bundle\SearchBundle\Query\Criteria\Criteria;
+
+class ExpressionVisitor extends BaseExpressionVisitor
+{
+    private ?string $searchQuery = null;
+    private ?string $currentCategoryId = null;
+
+    public function dispatch(Expression $expr, bool $isMainQuery = true)
+    {
+        // Use main query parameter to flatten main and expression.
+
+        switch (true) {
+            case $expr instanceof Comparison:
+                return $this->walkComparison($expr);
+            case $expr instanceof Value:
+                return $this->walkValue($expr);
+            case $expr instanceof CompositeExpression:
+                return $this->walkCompositeExpression($expr, $isMainQuery);
+            default:
+                throw new \RuntimeException('Unknown Expression ' . $expr::class);
+        }
+    }
+
+    public function walkCompositeExpression(CompositeExpression $expr, bool $isMainQuery = true): array
+    {
+        $type = '_must';
+        if (CompositeExpression::TYPE_AND !== $expr->getType()) {
+            $isMainQuery = false;
+            $type = '_should';
+        }
+
+        $filters = [];
+        foreach ($expr->getExpressionList() as $expression) {
+            $filters[] = $this->dispatch($expression, $isMainQuery);
+        }
+        $filters = array_values(array_filter($filters));
+
+        return $isMainQuery
+            ? array_merge(...$filters)
+            : ['boolFilter' => [$type => $filters]];
+    }
+
+    public function walkComparison(Comparison $comparison): ?array
+    {
+        [$type, $field] = Criteria::explodeFieldTypeName($comparison->getField());
+        $value = $this->dispatch($comparison->getValue());
+        $operator = match ($comparison->getOperator()) {
+            'IN' => Request::FILTER_OPERATOR_IN,
+            'LIKE' => Request::FILTER_OPERATOR_MATCH,
+            default => Request::FILTER_OPERATOR_EQ,
+            // todo add EXISTS
+        };
+
+        if ('all_text' === $field) {
+            $this->searchQuery = $value;
+
+            return null;
+        }
+
+        if ('inv_status' === $field) {
+            $field = 'stock.status';
+            if (count($value) > 1) {
+                return null; // if we want in stock and out of sotck product, we do not need this filter.
+            }
+            $operator = Request::FILTER_OPERATOR_EQ;
+            $value = in_array(\Oro\Bundle\ProductBundle\Entity\Product::INVENTORY_STATUS_IN_STOCK, $value, true);
+            //            return null; //todo mange specificque code for code stock
+        } elseif (str_starts_with($field, 'visibility_customer.')) {
+            [$field, $value] = explode('.', $field);
+        }
+
+        if ('category_path' === $field) {
+            //            $this->currentCategoryId = 'node_' . basename(str_replace('_', '/', $value)); // todo this is wrong, the current category should contain content node id !
+
+            //            return null;
+        }
+
+        if (str_starts_with($field, 'assigned_to')) {
+            return null; // Todo manage this
+        }
+        if (str_starts_with($field, 'manually_added_to')) {
+            return null; // Todo manage this
+        }
+
+        if (str_starts_with($field, 'category_path')) {
+            //            return null;
+        }
+
+        return [$field => [$operator => $value]];
+    }
+
+    public function walkValue(Value $value): mixed
+    {
+        return $value->getValue();
+    }
+
+    public function getCurrentCategoryId(): ?string
+    {
+        return $this->currentCategoryId;
+    }
+
+    public function getSearchQuery(): ?string
+    {
+        return $this->searchQuery;
+    }
+}
diff --git a/src/Search/GallyRequestBuilder.php b/src/Search/GallyRequestBuilder.php
new file mode 100644
index 0000000..99902cf
--- /dev/null
+++ b/src/Search/GallyRequestBuilder.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Gally to newer versions in the future.
+ *
+ * @package   Gally
+ * @author    Gally Team <elasticsuite@smile.fr>
+ * @copyright 2024-present Smile
+ * @license   Open Software License v. 3.0 (OSL-3.0)
+ */
+
+declare(strict_types=1);
+
+namespace Gally\OroPlugin\Search;
+
+use Gally\Sdk\GraphQl\Request;
+use Oro\Bundle\SearchBundle\Query\Criteria\Criteria;
+use Oro\Bundle\SearchBundle\Query\Query;
+
+class GallyRequestBuilder
+{
+    public function __construct(
+        private ContextProvider $contextProvider
+    ) {
+    }
+
+    public function build(Query $query, array $context): Request
+    {
+        if (!$this->isProductQuery($query)) {
+            // Todo !
+            throw new \Exception('Todo manage this');
+        }
+
+        [$currentPage, $pageSize] = $this->getPaginationInfo($query);
+        [$sortField, $sortDirection] = $this->getSortInfo($query);
+        [$searchQuery, $filters] = $this->getFilters($query);
+        $currentContentNode = $this->contextProvider->getCurrentContentNode();
+
+        return new Request(
+            $this->contextProvider->getCurrentLocalizedCatalog(),
+            $this->getSelectedFields($query),
+            $currentPage,
+            $pageSize,
+            $currentContentNode ? (string) $currentContentNode->getId() : null,
+            $searchQuery,
+            $filters,
+            $sortField,
+            $sortDirection,
+        );
+    }
+
+    private function isProductQuery(Query $query): bool
+    {
+        $from = $query->getFrom();
+
+        return 1 === \count($from) && str_starts_with($from[0], 'oro_product');
+    }
+
+    private function getSelectedFields(Query $query): array
+    {
+        // Todo Clean field name
+        $fields = $query->getSelect();
+        $selectedFields = empty($fields) ? [] : ['id'];
+        foreach ($fields as $field) {
+            [$type, $name] = Criteria::explodeFieldTypeName($field);
+            if ('names' === $name) {
+                $name = 'name';
+            }
+            if ('system_entity_id' === $name) {
+                $name = 'id';
+            }
+            if ('inv_status' == $name) { // todo
+                continue;
+            }
+            $selectedFields[] = $name;
+        }
+
+        return $selectedFields;
+    }
+
+    /**
+     * @return array{0: int, 1: int}
+     */
+    private function getPaginationInfo(Query $query): array
+    {
+        $from = (int) $query->getCriteria()->getFirstResult();
+        $pageSize = (int) $query->getCriteria()->getMaxResults() ?: 25;
+        $currentPage = (int) ceil($from / $pageSize) + 1;
+
+        return [$currentPage, $pageSize];
+    }
+
+    /**
+     * @return array{0: ?string, 1: ?string}
+     */
+    private function getSortInfo(Query $query): array
+    {
+        $orders = $query->getCriteria()->getOrderings();
+
+        if (!empty($orders)) {
+            // We can only use one sort order in gally (score and id ordering are added automatically).
+            $order = array_key_first($orders);
+            [$type, $field] = Criteria::explodeFieldTypeName($order);
+
+            if ('category_sort_order' == $field || str_starts_with($field, 'assigned_to_sort_order.')) {
+                // todo manage this globally
+                $field = 'category__position';
+                $field = '_score';
+            }
+
+            return [$field, 'ASC' === $orders[$order] ? Request::SORT_DIRECTION_ASC : Request::SORT_DIRECTION_DESC];
+        }
+
+        return [null, null];
+    }
+
+    /**
+     * @return array{0: ?string, 1: ?string, 2: array}
+     */
+    private function getFilters(Query $query): array
+    {
+        $visitor = new ExpressionVisitor();
+        $filters = [];
+
+        if ($expression = $query->getCriteria()->getWhereExpression()) {
+            $filters = $visitor->dispatch($expression);
+        }
+
+        return [$visitor->getSearchQuery(), [$filters]];
+    }
+}
diff --git a/src/Voter/ElasticSearchEngineFeatureVoter.php b/src/Voter/ElasticSearchEngineFeatureVoter.php
index fc4a3ef..61f1bfd 100644
--- a/src/Voter/ElasticSearchEngineFeatureVoter.php
+++ b/src/Voter/ElasticSearchEngineFeatureVoter.php
@@ -49,6 +49,10 @@ public function vote($feature, $scopeIdentifier = null)
             return self::FEATURE_DISABLED;
         }
 
+        if ('saved_search' === $feature) { // Todo
+            return self::FEATURE_DISABLED;
+        }
+
         return VoterInterface::FEATURE_ABSTAIN;
     }
 }