Search by

magenxcommerce / module-quick-search-graph-ql

magenxcommerce

Quick-search dropdown suggestions for the headless storefront: the store's popular search terms and admin-managed sponsored items (products, categories, brands) with keyword targeting and date windows, served by one quickSearchSuggestions GraphQL query. Replaces magenxcommerce/module-popular-search-

Package info

github.com/magenxcommerce/module-quick-search-graph-ql

Type:magento2-module

pkg:composer/magenxcommerce/module-quick-search-graph-ql

Statistics

Installs: 0

Dependents: 2

Suggesters: 0

Stars: 1

Open Issues: 0

v1.2.0 2026-10-05 12:26 UTC

This package is auto-updated.

Last update: 2026-10-24 19:33:09 UTC


README

Built with AI assistance. This module is built with significant use of Claude Code, under active human review and guidance. It is open source and in active development. AI assistance does not lower the engineering bar: every change must meet the same correctness, security, documentation and maintainability requirements as any other contribution.

No warranty. We are not responsible for any incompatibility. By installing this software, you accept responsibility for its security, upgrades, patches and monitoring.

Everything the headless storefront's quick-search dropdown shows before search results come in, served by one GraphQL query:

  • Popular search terms. These are the store's most searched phrases, from Magento's search_query log. They render as "Top searches" when the box opens. This part used to be Magenx_PopularSearchGraphQl, which this module replaces (see Migrating).
  • Sponsored items (promotions). Merchants add products, categories or brands in an admin grid:
    • With an empty search box, general promotions (no keywords) show in the dropdown's top area.
    • After a term is typed, promotions whose keywords match it, plus the general ones, show below the product results.

Same discipline as the other Magenx companions: GraphQL plus admin only, no Luma templates, no core module touched.

Schema

{
  quickSearchSuggestions(popularTermsCount: Int) {
    popular_terms { query_text num_results popularity }
    promotions {
      id
      type               # PRODUCT | CATEGORY | BRAND | LINK
      title              # admin override, else product / category name or brand label
      image_url          # admin override, else the category image / brand image swatch; null if none
      keywords           # lower-cased; [] = general
      product { uid name url_key image { url label } price_range { … } }  # PRODUCT
      category_url_path  # CATEGORY
      brand_label        # BRAND (manufacturer option label)
      link_url           # LINK (storefront path or http(s) URL)
    }
  }
}
  • The store view comes from the Store header.
  • popular_terms and promotions have their own resolvers, so a client that selects only one of them never pays for the other.
  • A disabled part returns [], never an error.

Popular terms

  • Terms must have is_active = 1, num_results > 0 and display_in_terms = 1. The last one is the "Display in Suggested Terms" toggle on Marketing > SEO & Search > Search Terms. Terms are scoped to the store view and ordered by popularity DESC.
  • Approval (on by default). Shoppers create and rank search_query rows just by searching, so anyone could otherwise push a phrase into every visitor's "Top searches" by repeating it. With Show Only Approved Terms on, a term must also be approved (search_query.magenx_top_search = 1, added by this module). Approve or remove terms with the Approve for Top Searches / Remove from Top Searches mass actions on the stock Search Terms grid; its Top Searches column shows the state. These run under the grid's own ACL, Magento_Search::search, and take effect at once.
  • popularTermsCount is capped at the configured count. When it is omitted, the configured count is used.
  • One indexed select on search_query, cached for an hour per store, count and approval setting.
  • The table fills as shoppers search. Headless GraphQL searches are logged through the same Magento\Search\Model\Query path, so a new store shows nothing until searches build up or terms are added in the admin.

Promotions

  • A promotion is either for one store view or for all of them (store_id 0).
  • Only enabled promotions inside their date window are returned. The window uses the store's local date, and active_to is inclusive. Results come in sort_order, capped at Maximum Items.
  • A promotion whose target no longer resolves is dropped silently:
    • a product that is disabled, not visible, out of stock or not in the store's website
    • a category that is inactive or outside the store's root
    • a brand option that was deleted
    • a link whose target is not a / path or an http(s) URL
  • The promotion rows are cached per store view in the Magento cache, tagged magenx_quick_search_promotion. Saving or deleting a promotion cleans the tag. The date window is applied on read, so start and end dates need no cache flush.
  • Targets are resolved in bulk: at most one product collection, one category collection and one manufacturer-option lookup per call, whatever the pool size. The core ProductInterface resolvers price the products for the request's customer group.
  • Keyword matching happens in the storefront. The whole pool is fetched once per dropdown open, so typing costs no extra request.

