Skip to content
intermediate Phase 70 · Search System

Custom Search — Customization and Relevance Tuning

Implementing custom search in Magento 2: custom search functionality, search customization, relevance tuning, and advanced search features

1h
1 problems
Topic Progress 0%

Custom Search Implementation

Custom Search Controller

namespace Vendor\Module\Controller\Search;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Search\Model\QueryFactory;

class Advanced extends Action
{
    public function __construct(
        Context $context,
        private QueryFactory $queryFactory
    ) {
        parent::__construct($context);
    }

    public function execute()
    {
        $query = $this->queryFactory->create();
        $query->setSearchParam('q', $this->getRequest()->getParam('q'));
        $query->setSearchParam('category_id', $this->getRequest()->getParam('category'));
        
        $this->loadLayout();
        $this->getLayout()->getChildBlock('search.results')
            ->setQuery($query);
        $this->renderLayout();
    }
}

Custom Search Block

namespace Vendor\Module\Block\Search;

use Magento\Search\Block\Result;

class CustomResult extends Result
{
    public function getSearchResults(): array
    {
        $results = parent::getSearchResults();
        
        // Add custom ranking
        return $this->rankResults($results);
    }

    private function rankResults(array $results): array
    {
        usort($results, function ($a, $b) {
            return $b->getRelevance() - $a->getRelevance();
        });
        return $results;
    }
}

Custom Search Collection

namespace Vendor\Module\Model\Search;

class CustomCollection extends \Magento\Search\Model\ResourceModel\Fulltext\Collection
{
    public function addSearchFilter($query)
    {
        // Custom search logic
        parent::addSearchFilter($query);
        
        // Add relevance scoring
        $this->getSelect()->order('relevance DESC');
        
        return $this;
    }
}

Search Customization

Custom Search Result Display

// Template: search/results.phtml
<?php foreach ($block->getSearchResults() as $result): ?>
<div class="search-result">
    <h3><a href="<?php echo $result->getProductUrl(); ?>">
        <?php echo $result->getName(); ?>
    </a></h3>
    <p class="price"><?php echo $result->getPrice(); ?></p>
    <p class="relevance">Relevance: <?php echo $result->getRelevance(); ?></p>
    <?php if ($result->getCustomAttribute()): ?>
        <p class="custom"><?php echo $result->getCustomAttribute(); ?></p>
    <?php endif; ?>
</div>
<?php endforeach; ?>

Search Filters

// Add custom filters to search
public function addFilters(array $filters)
{
    foreach ($filters as $attribute => $value) {
        $this->getSelect()->where(
            $this->getResource()->getAttribute($attribute)->getAttributeCode() . ' = ?',
            $value
        );
    }
}

Search Sorting

// Custom sort options
public function addSortOrder($sortOrder)
{
    switch ($sortOrder) {
        case 'relevance':
            $this->getSelect()->order('relevance DESC');
            break;
        case 'price_asc':
            $this->getSelect()->order('price ASC');
            break;
        case 'price_desc':
            $this->getSelect()->order('price DESC');
            break;
        case 'name':
            $this->getSelect()->order('name ASC');
            break;
    }
}

Relevance Tuning

Relevance Scoring

// Custom relevance calculation
public function calculateRelevance($product, $query)
{
    $score = 0;
    
    // Exact name match: highest weight
    if (stripos($product->getName(), $query) !== false) {
        $score += 100;
    }
    
    // SKU match
    if (stripos($product->getSku(), $query) !== false) {
        $score += 90;
    }
    
    // Description match
    if (stripos($product->getDescription(), $query) !== false) {
        $score += 50;
    }
    
    // Boost in-stock products
    if ($product->isSalable()) {
        $score += 20;
    }
    
    // Boost by sales
    $score += $product->getSalesCount() / 10;
    
    return $score;
}

Attribute Weight Configuration

// Attribute search weights
'catalog/search' => [
    'weight' => [
        'name' => 10,
        'sku' => 9,
        'description' => 5,
        'short_description' => 4,
        'custom_attribute' => 3
    ]
]

Elasticsearch Relevance

{
  "query": {
    "multi_match": {
      "query": "laptop",
      "fields": ["name^10", "sku^9", "description^5"]
    }
  }
}

Boosting Strategies

  1. Exact match boost — exact term matches get highest score
  2. Field weight — name > SKU > description
  3. Stock boost — in-stock products ranked higher
  4. Sales boost — popular products ranked higher
  5. Recency boost — newer products ranked higher

Advanced Search Features

Faceted Search

// Add facets to search results
public function addFacets()
{
    $this->getSelect()->group('category_id');
    
    // Category facet
    $this->getSelect()->columns([
        'category_count' => new \Zend_Db_Expr('COUNT(DISTINCT category_id)')
    ]);
}

Search Analytics

// Track search terms
namespace Vendor\Module\Service;

class SearchAnalytics
{
    public function trackSearch(string $query, int $resultCount): void
    {
        $this->resource->getConnection()->insert('search_log', [
            'query' => $query,
            'result_count' => $resultCount,
            'created_at' => date('Y-m-d H:i:s')
        ]);
    }
}

Search API

// REST API for custom search
// GET /rest/V1/search?searchCriteria[request_name]=quick_search&searchCriteria[filterGroups][0][filters][0][field]=q&searchCriteria[filterGroups][0][filters][0][value]=laptop

// GraphQL search
query {
  products(search: "laptop") {
    items {
      name
      sku
      price { regularPrice { amount { value } } }
    }
    total_count
  }
}

Best Practices

  1. Tune relevance weights based on analytics
  2. Use synonyms for common misspellings
  3. Track zero-result searches for improvement
  4. Implement search suggestions
  5. A/B test relevance changes

Practice Problems

0 / 1 solved
Search Relevance Tuning

Users report irrelevant search results. Analyze and improve search relevance scoring.

Quiz

1. What gives highest relevance in search scoring?

Question 1 options

2. How do you customize search result ranking?

Question 2 options

3. What is faceted search?

Question 3 options

4. Why track zero-result searches?

Question 4 options

Flashcards

Question

What gives highest search relevance?

Answer

Exact name match, followed by SKU, then description

Question

What is faceted search?

Answer

Search with category/attribute filters for refined results

Question

How to track search analytics?

Answer

Log search terms, result counts, and zero-result searches

Question

What is relevance tuning?

Answer

Adjusting attribute weights and boost factors to improve search results

Question

How to improve search with synonyms?

Answer

Map related terms together so laptop also finds notebook results

Revision Notes

Key Takeaways

  • 1. Custom search uses controllers, blocks, and collections
  • 2. Relevance scoring uses attribute weights and boost factors
  • 3. Exact name match gets highest relevance score
  • 4. Faceted search enables category/attribute filtering
  • 5. Track search analytics to identify improvement opportunities
  • 6. Use synonyms to expand search coverage

Interview Tips

  • Explain how to implement custom search functionality
  • Describe relevance scoring and tuning strategies
  • Discuss faceted search and filtering
  • Know how to use search analytics for improvement

Cheat Sheet

Custom Search Cheat Sheet

Relevance scoring:

  • Exact name: 100
  • SKU: 90
  • Description: 50
  • In-stock: +20
  • Sales: +sales/10

Faceted search:

  • Category filter
  • Attribute filter
  • Price range

Analytics:

  • Track search terms
  • Monitor zero-results
  • Analyze click-through

Improvement:

  • Tune attribute weights
  • Add synonyms
  • A/B test changes
  • Review analytics