Have your say in Community Polls: What was/is your greatest motivation to start your own business?
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

SKU returing multiple products with different SKUs on search but required exact match SKU

SKU returing multiple products with different SKUs on search but required exact match SKU

sanakhanbano
Shopify Partner
2 0 1

Hi,

I am searching products with exact SKU matches but multiple products are returned with different SKUs. I want to search only for the exact match SKU. The product title contains SKU. For Example, I am searching for Sku  BBSS0297 but I'm also getting the below SKUs.
BBSS0299 BBSS0237 BBSS0227 BBSS0397

 

I'm getting exact matches only for snowboard testing products by Shopify,

I'm using the default dawn theme with its using predictive search method and also the paid turbo theme.

Kindly help me with this problem.

Screenshot 2023-07-12 014243.pngScreenshot 2023-07-12 014427.pngScreenshot 2023-07-12 014624.png

 

 

Replies 5 (5)

Liam
Community Manager
3108 344 894

Hi Sanakhanbano,

 

This behaviour looks to be happening because Shopify's default search functionality returns results that are similar, but not necessarily exact matches. This is particularly true when it comes to fields like the product title which is full-text searchable.

 

If a SKU, or part of a SKU, is present in a title all partial matching products may be returned in the search results even if the exact SKU does not match. This might be why you're not seeing this behaviour on the demo snowboard products, as they don't have SKUs in their titles. 

 

It should be possible to customize the search form however and add different query parameters to change which kinds of results are returned. The `prefix` query parameter specifies whether we want to perform a partial word match on the last search term, so setting this to `none` would prevent a partial word match on the last search term.

 

These query parameters can be applied to a search form as hidden input and there's an example implementation in our docs here. It's also worth noting that this `prefix` query parameter will affect all searches in your store, not just those for SKUs.

 

Try this out and see if it's working as you expect,

Liam | Developer Advocate @ Shopify 
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution
 - To learn more visit Shopify.dev or the Shopify Web Design and Development Blog

sanakhanbano
Shopify Partner
2 0 1

I have changed the prefix parameter to "none" but still the same problem.

There is clash between search by title and search by sku both are working same time so returning multiple products as title contains sku.

 

This is my main-search.liquid code:

 

 

 

{{ 'template-collection.css' | asset_url | stylesheet_tag }}
{{ 'component-card.css' | asset_url | stylesheet_tag }}
{{ 'component-price.css' | asset_url | stylesheet_tag }}
{{ 'component-search.css' | asset_url | stylesheet_tag }}

{%- if section.settings.enable_filtering or section.settings.enable_sorting -%}
  {{ 'component-facets.css' | asset_url | stylesheet_tag }}
  <script src="{{ 'facets.js' | asset_url }}" defer="defer"></script>
{%- endif -%}

<script src="{{ 'main-search.js' | asset_url }}" defer="defer"></script>

<style>
  .template-search__header {
    margin-bottom: 3rem;
  }

  .template-search__search {
    margin: 0 auto 3.5rem;
    max-width: 74.1rem;
  }

  .template-search__search .search {
    margin-top: 3rem;
  }

  .template-search--empty {
    padding-bottom: 18rem;
  }

  @media screen and (min-width: 750px) {
    .template-search__header {
      margin-bottom: 5rem;
    }
  }

  .search__button .icon {
    height: 1.8rem;
  }
</style>

{%- liquid
  assign sort_by = search.sort_by | default: search.default_sort_by
  assign terms = search.terms | escape
  assign search_url = '?q=' | append: terms | append: '&options%5Bprefix%5D=none&sort_by=' | append: sort_by
-%}

{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top | times: 0.75 | round: 0 }}px;
    padding-bottom: {{ section.settings.padding_bottom | times: 0.75 | round: 0 }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}

