diff --git a/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php b/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php
index 8590c9b587..e094730e23 100644
--- a/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php
+++ b/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Adapter/FieldMapper/ProductFieldMapper.php
@@ -131,7 +131,7 @@ class ProductFieldMapper implements FieldMapperInterface
                 if ($attribute->getIsFilterable() || $attribute->getIsFilterableInSearch()) {
                     $allAttributes[$attributeCode]['type'] = FieldType::ES_DATA_TYPE_KEYWORD;
                 } else if ($allAttributes[$attributeCode]['type'] === FieldType::ES_DATA_TYPE_TEXT) {
-                    $allAttributes[$attributeCode]['index'] = 'no';
+                    $allAttributes[$attributeCode]['index'] = false;
                 }
             } else if ($attributeCode == "category_ids") {
                 $allAttributes[$attributeCode] = [
@@ -173,8 +173,15 @@ class ProductFieldMapper implements FieldMapperInterface
      */
     protected function getRefinedFieldName($frontendInput, $fieldType, $attributeCode)
     {
-        return (in_array($frontendInput, ['select', 'boolean'], true) && $fieldType === 'integer')
-            ? $attributeCode . '_value' : $attributeCode;
+        switch ($frontendInput) {
+            case 'select':
+            case 'multiselect':
+                return in_array($fieldType, ['text','integer'], true) ? $attributeCode . '_value' : $attributeCode;
+            case 'boolean':
+                return $fieldType === 'integer' ? $attributeCode . '_value' : $attributeCode;
+            default:
+                return $attributeCode;
+        }
     }
 
     /**
diff --git a/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
index e260447173..0356ba2353 100644
--- a/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
+++ b/vendor/magento/module-elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php
@@ -252,7 +252,7 @@ class Elasticsearch implements ClientInterface
                                 'match_mapping_type' => 'string',
                                 'mapping' => $this->prepareFieldInfo([
                                     'type' => 'text',
-                                    'index' => 'no',
+                                    'index' => false,
                                 ]),
                             ],
                         ],
diff --git a/vendor/magento/module-elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php b/vendor/magento/module-elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php
index 4d329f212d..c08f7f9d54 100644
--- a/vendor/magento/module-elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php
+++ b/vendor/magento/module-elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php
@@ -6,7 +6,6 @@
 namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper;
 
 use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider;
-use Magento\Eav\Api\Data\AttributeInterface;
 use Magento\Elasticsearch\Model\Adapter\Document\Builder;
 use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
 use Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface;
@@ -36,11 +35,6 @@ class ProductDataMapper implements BatchDataMapperInterface
     /**
      * @var array
      */
-    private $attributeData = [];
-
-    /**
-     * @var array
-     */
     private $excludedAttributes;
 
     /**
@@ -107,8 +101,6 @@ class ProductDataMapper implements BatchDataMapperInterface
      */
     public function map(array $documentData, $storeId, array $context = [])
     {
-        // reset attribute data for new store
-        $this->attributeData = [];
         $documents = [];
 
         foreach ($documentData as $productId => $indexData) {
@@ -120,9 +112,7 @@ class ProductDataMapper implements BatchDataMapperInterface
                     $this->builder->addField($attributeCode, $value);
                     continue;
                 }
-                if (in_array($attributeCode, $this->excludedAttributes, true)) {
-                    continue;
-                }
+
                 $this->builder->addField(
                     $this->fieldMapper->getFieldName(
                         $attributeCode,
@@ -154,25 +144,31 @@ class ProductDataMapper implements BatchDataMapperInterface
      * @param int $storeId
      * @return array
      */
-    private function convertToProductData($productId, array $indexData, $storeId)
+    private function convertToProductData(int $productId, array $indexData, int $storeId): array
     {
         $productAttributes = [];
-        foreach ($indexData as $attributeId => $attributeValue) {
-            $attributeData = $this->getAttributeData($attributeId);
-            if (!$attributeData) {
+
+        if (isset($indexData['options'])) {
+            // cover case with "options"
+            // see \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider::prepareProductIndex
+            $productAttributes['options'] = $indexData['options'];
+            unset($indexData['options']);
+        }
+
+        foreach ($indexData as $attributeId => $attributeValues) {
+            /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
+            $attribute = $this->dataProvider->getSearchableAttribute($attributeId);
+            if (in_array($attribute->getAttributeCode(), $this->excludedAttributes, true)) {
                 continue;
             }
-            $productAttributes = array_merge(
-                $productAttributes,
-                $this->convertAttribute(
-                    $productId,
-                    $attributeId,
-                    $attributeValue,
-                    $attributeData,
-                    $storeId
-                )
-            );
+
+            if (!\is_array($attributeValues)) {
+                $attributeValues = [$productId => $attributeValues];
+            }
+            $attributeValues = $this->prepareAttributeValues($productId, $attribute, $attributeValues, $storeId);
+            $productAttributes += $this->convertAttribute($attribute, $attributeValues);
         }
+
         return $productAttributes;
     }
 
@@ -180,174 +176,108 @@ class ProductDataMapper implements BatchDataMapperInterface
      * Convert data for attribute: 1) add new value {attribute_code}_value for select and multiselect searchable
      * attributes, that will contain actual value 2) add child products data to composite products
      *
-     * @param int $productId
-     * @param int $attributeId
-     * @param mixed $attributeValue
-     * @param array $attributeData
-     * @param int $storeId
+     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
+     * @param array $attributeValues
      * @return array
      */
-    private function convertAttribute($productId, $attributeId, $attributeValue, array $attributeData, $storeId)
+    private function convertAttribute($attribute, array $attributeValues): array
     {
         $productAttributes = [];
-        $attributeCode = $attributeData[AttributeInterface::ATTRIBUTE_CODE];
-        $attributeFrontendInput = $attributeData[AttributeInterface::FRONTEND_INPUT];
-        if (is_array($attributeValue)) {
-            if (!$attributeData['is_searchable']) {
-                $value = $this->getValueForAttribute(
-                    $productId,
-                    $attributeCode,
-                    $attributeValue,
-                    $attributeData['is_searchable']
-                );
-            } else {
-                if (($attributeFrontendInput == 'select' || $attributeFrontendInput == 'multiselect')
-                    && !in_array($attributeCode, $this->excludedAttributes)
-                ) {
-                    $value = $this->getValueForAttribute(
-                        $productId,
-                        $attributeCode,
-                        $attributeValue,
-                        $attributeData['is_searchable']
-                    );
-                    $productAttributes[$attributeCode . '_value'] = $this->getValueForAttributeOptions(
-                        $attributeData,
-                        $attributeValue
-                    );
-                } else {
-                    $value = implode(' ', $attributeValue);
-                }
-            }
-        } else {
-            $value = $attributeValue;
-        }
 
-        // cover case with "options"
-        // see \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider::prepareProductIndex
-        if ($value) {
-            if ($attributeId === 'options') {
-                $productAttributes[$attributeId] = $value;
-            } else {
-                if (isset($attributeData[AttributeInterface::OPTIONS][$value])) {
-                    $productAttributes[$attributeCode . '_value'] = $attributeData[AttributeInterface::OPTIONS][$value];
+        $retrievedValue = $this->retrieveFieldValue($attributeValues);
+        if ($retrievedValue) {
+            $productAttributes[$attribute->getAttributeCode()] = $retrievedValue;
+
+            if ($attribute->getIsSearchable()) {
+                $attributeLabels = $this->getValuesLabels($attribute, $attributeValues);
+                $retrievedLabel = $this->retrieveFieldValue($attributeLabels);
+                if ($retrievedLabel) {
+                    $productAttributes[$attribute->getAttributeCode() . '_value'] = $retrievedLabel;
                 }
-                $productAttributes[$attributeCode] = $this->formatProductAttributeValue(
-                    $value,
-                    $attributeData,
-                    $storeId
-                );
             }
         }
+
         return $productAttributes;
     }
 
     /**
-     * Get product attribute data by attribute id
-     *
-     * @param int $attributeId
+     * @param int $productId
+     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
+     * @param array $attributeValues
+     * @param int $storeId
      * @return array
      */
-    private function getAttributeData($attributeId)
+    private function prepareAttributeValues(int $productId, $attribute, array $attributeValues, int $storeId): array
     {
-        if (!array_key_exists($attributeId, $this->attributeData)) {
-            $attribute = $this->dataProvider->getSearchableAttribute($attributeId);
-            if ($attribute) {
-                $options = [];
-                if ($attribute->getFrontendInput() === 'select' || $attribute->getFrontendInput() === 'multiselect') {
-                    foreach ($attribute->getOptions() as $option) {
-                        $options[$option->getValue()] = $option->getLabel();
-                    }
-                }
-                $this->attributeData[$attributeId] = [
-                    AttributeInterface::ATTRIBUTE_CODE => $attribute->getAttributeCode(),
-                    AttributeInterface::FRONTEND_INPUT => $attribute->getFrontendInput(),
-                    AttributeInterface::BACKEND_TYPE => $attribute->getBackendType(),
-                    AttributeInterface::OPTIONS => $options,
-                    'is_searchable' => $attribute->getIsSearchable(),
-                ];
-            } else {
-                $this->attributeData[$attributeId] = null;
+        if (in_array($attribute->getAttributeCode(), $this->attributesExcludedFromMerge, true)) {
+            $attributeValues = [
+                $productId => $attributeValues[$productId] ?? '',
+            ];
+        }
+
+        if ($attribute->getFrontendInput() === 'multiselect') {
+            $attributeValues = $this->prepareMultiselectValues($attributeValues);
+        }
+
+        if ($this->isAttributeDate($attribute)) {
+            foreach ($attributeValues as $key => $attributeValue) {
+                $attributeValues[$key] = $this->dateFieldType->formatDate($storeId, $attributeValue);
             }
         }
 
-        return $this->attributeData[$attributeId];
+        return $attributeValues;
     }
 
     /**
-     * Format product attribute value for search engine
-     *
-     * @param mixed $value
-     * @param array $attributeData
-     * @param string $storeId
-     * @return string
+     * @param array $values
+     * @return array
      */
-    private function formatProductAttributeValue($value, $attributeData, $storeId)
+    private function prepareMultiselectValues(array $values): array
     {
-        if ($attributeData[AttributeInterface::FRONTEND_INPUT] === 'date'
-            || in_array($attributeData[AttributeInterface::BACKEND_TYPE], ['datetime', 'timestamp'])) {
-            return $this->dateFieldType->formatDate($storeId, $value);
-        } elseif ($attributeData[AttributeInterface::FRONTEND_INPUT] === 'multiselect') {
-            return str_replace(',', ' ', $value);
-        } else {
-            return $value;
-        }
+        return \array_merge(...\array_map(function(string $value) {
+            return \explode(',', $value);
+        }, $values));
     }
 
     /**
-     * Return single value if value exists for the productId in array, otherwise return concatenated array values
-     *
-     * @param int $productId
-     * @param string $attributeCode
-     * @param array $attributeValue
-     * @param bool $isSearchable
-     * @return mixed
+     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
+     * @return bool
      */
-    private function getValueForAttribute($productId, $attributeCode, array $attributeValue, $isSearchable)
+    private function isAttributeDate($attribute): bool
     {
-        if ((!$isSearchable || in_array($attributeCode, $this->attributesExcludedFromMerge))
-            && isset($attributeValue[$productId])
-        ) {
-            $value = $attributeValue[$productId];
-        } elseif (in_array($attributeCode, $this->attributesExcludedFromMerge) && !isset($attributeValue[$productId])) {
-            $value = '';
-        } else {
-            $value = implode(' ', $attributeValue);
-        }
-        return $value;
+        return $attribute->getFrontendInput() === 'date'
+            || in_array($attribute->getBackendType(), ['datetime', 'timestamp'], true);
     }
 
     /**
-     * Concatenate select and multiselect attribute values
-     *
-     * @param array $attributeData
-     * @param array $attributeValue
-     * @return string
+     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
+     * @param array $attributeValues
+     * @return array
      */
-    private function getValueForAttributeOptions(array $attributeData, array $attributeValue)
+    private function getValuesLabels($attribute, array $attributeValues): array
     {
-        $result = null;
-        $selectedValues = [];
-        if ($attributeData[AttributeInterface::FRONTEND_INPUT] == 'select') {
-            foreach ($attributeValue as $selectedValue) {
-                if (isset($attributeData[AttributeInterface::OPTIONS][$selectedValue])) {
-                    $selectedValues[] = $attributeData[AttributeInterface::OPTIONS][$selectedValue];
-                }
-            }
-        }
-        if ($attributeData[AttributeInterface::FRONTEND_INPUT] == 'multiselect') {
-            foreach ($attributeValue as $selectedAttributeValues) {
-                $selectedAttributeValues = explode(',', $selectedAttributeValues);
-                foreach ($selectedAttributeValues as $selectedValue) {
-                    if (isset($attributeData[AttributeInterface::OPTIONS][$selectedValue])) {
-                        $selectedValues[] = $attributeData[AttributeInterface::OPTIONS][$selectedValue];
-                    }
-                }
+        $attributeLabels = [];
+        foreach ($attribute->getOptions() as $option) {
+            if (\in_array($option->getValue(), $attributeValues)) {
+                $attributeLabels[] = $option->getLabel();
             }
         }
-        $selectedValues = array_unique($selectedValues);
-        if (!empty($selectedValues)) {
-            $result = implode(' ', $selectedValues);
-        }
-        return $result;
+
+        return $attributeLabels;
+    }
+
+    /**
+     * Retrieve value for field. If field have only one value this method return it.
+     * Otherwise will be returned array of these values.
+     * Note: array of values must have index keys, not as associative array.
+     *
+     * @param array $values
+     * @return array|string
+     */
+    private function retrieveFieldValue(array $values)
+    {
+        $values = \array_filter(\array_unique($values));
+
+        return count($values) === 1 ? \array_shift($values) : \array_values($values);
     }
 }
diff --git a/vendor/magento/module-elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php b/vendor/magento/module-elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php
index d0258286b6..fe526ec63b 100644
--- a/vendor/magento/module-elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php
+++ b/vendor/magento/module-elasticsearch/Model/Adapter/FieldMapper/ProductFieldMapper.php
@@ -115,4 +115,23 @@ class ProductFieldMapper extends Elasticsearch5ProductFieldMapper implements Fie
 
         return $allAttributes;
     }
+
+    /**
+     * @param string $frontendInput
+     * @param string $fieldType
+     * @param string $attributeCode
+     * @return string
+     */
+    protected function getRefinedFieldName($frontendInput, $fieldType, $attributeCode)
+    {
+        switch ($frontendInput) {
+            case 'select':
+            case 'multiselect':
+                return in_array($fieldType, ['string','integer'], true) ? $attributeCode . '_value' : $attributeCode;
+            case 'boolean':
+                return $fieldType === 'integer' ? $attributeCode . '_value' : $attributeCode;
+            default:
+                return $attributeCode;
+        }
+    }
 }
