src/Services/ElasticSearchService.php line 162

Open in your IDE?
  1. <?php
  2. namespace App\Services;
  3. use Elasticsearch\ClientBuilder;
  4. final class ElasticSearchService {
  5.     public const MIN_STOCK_REQUIREMENT 5;
  6.     public const MAX_SEARCH_RESULTS 40;
  7.     public const MAX_SEARCH_RESULTS_AJAX 1000;
  8.     public const MAX_WINDOW_RESULTS 100000// 100k
  9.     public const MAX_FILTER_FACET_VALUES 1000// 1000 valeurs max, ce qui est large
  10.     public const PREFIX_INDEX_NAME 'ppmcdev_';
  11.     public const PREFIX_FACET_FIELD_NAME 'facet_';
  12.     public const FACET_PRICE_MIN 'min_price';
  13.     public const FACET_PRICE_MAX 'max_price';
  14.     public const AVAILABLE_SORT_COLUMNS = [
  15.         'name''created_at''price''badge_sort'
  16.     ];
  17.     public function getIndexName(string $localeCodebool $changeIndex false): string
  18.     {
  19.         $projectRoot __DIR__ '/../../';
  20.         $currentIndex json_decode(file_get_contents($projectRoot.'current_elastic_index.json'));
  21.         if($changeIndex) {
  22.             if($currentIndex->index == 1) {
  23.                 $currentIndex->index 2;
  24.             }
  25.             else {
  26.                 $currentIndex->index 1;
  27.             }
  28.         }
  29.         // dump($currentIndex->index);
  30.         // $projectRoot = $this->get('kernel')->getProjectDir();
  31.         return self::PREFIX_INDEX_NAME$currentIndex->index '_' strtolower($localeCode);
  32.     }
  33.     public function rewriteCurrentIndex()
  34.     {
  35.         $projectRoot __DIR__ '/../../';
  36.         $currentIndex json_decode(file_get_contents($projectRoot.'current_elastic_index.json'));
  37.         if($currentIndex->index == 1) {
  38.             $currentIndex->index 2;
  39.         }
  40.         else {
  41.             $currentIndex->index 1;
  42.         }
  43.         file_put_contents($projectRoot.'current_elastic_index.json'json_encode($currentIndex));
  44.     }
  45.     /**
  46.      * Effectue une recherche
  47.      */
  48.     public function search(string $localeCodestring $searchint $offset, array $facetFilters = [], $sortBy null$sortOrder 'desc')
  49.     {
  50.         // REQUETE DE BASE
  51.         $params = [
  52.             'index' => $this->getIndexName($localeCode),
  53.             'from' => $offset,
  54.             'size' => self::MAX_SEARCH_RESULTS,
  55.             'track_total_hits' => true,
  56.             'body' => [
  57.                 'min_score' => 0,
  58.                 'query' => [
  59.                     'bool' => [
  60.                         'must' => [
  61.                             [
  62.                                 'range' => [
  63.                                     'stock' => [
  64.                                         'gte' => self::MIN_STOCK_REQUIREMENT
  65.                                     ]
  66.                                 ]
  67.                             ],
  68.                             [
  69.                                 'term' => [
  70.                                     'blocked' => 0
  71.                                 ]
  72.                             ]
  73.                         ]
  74.                     ]
  75.                 ]
  76.             ],
  77.             '_source' => false
  78.         ];
  79.         // SEARCH QUERY
  80.         if (empty($search) == true) {
  81.             $params['body']['query']['bool']['must'][] = [
  82.                 'match_all' => (object) []
  83.             ];
  84.         } else {
  85.             $params['body']['query']['bool']['must'][] = [
  86.                 'match' => [
  87.                     'global_single_field' => [
  88.                         'query' => $search,
  89.                         'fuzziness' => '1'
  90.                     ]
  91.                 ]
  92.             ];
  93.         }
  94.         // FILTER FACET
  95.         if (count($facetFilters) > 0) {
  96.             foreach ($facetFilters as $key => $value) {
  97.                 if ($key == self::FACET_PRICE_MIN || $key == self::FACET_PRICE_MAX) {
  98.                     $value intval($value);
  99.                 }
  100.                 $fieldKey $key;
  101.                 if (is_numeric($key) == true) {
  102.                     $fieldKey self::PREFIX_FACET_FIELD_NAME $key;
  103.                 }
  104.                 if ($key == self::FACET_PRICE_MIN || $key == self::FACET_PRICE_MAX) {
  105.                     $params['body']['query']['bool']['must'][] = [
  106.                         'range' => [
  107.                             'price' => [
  108.                                 ($key == self::FACET_PRICE_MIN 'gte' 'lte') => $value 100
  109.                             ]
  110.                         ]
  111.                     ];
  112.                 } else {
  113.                     $params['body']['query']['bool']['must'][] = [
  114.                         'terms' => [
  115.                             $fieldKey => is_array($value) ? $value : [$value]
  116.                         ]
  117.                     ];
  118.                 }
  119.             }
  120.         }
  121.         // SORT BY
  122.         if (in_array($sortByself::AVAILABLE_SORT_COLUMNS) == false) {
  123.             $sortBy null;
  124.         }
  125.         if (empty($sortBy) == false) {
  126.             $params['body']['sort'] = [
  127.                 [
  128.                     $sortBy => $sortOrder
  129.                 ]
  130.             ];
  131.         }
  132.         $client ClientBuilder::create()->build();
  133.         $results $client->search($params);
  134.         return $results;
  135.     }
  136.     /**
  137.      * Récupère les filtres à facettes disponible dans ElasticSearch 
  138.      * pour cette recherche
  139.      * 
  140.      * @param int $searchTxt La recherche
  141.      * @param array $facetFilters les filtres à facettes à appliquer sur cette requête de recherche
  142.      * @param array $siteFilterFacetIds Tous les ids de tous les filtres à facettes
  143.      */
  144.     public function getFilterFacetsSearch(string $localeCodestring $search, array $facetFilters = [], array $siteFilterFacetIds)
  145.     {
  146.         // REQUETE DE BASE
  147.         $params = [
  148.             'index' => $this->getIndexName($localeCode),
  149.             'size' => 0,
  150.             'body' => [
  151.                 'min_score' => 1,
  152.                 'query' => [
  153.                     'bool' => [
  154.                         'must' => [
  155.                             [
  156.                                 'range' => [
  157.                                     'stock' => [
  158.                                         'gte' => self::MIN_STOCK_REQUIREMENT
  159.                                     ]
  160.                                 ]
  161.                             ]
  162.                         ]
  163.                     ]
  164.                 ],
  165.                 'aggs' => [
  166.                     'shape' => [
  167.                         'terms' => [
  168.                             'field' => 'shape',
  169.                             'size' => self::MAX_FILTER_FACET_VALUES
  170.                         ]
  171.                     ],
  172.                     'color' => [
  173.                         'terms' => [
  174.                             'field' => 'color',
  175.                             'size' => self::MAX_FILTER_FACET_VALUES
  176.                         ]
  177.                     ],
  178.                     'badge' => [
  179.                         'terms' => [
  180.                             'field' => 'badge',
  181.                             'size' => self::MAX_FILTER_FACET_VALUES
  182.                         ]
  183.                     ],
  184.                     self::FACET_PRICE_MAX => [
  185.                         'max' => [
  186.                             'field' => 'price'
  187.                         ]
  188.                     ],
  189.                     self::FACET_PRICE_MIN => [
  190.                         'min' => [
  191.                             'field' => 'price'
  192.                         ]
  193.                     ]
  194.                 ]
  195.             ],
  196.             '_source' => false
  197.         ];
  198.         // SEARCH QUERY
  199.         if (empty($search) == true) {
  200.             $params['body']['query']['bool']['must'][] = [
  201.                 'match_all' => (object) []
  202.             ];
  203.         } else {
  204.             $params['body']['query']['bool']['must'][] = [
  205.                 'match' => [
  206.                     'global_single_field' => [
  207.                         'query' => $search,
  208.                         'fuzziness' => '1'
  209.                     ]
  210.                 ]
  211.             ];
  212.         }
  213.         // FILTER FACET DE BASE
  214.         if (count($facetFilters) > 0) {
  215.             foreach ($facetFilters as $key => $value) {
  216.                 $fieldKey $key;
  217.                 if (is_numeric($key) == true) {
  218.                     $fieldKey self::PREFIX_FACET_FIELD_NAME $key;
  219.                 }
  220.                 if ($key == self::FACET_PRICE_MIN || $key == self::FACET_PRICE_MAX) {
  221.                     $params['body']['query']['bool']['must'][] = [
  222.                         'range' => [
  223.                             'price' => [
  224.                                 ($key == self::FACET_PRICE_MIN 'gte' 'lte') => $value 100
  225.                             ]
  226.                         ]
  227.                     ];
  228.                 } else {
  229.                     $params['body']['query']['bool']['must'][] = [
  230.                         'terms' => [
  231.                             $fieldKey => is_array($value) ? $value : [$value]
  232.                         ]
  233.                     ];
  234.                 }
  235.             }
  236.         }
  237.         // AGREGATION
  238.         foreach ($siteFilterFacetIds as $rowId) {
  239.             $params['body']['aggs'][$rowId] = [
  240.                 'terms' => [
  241.                     'field' => self::PREFIX_FACET_FIELD_NAME $rowId,
  242.                     'size' => self::MAX_FILTER_FACET_VALUES
  243.                 ]
  244.             ];
  245.         }
  246.         
  247.         $client ClientBuilder::create()->build();
  248.         $results $client->search($params);
  249.         
  250.         // exit();
  251.         return $results['aggregations'];
  252.     }
  253.     /**
  254.      * Récupère des éléments dans ElasticSearch à partir d'un taxon
  255.      */
  256.     public function getElementsByTaxon(string $localeCodeint $taxonIdint $offset, array $facetFilters = [], $sortBy null$sortOrder 'desc'$ajax null)
  257.     {
  258.         // REQUETE DE BASE
  259.         $size self::MAX_SEARCH_RESULTS;
  260.         if($ajax){ 
  261.             $size self::MAX_SEARCH_RESULTS_AJAX;
  262.             $offset 0;
  263.         }
  264.         $params = [
  265.             'index' => $this->getIndexName($localeCode),
  266.             'from' => $offset,
  267.             'size' => $size,
  268.             'track_total_hits' => true,
  269.             'body' => [
  270.                 'min_score' => 0,
  271.                 'query' => [
  272.                     'bool' => [
  273.                         'must' => [
  274.                             [
  275.                                 'term' => [
  276.                                     'taxons' => $taxonId
  277.                                 ],
  278.                             ],
  279.                             [
  280.                                 'range' => [
  281.                                     'stock' => [
  282.                                         'gte' => self::MIN_STOCK_REQUIREMENT
  283.                                     ]
  284.                                 ]
  285.                             ],
  286.                             [
  287.                                 'term' => [
  288.                                     'blocked' => 0
  289.                                 ]
  290.                             ]
  291.                         ]
  292.                     ]
  293.                 ]
  294.             ],
  295.             '_source' => false
  296.         ];
  297.         // FILTER FACET
  298.         if (count($facetFilters) > 0) {
  299.             foreach ($facetFilters as $key => $value) {
  300.                 if ($key == self::FACET_PRICE_MIN || $key == self::FACET_PRICE_MAX) {
  301.                     $value intval($value);
  302.                 }
  303.                 $fieldKey $key;
  304.                 if (is_numeric($key) == true) {
  305.                     $fieldKey self::PREFIX_FACET_FIELD_NAME $key;
  306.                 }
  307.                 if ($key == self::FACET_PRICE_MIN || $key == self::FACET_PRICE_MAX) {
  308.                     $params['body']['query']['bool']['must'][] = [
  309.                         'range' => [
  310.                             'price' => [
  311.                                 ($key == self::FACET_PRICE_MIN 'gte' 'lte') => $value 100
  312.                             ]
  313.                         ]
  314.                     ];
  315.                 } else {
  316.                     $params['body']['query']['bool']['must'][] = [
  317.                         'terms' => [
  318.                             $fieldKey => is_array($value) ? $value : [$value]
  319.                         ]
  320.                     ];
  321.                 }
  322.             }
  323.         }
  324.         // SORT BY
  325.         if (in_array($sortByself::AVAILABLE_SORT_COLUMNS) == false) {
  326.             $sortBy null;
  327.         }
  328.         
  329.         if (empty($sortBy) == true) {
  330.             $params['body']['sort'] = [
  331.                 [
  332.                     'badge_sort' => 'asc'
  333.                 ],
  334.                 [
  335.                     'stock' => 'desc'
  336.                 ],
  337.                 [
  338.                     '_score' => 'desc'
  339.                 ]
  340.             ];
  341.         } else {
  342.             $params['body']['sort'] = [
  343.                 [
  344.                     $sortBy => $sortOrder
  345.                 ]
  346.             ];
  347.         }
  348.     
  349.         $client ClientBuilder::create()->build();
  350.         $results $client->search($params);
  351.         dump$results);
  352.         return $results;
  353.     }
  354.     /**
  355.      * Récupère les filtres à facettes disponible dans ElasticSearch 
  356.      * pour les produits qui sont dans ce taxon.
  357.      * 
  358.      * @param int $taxonId L'ID de la catégorie
  359.      * @param array $siteFilterFacetIds Tous les ids de tous les filtres à facettes
  360.      */
  361.     public function getFilterFacetsByTaxon(string $localeCodeint $taxonId, array $siteFilterFacetIds)
  362.     {
  363.     
  364.         // REQUETE DE BASE
  365.         $params = [
  366.             'index' => $this->getIndexName($localeCode),
  367.             'size' => 0,
  368.             'body' => [
  369.                 'min_score' => 0,
  370.                 'query' => [
  371.                     'bool' => [
  372.                         'must' => [
  373.                             [
  374.                                 'term' => [
  375.                                     'taxons' => $taxonId
  376.                                 ],
  377.                             ],
  378.                             [
  379.                                 'range' => [
  380.                                     'stock' => [
  381.                                         'gte' => self::MIN_STOCK_REQUIREMENT
  382.                                     ]
  383.                                 ]
  384.                             ]
  385.                         ]
  386.                     ]
  387.                 ],
  388.                 'aggs' => [
  389.                     'shape' => [
  390.                         'terms' => [
  391.                             'field' => 'shape',
  392.                             'size' => self::MAX_FILTER_FACET_VALUES
  393.                         ]
  394.                     ],
  395.                     'color' => [
  396.                         'terms' => [
  397.                             'field' => 'color',
  398.                             'size' => self::MAX_FILTER_FACET_VALUES
  399.                         ]
  400.                     ],
  401.                     'badge' => [
  402.                         'terms' => [
  403.                             'field' => 'badge',
  404.                             'size' => self::MAX_FILTER_FACET_VALUES
  405.                         ]
  406.                     ],
  407.                     self::FACET_PRICE_MAX => [
  408.                         'max' => [
  409.                             'field' => 'price'
  410.                         ]
  411.                     ],
  412.                     self::FACET_PRICE_MIN => [
  413.                         'min' => [
  414.                             'field' => 'price'
  415.                         ]
  416.                     ]
  417.                 ]
  418.             ],
  419.             '_source' => false
  420.         ];
  421.         // AGREGATION
  422.     
  423.         foreach ($siteFilterFacetIds as $rowId) {
  424.             $params['body']['aggs'][$rowId] = [
  425.                 'terms' => [
  426.                     'field' => self::PREFIX_FACET_FIELD_NAME $rowId,
  427.                     'size' => self::MAX_FILTER_FACET_VALUES
  428.                 ]
  429.             ];
  430.         }    
  431.         // dump($params);
  432.         // exit();
  433.         $client ClientBuilder::create()->build();
  434.         // dump($client->indices());
  435.         // exit();
  436.         $results $client->search($params);
  437.         // dump( $results);
  438.         return $results['aggregations'];
  439.     }
  440. }