{% paginate search.results by 24 %}
  <div class="template-search{% unless search.performed and search.results_count > 0 %} template-search--empty{% endunless %} section-{{ section.id }}-padding">
    <div class="template-search__header page-width">
      <h1 class="h2 center">
        {%- if search.performed -%}
          {{- 'templates.search.title' | t -}}
        {%- else -%}
          {{- 'general.search.search' | t -}}
        {%- endif -%}
      </h1>
      <div class="template-search__search">
        {%- if settings.predictive_search_enabled -%}
          <predictive-search data-loading-text="{{ 'accessibility.loading' | t }}">
        {%- endif -%}
            <main-search>
              <form action="{{ routes.search_url }}" method="get" role="search" class="search">
                <div class="field">
                  <input
                    class="search__input field__input"
                    id="Search-In-Template"
                    type="search"
                    name="q"
                    value="{{ search.terms | escape }}"
                    placeholder="{{ 'general.search.search' | t }}"
                    {%- if settings.predictive_search_enabled -%}
                      role="combobox"
                      aria-expanded="false"
                      aria-owns="predictive-search-results"
                      aria-controls="predictive-search-results"
                      aria-haspopup="listbox"
                      aria-autocomplete="list"
                      autocorrect="off"
                      autocomplete="off"
                      autocapitalize="off"
                      spellcheck="false"
                    {%- endif -%}
                  >
                  <label class="field__label" for="Search-In-Template">{{ 'general.search.search' | t }}</label>
                  <input name="options[prefix]" type="hidden" value="none">

                  {%- if settings.predictive_search_enabled -%}
                    <div class="predictive-search predictive-search--search-template" tabindex="-1" data-predictive-search>
                      <div class="predictive-search__loading-state">
                        <svg aria-hidden="true" focusable="false" class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
                          <circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
                        </svg>
                      </div>
                    </div>

                    <span class="predictive-search-status visually-hidden" role="status" aria-hidden="true"></span>
                  {%- endif -%}

                  <button type="reset" class="reset__button field__button{% if search.terms == blank %} hidden{% endif %}" aria-label="{{ 'general.search.reset' | t }}">
                    <svg class="icon icon-close" aria-hidden="true" focusable="false">
                      <use xlink:href="#icon-reset">
                    </svg>
                  </button>
                  <button type="submit" class="search__button field__button" aria-label="{{ 'general.search.search' | t }}">
                    <svg class="icon icon-search" aria-hidden="true" focusable="false">
                      <use xlink:href="#icon-search">
                    </svg>
                  </button>
                </div>
              </form>
            </main-search>
        {%- if settings.predictive_search_enabled -%}
          </predictive-search>
        {%- endif -%}

      </div>
      {%- if search.performed -%}
        {%- unless section.settings.enable_filtering or section.settings.enable_sorting -%}
          {%- if search.results_count > 0 -%}
            <p role="status">{{ 'templates.search.results_with_count_and_term' | t: terms: search.terms, count: search.results_count }}</p>
          {%- endif -%}
        {%- endunless -%}
        {%- if search.results_count == 0 and search.filters == empty -%}
          <p role="status">{{ 'templates.search.no_results' | t: terms: search.terms }}</p>
        {%- endif -%}
      {%- endif -%}
    </div>
    {%- if search.performed -%}
      {%- if section.settings.enable_sorting and section.settings.filter_type == 'vertical' and search.filters != empty -%}
        <facet-filters-form class="facets facets-vertical-sort page-width small-hide no-js-hidden">
          <form class="facets-vertical-form" id="FacetSortForm">
            <div class="facet-filters sorting caption">
              <div class="facet-filters__field">
                <h2 class="facet-filters__label caption-large text-body">
                  <label for="SortBy">{{ 'products.facets.sort_by_label' | t }}</label>
                </h2>
                <div class="select">
                  {%- assign sort_by = search.sort_by | default: search.default_sort_by -%}
                  <select name="sort_by" class="facet-filters__sort select__select caption-large" id="SortBy" aria-describedby="a11y-refresh-page-message">
                    {%- for option in search.sort_options -%}
                      <option value="{{ option.value | escape }}"{% if option.value == sort_by %} selected="selected"{% endif %}>{{ option.name | escape }}</option>
                    {%- endfor -%}
                  </select>
                  {% render 'icon-caret' %}
                </div>
              </div>
              <noscript>
                <button type="submit" class="facets__button-no-js button button--secondary">{{ 'products.facets.sort_button' | t }}</button>
              </noscript>
            </div>

            <div class="product-count-vertical light" role="status">
              <h2 class="product-count__text text-body">
                <span id="ProductCountDesktop">
                  {%- if search.results_count -%}
                    {{ 'templates.search.results_with_count' | t: terms: search.terms, count: search.results_count }}
                  {%- elsif search.products_count == search.all_products_count -%}
                    {{ 'products.facets.product_count_simple' | t: count: search.products_count }}
                  {%- else -%}
                    {{ 'products.facets.product_count' | t: product_count: search.products_count, count: search.all_products_count }}
                  {%- endif -%}
                </span>
              </h2>
              <div class="loading-overlay__spinner">
                <svg aria-hidden="true" focusable="false" class="spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
                  <circle class="path" fill="none" stroke-width="6" cx="33" cy="33" r="30"></circle>
                </svg>
              </div>
            </div>
          </form>
        </facet-filters-form>
      {%- endif -%}
      <div{% if section.settings.filter_type == 'vertical' %} class="facets-vertical page-width"{% endif %}>
        {%- if search.filters != empty -%}
          {%- if section.settings.enable_filtering or section.settings.enable_sorting -%}
            <aside aria-labelledby="verticalTitle" class="facets-wrapper{% unless section.settings.enable_filtering %} facets-wrapper--no-filters{% endunless %}{% if section.settings.filter_type != 'vertical' %} page-width{% endif %}" id="main-search-filters" data-id="{{ section.id }}">
              {% render 'facets', results: search, enable_filtering: section.settings.enable_filtering, enable_sorting: section.settings.enable_sorting, filter_type: section.settings.filter_type, paginate: paginate %}
            </aside>
          {%- endif -%}
        {%- endif -%}
        <div class="product-grid-container" id="ProductGridContainer">
          {%- if search.results.size == 0 and search.filters != empty -%}
            <div class="template-search__results collection collection--empty{% if section.settings.filter_type != 'vertical' %} page-width{% endif %}" id="product-grid" data-id="{{ section.id }}">
              <div class="loading-overlay gradient"></div>
              <div class="title-wrapper center">
                <h2 class="title title--primary">
                  {{ 'sections.collection_template.empty' | t }}<br>
                  {{ 'sections.collection_template.use_fewer_filters_html' | t: link: search_url, class: "underlined-link link" }}
                </h2>
              </div>
            </div>
          {%- else -%}
            <div class="template-search__results collection{% if section.settings.filter_type != 'vertical' %} page-width{% endif %}" id="product-grid" data-id="{{ section.id }}">
              <div class="loading-overlay gradient"></div>
              <ul class="grid product-grid  grid--{{ section.settings.columns_mobile }}-col-tablet-down grid--{{ section.settings.columns_desktop }}-col-desktop" role="list">
                {%- for item in search.results -%}
                  {% assign lazy_load = false %}
                  {%- if forloop.index > 2 -%}
                    {%- assign lazy_load = true -%}
                  {%- endif -%}

                  <li class="grid__item">
                    {%- case item.object_type -%}
                      {%- when 'product' -%}
                        {%- capture product_settings -%}{%- if section.settings.product_show_vendor -%}vendor,{%- endif -%}title,price{%- endcapture -%}
                        {% render 'card-product',
                          card_product: item,
                          media_aspect_ratio: section.settings.image_ratio,
                          show_secondary_image: section.settings.show_secondary_image,
                          show_vendor: section.settings.show_vendor,
                          show_rating: section.settings.show_rating,
                          lazy_load: lazy_load
                        %}
                      {%- when 'article' -%}
                        {% render 'article-card',
                          article: item,
                          show_image: true,
                          show_date: section.settings.article_show_date,
                          show_author: section.settings.article_show_author,
                          show_badge: true,
                          media_aspect_ratio: 1,
                          lazy_load: lazy_load
                        %}
                      {%- when 'page' -%}
                        <div class="article-card-wrapper card-wrapper underline-links-hover">
                          <div class="card card--card card--text ratio color-{{ settings.blog_card_color_scheme }}" style="--ratio-percent: 100%;">
                            <div class="card__content">
                              <div class="card__information">
                                <h3 class="card__heading">
                                  <a href="{{ item.url }}" class="full-unstyled-link">
                                    {{ item.title | truncate: 50 | escape }}
                                  </a>
                                </h3>
                              </div>
                              <div class="card__badge {{ settings.badge_position }}">
                                <span class="badge color-background-1">{{ 'templates.search.page' | t }}</span>
                              </div>
                            </div>
                          </div>
                        </div>
                    {%- endcase -%}
                  </li>
                {%- endfor -%}
              </ul>
              {%- if paginate.pages > 1 -%}
                {% render 'pagination', paginate: paginate %}
              {%- endif -%}
            </div>
          {%- endif -%}
        </div>
      </div>
    {%- endif -%}
  </div>
{% endpaginate %}