Admin

  • Marketing > SEO & Search > Quick Search Promotions: the grid with add, edit, delete and mass delete actions. ACL Magenx_QuickSearchGraphQl::promotion.
    • Type / Target: Product = SKU, Link = a storefront path such as /deals or a CMS page /summer-sale, or a full http(s) URL. A link needs a title and an image. Category and Brand are picked from selects that the Type switches to: categories in tree order as "Women / Tops (#45)", grouped by store root, and manufacturer options as "Label (#id)", both with admin names. The selects are loaded only when the edit form opens. SKUs stay a text field, because a product list would not scale to large catalogs. The grid shows category and brand targets as "Name (#id)", looked up once per page of rows. On save:
      • the SKU, the category (below a store root) or the brand option must exist;
      • a link must not use another scheme or //host, and must not point at an executable or installer (.exe, .msi, .apk, .dmg, .sh, macro-enabled Office files, ...). That check runs on read too. It can only judge the file name: an extension-less URL that serves a download is not detected.
    • Title, Image: optional overrides. The image must be a JPG, PNG, GIF, WebP or AVIF file of up to 5 MB in pub/media, given as its path or its full media URL (stored as the path). On save the file must exist and its content must be an image of the type its extension says. SVG is refused. The storefront's image loader serves everything through its /media proxy, so images hosted elsewhere cannot work.
    • Title and Target are capped at 255 characters, Image at 512 and Keywords at 2,000.
    • Keywords: comma-separated. Empty means general. A promotion matches a search when the term contains one of its keywords, or a keyword starts with the term (so "sams" already matches "samsung").
    • Store View, Active From / To, Sort Order.
  • Stores > Configuration > Magenx > Quick Search
    • Popular Search Terms: magenx_popular_search/general/{enabled,count}, defaults on / 8. These are the old module's paths, kept on purpose. Show Only Approved Terms: magenx_popular_search/general/require_approval, default on.
    • Promotions: magenx_quick_search/promotions/{enabled,max_items}, defaults on / 10. On by default because nothing shows until a promotion exists.
  • Popular terms are curated on the stock Search Terms grid: approve terms for top searches with the mass action, and delete junk terms or untick "Display in Suggested Terms" to hide one.

Data

  • magenx_quick_search_promotion holds the promotions. Its (store_id, is_active, sort_order) index serves the cache-miss read and backs the store_id foreign key. Deleting a store view cascades.
  • Popular terms use the stock search_query table. This module adds one column to it, magenx_top_search (the approval flag), and a (store_id, magenx_top_search, popularity) index for the approved-terms read.

Deploy

bin/magento module:enable Magenx_QuickSearchGraphQl
bin/magento setup:upgrade

The storefront needs no particular deploy order: nothing is added to StoreConfig. The storefront calls quickSearchSuggestions only when the dropdown opens and fails soft, so a storefront deployed before this module shows no top searches or promotions and nothing else breaks.

Upgrading to approval. Approval is on by default. On upgrade, a data patch approves the terms each store view showed at that moment, so the storefront doesn't change. Only terms that would newly enter the list wait for approval.

Migrating from Magenx_PopularSearchGraphQl

  • Settings carry over. The popular-terms settings are read from the old magenx_popular_search/* paths, so nothing needs to be migrated.

  • Order matters. The storefront release that switches to quickSearchSuggestions also stops selecting the old module's popular_search_enabled / popular_search_count StoreConfig fields. Remove the old module only after that storefront release is live. An older storefront still selects those fields, and without the old module Magento rejects its whole GetStoreConfig query.

  • Both modules can run side by side. This module declares its own type and field names (quickSearchSuggestions, QuickSearchPopularTerm), so there is no schema clash.

  • Removing the old module, once the new storefront is live:

    bin/magento module:disable Magenx_PopularSearchGraphQl
    composer remove magenxcommerce/module-popular-search-graph-ql
    bin/magento setup:upgrade
    

Storefront wiring

QUICK_SEARCH_SUGGESTIONS (engine features/catalog/queries.ts) is run by the browser and is allowlisted in /api/graphql. The header search panels run it once, the first time the dropdown opens, never on page load. It selects the lean product fields the dropdown renders. No Turnstile: it is read-only, cheap, returns admin-curated public data and enumerates nothing.