0

How to prevent bundle products showing up in catalog search results where the bundle product itself does not match the search query?

Running Magento 2.3.7, have observed the following behavior regardless of whether elasticsearch (v7) or mysql is the search engine.

In product attributes, we have set only the product SKU, name, and short description to be searchable. (This question still stands if there were other attributes we wanted to be indexed in the search engine, but for purposes of having a concrete example, we have only these 3 attributes.)

For simple products, this seems to work. On the storefront I can search the catalog for a string, and various simple products with that string in their SKU, name, or short description appear in the results.

However, things get messy with bundle products.

Our store uses a lot of bundle products. We expect / want that bundle products appear in search results based on the same criteria as simple products. That is, they would only appear if the search query is relevant to one of their attributes that is "searchable" as per our configuration of the catalog product attributes. (In our case, the SKU, name, or short description.)

What happens is that all the "child" products of a bundle (every possible selection of every bundle option) gets included as part of the data for the bundle product that. This is a problem because our customers can enter a very specific search query that exactly matches a product's name, but the search results will be cluttered with many bundles that include that product as an option, and customers thus cannot find the product they are looking for even by searching the exact product name.

Example: Let's say I have a product called XYZ. It is included in many different bundle products as an option/selection. Let's say, the bundle products are called ABC, DEF, GHI. And each of these bundle products contains various simple products as the bundle options, but they all have XYZ in common. When a user does a catalog search for "XYZ", the results will include ABC, DEF, GHI, and XYZ. Even though the bundle products themselves do not contain the string "XYZ" anywhere. And if there are enough bundle products that include XYZ as an option, then the product XYZ gets drowned out by all the bundle products in the search results, thus the user cannot easily find the product they are searching for.

I understand why this might be the default behavior of Magento. But how can I change this? For search purposes, is there a way to ignore the relationship between bundle products and their child products?

What has been tried:

In vendor/magento/module-catalog-search/Model/ResourceModel/Fulltext.php

There is the function:

    /**
     * Retrieve product relations by children.
     *
     * @param int|array $childIds
     * @return array
     * @since 100.2.0
     */
    public function getRelationsByChild($childIds)
    {
        $connection = $this->getConnection();
        $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
        $select = $connection
            ->select()
            ->from(
                ['relation' => $this->getTable('catalog_product_relation')],
                []
            )->distinct(true)
            ->join(
                ['cpe' => $this->getTable('catalog_product_entity')],
                'cpe.' . $linkField . ' = relation.parent_id',
                ['cpe.entity_id']
            )->where(
                'relation.child_id IN (?)',
                $childIds
            );

        return $connection->fetchCol($select);
    }

I tried modifying this function so it simply returns an empty list every time, but this doesn't seem to have an effect.

1 Answer 1

0

The file with the relevant code is:

vendor/magento/module-catalog-search/Model/Indexer/Fulltext/Action/DataProvider.php

Specifically, the function below:

    /**
     * Return all product children ids
     *
     * @param int $productId Product Entity Id
     * @param string $typeId Super Product Link Type
     * @return array|null
     * @since 100.0.3
     */
    public function getProductChildIds($productId, $typeId)
    {
        $typeInstance = $this->getProductTypeInstance($typeId);
        $relation = $typeInstance->isComposite($this->getProductEmulator($typeId))
            ? $typeInstance->getRelationInfo()
            : false;

        if ($relation && $relation->getTable() && $relation->getParentFieldName() && $relation->getChildFieldName()) {
            $select = $this->connection->select()->from(
                ['main' => $this->getTable($relation->getTable())],
                [$relation->getChildFieldName()]
            );
            $select->join(
                ['e' => $this->resource->getTableName('catalog_product_entity')],
                'e.' . $this->metadata->getLinkField() . ' = main.' . $relation->getParentFieldName()
            )->where(
                'e.entity_id = ?',
                $productId
            );

            if ($relation->getWhere() !== null) {
                $select->where($relation->getWhere());
            }
            return $this->connection->fetchCol($select);
        }

        return null;
    }

This is where Magento's logic for indexing bundle products based on their children's attributes is implemented. We can make a plugin or do a rewrite to replace this function with whatever we need. In our case, we may simply want this function to return null every time, thereby ensuring that the index for fulltext search results is built without combining child info into a parent bundle product's search index data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.