{% schema %}
{
  "name": "t:sections.main-search.name",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "range",
      "id": "columns_desktop",
      "min": 1,
      "max": 5,
      "step": 1,
      "default": 4,
      "label": "t:sections.main-search.settings.columns_desktop.label"
    },
    {
      "type": "header",
      "content": "t:sections.main-search.settings.header__1.content"
    },
    {
      "type": "select",
      "id": "image_ratio",
      "options": [
        {
          "value": "adapt",
          "label": "t:sections.main-search.settings.image_ratio.options__1.label"
        },
        {
          "value": "portrait",
          "label": "t:sections.main-search.settings.image_ratio.options__2.label"
        },
        {
          "value": "square",
          "label": "t:sections.main-search.settings.image_ratio.options__3.label"
        }
      ],
      "default": "adapt",
      "label": "t:sections.main-search.settings.image_ratio.label"
    },
    {
      "type": "checkbox",
      "id": "show_secondary_image",
      "default": false,
      "label": "t:sections.main-search.settings.show_secondary_image.label"
    },
    {
      "type": "checkbox",
      "id": "show_vendor",
      "default": false,
      "label": "t:sections.main-search.settings.show_vendor.label"
    },
    {
      "type": "checkbox",
      "id": "show_rating",
      "default": false,
      "label": "t:sections.main-search.settings.show_rating.label",
      "info": "t:sections.main-search.settings.show_rating.info"
    },
    {
      "type": "header",
      "content": "t:sections.main-collection-product-grid.settings.header__1.content"
    },
    {
      "type": "checkbox",
      "id": "enable_filtering",
      "default": true,
      "label": "t:sections.main-collection-product-grid.settings.enable_filtering.label",
      "info": "t:sections.main-collection-product-grid.settings.enable_filtering.info"
    },
    {
      "type": "select",
      "id": "filter_type",
      "options": [
        {
          "value": "horizontal",
          "label": "t:sections.main-collection-product-grid.settings.filter_type.options__1.label"
        },
        {
          "value": "vertical",
          "label": "t:sections.main-collection-product-grid.settings.filter_type.options__2.label"
        },
        {
          "value": "drawer",
          "label": "t:sections.main-collection-product-grid.settings.filter_type.options__3.label"
        }
      ],
      "default": "horizontal",
      "label": "t:sections.main-collection-product-grid.settings.filter_type.label",
      "info": "t:sections.main-collection-product-grid.settings.filter_type.info"
    },
    {
      "type": "checkbox",
      "id": "enable_sorting",
      "default": true,
      "label": "t:sections.main-collection-product-grid.settings.enable_sorting.label"
    },
    {
      "type": "header",
      "content": "t:sections.main-search.settings.header__2.content",
      "info": "t:sections.main-search.settings.header__2.info"
    },
    {
      "type": "checkbox",
      "id": "article_show_date",
      "default": true,
      "label": "t:sections.main-search.settings.article_show_date.label"
    },
    {
      "type": "checkbox",
      "id": "article_show_author",
      "default": false,
      "label": "t:sections.main-search.settings.article_show_author.label"
    },
    {
      "type": "header",
      "content": "t:sections.main-search.settings.header_mobile.content"
    },
    {
      "type": "select",
      "id": "columns_mobile",
      "default": "2",
      "label": "t:sections.main-search.settings.columns_mobile.label",
      "options": [
        {
          "value": "1",
          "label": "t:sections.main-search.settings.columns_mobile.options__1.label"
        },
        {
          "value": "2",
          "label": "t:sections.main-search.settings.columns_mobile.options__2.label"
        }
      ]
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 36
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 36
    }
  ]
}
{% endschema %}

 

 

 

gcaprio
Shopify Partner
3 0 1

Thanks @Liam but I'm running into a similar issue: I'm searching for specific SKUs and partial matches are coming back. For example, I have products with the following SKUs:

 

NS721

NS721-NS770

 

this query string:

type=product&options[prefix]=none&q=variants.sku:NS721

Still returns both products, not just the first one as expected. I can't figure out how to limit the query to exact matches only. Is this not possible?

Liquify
Shopify Partner
98 1 59

yeah - this is a limitation in shopify native search 
it would be nice common sense if the exact match on sku was first in results
but it usually isn't unless by accident

you can build just a sku only search easy enough
query would look something like this for returning exact match on sku

 

/search?type=product&options%5Bprefix%5D=last&options%5Bunavailable_products%5D=last&searchOptions=default&q=variants.sku%3AKC101C


but most people want a single search bar for everything 
probably best to explore apps
there you have much more control over results
for example with Boost you can define which fields are searchable and set weighting

with native search you either need to have a dropdown to limit search scope to e.g. sku or have a separate search bar. at least that's how I see it.

★★★ Need help setting up your Shopify store? Hire me here: https://liquify.design ★★
gcaprio
Shopify Partner
3 0 1

Sadly the above won't work: it still searches and returns fuzzy and partial matches, even if you surround the SKU in ""'s.

 

I was able to get around this issue using a combination of the search & discovery app and the filter url params. Check out how those parameters are built on a collection page and you can probably get something working.