Skip to content

Limits and guarantees

This is the page to read before buying. It states what Product Search promises and where each promise stops. The limits get more space, because they are what costs money to discover late.

At a glance

If you are asking The short answer
Will it put the best match first? No. It changes which products come back, not their order. Your pins lead, then OpenCart's own order. More
Will my results change the day I switch it on? On OpenCart 4.1.x, yes: every word has to match, so results get narrower. On 4.0.2.0 nothing narrows. More
Does searching a top-level category find its subcategories' products? No. Only the categories a product is filed under directly are matched. More
Does it add autocomplete, filters or "did you mean"? No. There is no storefront interface at all, and a corrected spelling is never announced. More
Do my pins show when a shopper sorts by price? No. Pins apply only under the default sort. More
Does a misspelling get corrected when the search found something? No. Correction runs only when a search found nothing at all, and never on a product code. More
Does the report record who searched? No. Daily totals per keyword, with no IP address, customer or session, and no setting that adds one. More
Can each store in a multi-store install have its own synonyms? No. Synonym groups and rules belong to a language, and every store shares them. More
Can another system read what shoppers searched for? Yes, once you switch the API on: the same daily totals the report shows, one row per keyword, store, language, category and day. Never who searched, and never your synonyms or pins. More
Does uninstalling delete my synonyms, rules and counters? No. The four tables are never dropped. More

The one guarantee

Product Search finds more of the right products. It does not put them in a better order.

Every word a shopper types has to match something, brand and department names count as things to match against, synonyms you author widen what a word means, a misspelling is corrected against your own catalogue, and the products you pin lead the page. What comes back is a better set of results. Within that set the order is OpenCart's own (whatever the shopper's sort control says, and sort_order when they have not touched it), with your pinned products in front of it.

There is no relevance weighting. Nothing scores a product for matching in the name rather than in a category name, or for matching twice.

Results are narrower on OpenCart 4.1.x

OpenCart is not consistent here. On 4.0.2.0 a two-word search requires both words; on 4.1.0.3 it requires either. Product Search requires both at every release.

On a 4.1.x store that is a visible change on the day you switch it on: red wool jumper stops returning every red thing in the shop. You get fewer results, and more searches that come back with nothing, which is the intended trade. Typo tolerance rescues the ones that failed on a spelling, and the search report shows you the rest so you can author a synonym group or pin something.

On a 4.0.2.0 store nothing narrows, because core already required every word.

What it does not touch

There is no storefront user interface. No autocomplete dropdown, no suggestions, no faceted filtering, no template edits, no JavaScript added to your theme. The results page is the one your theme already has, and the search box is the one your theme already has. Everything this extension does happens between the search box and the results. Facets on search results come from Advanced Product Filters, a separate extension, when it is installed; Product Search only provides the hook it uses.

The shopper is never told their spelling was corrected. There is no "did you mean" and no "showing results for". They type labtop, the box still says labtop, and laptops appear.

Product prices, stock and visibility are OpenCart's. A replaced search applies the same customer group, store, language, status and date-available rules core applies, and returns rows in the shape core returns them, so discounts and specials render exactly as they did.

Which fields are matched

The product name, the product tags, the manufacturer's name, and the names of the categories the product is filed under. The description is matched too, but only when the shopper ticks OpenCart's own search in product descriptions box, and then as the whole phrase rather than word by word.

Directly assigned categories only. A product filed solely under Electronics › Laptops is not found by a search for electronics. Matching parent categories would make one top-level name match most of the catalogue, which is the flood that requiring every word exists to prevent. Where you want a department name to work anyway, use a synonym group or a merchandising rule; both state explicitly what a parent match would have guessed.

Product codes are matched whole, never word by word and never fuzzily. The model number relaxes to a partial match, so XR20 finds XR2000; the other codes keep the behaviour OpenCart gave them where they are stored: matched exactly in the columns, matched from the start in the identifier table. A code is never corrected for spelling. XR2000 and XR2001 are one edit apart and are different products, and somebody typing a code is reading it off a box.

Codes are searched wherever your store keeps them. OpenCart moved SKU, UPC, EAN, JAN, ISBN and MPN out of the product row and into a table of their own in 4.1.0.1, and finished the move in 4.1.0.4. In between, a store has both: the old columns still hold whatever was there the day you upgraded, and OpenCart's own search stopped reading them, so a product nobody has re-saved since can have a SKU your storefront no longer finds. Product Search asks your database which of the two your store has and searches both where both are there, so upgrading does not silently cost you a code.

No accent folding. creme does not match crème. It cannot be added later without silently breaking every synonym group and rule already authored, and there is no folding that is right for every language: German wants ö as oe, French wants it as o. A synonym group is the owner-controlled answer.

Synonym groups

  • A group belongs to one language, not to one store. A multi-store install shares its vocabulary, which is nearly always what you want and is not configurable.
  • A group holds between 2 and 20 terms. One term is not a group (it expands to itself and changes no search), and a group of two hundred terms is a query no storefront wants to run.
  • Terms are stored normalised: trimmed, whitespace collapsed, lower-cased. What you typed and what is stored can differ in case and spacing.
  • A term belongs to one group per language. Saving the same term in a second group of that language is refused, naming the group it is already in.
  • One refused group refuses the whole Save. Groups are stored by the same Save as the settings, so while any row is in error neither the groups nor the settings are written.
  • Synonym groups move in and out as a CSV file, one language per file. Download CSV on the Synonyms tab writes the chosen language's groups with a header row reading status,terms, then one group per row: 1 or 0, then one term per cell. The file carries no language column, so it moves between stores whose language ids differ.
  • An import replaces that language's groups, all of them or none. Every group in the file is checked by the same rules as the form (2 to 20 terms, a term in one group only), and one refused line refuses the whole file, naming the line, with nothing written. The other languages' groups are left as they are. A file with only its header row empties the language.
  • An import discards what is unsaved on the tab. It is its own request, not part of Save, and the screen reloads from what the import wrote. Save first if you have rows you want to keep. There is no synonym API: the API reads the search counters and nothing else.

Merchandising pins

  • A rule pins at most 10 products to one keyword unless you change it, in the order you put them in. Most products one rule may pin, on the default store's Settings tab, moves that ceiling anywhere from 1 to 100, for every rule at once. Much above a page of results and page one is entirely merchandising, and the shopper never meets an organic result.
  • A rule matches the shopper's whole query, exactly, after normalisation. A rule on winter coat does not fire on winter coats or on coat winter. One rule per keyword per language, which the database enforces.
  • Pins apply only under the default sort. The moment a shopper picks their own sort or reverses the order, the pins stand down and they get the results they asked for. Sorting by price and still being shown what the shop wants first is the behaviour shoppers learn to distrust.
  • A pinned product appears for that query whether or not it matches it, and is removed from wherever it fell naturally, so it is never shown twice.
  • Pins spill rather than truncate: ten pins with a page size of five fill page one and continue onto page two.
  • A pinned product the shopper may not see (disabled, out of store, out of date-available range) is not shown. The rule is not an override of visibility. Nor is one outside the category the shopper narrowed the search to.
  • One rule per language can answer a search that finds nothing. The first row of the Merchandising list, When a search finds nothing, is a rule with no keyword. Its products are shown, in your order, when a search matches no product at all, after typo tolerance has had its try and after every keyword rule has been looked at: a keyword rule with a product to show wins. It stands down when the shopper sorts, like every pin, and when the page is empty only because the shopper ticked an Advanced Product Filters facet that excludes everything, since that was their choice. The products obey visibility and the shopper's category exactly as pins do. Nothing on the page says these are not matches: Product Search adds no text to your storefront, so pick products that make sense on a page that found nothing. The search is still counted as zero-result in the report, which is fixed, because otherwise the report's queue of unhandled terms would empty the day you set the rule.
  • What the zero-result rule costs. A search that finds something reads no more than it did before. A search that finds nothing, in a language with the rule set, runs one more database statement to fetch the rule's products and skips the two it would have run for the natural results and their count. With an Advanced Product Filters facet ticked, the rule's decision asks up to three statements in all: the count without the facet, the search's own keyword rule without the facet, and the rule's products. The first two are the ones the report already asks of every faceted search that finds nothing, rule or not, so the worst case is still the one statement more. The cost reference measures searches that find something, so this figure is stated here rather than there.

Typo tolerance

  • It fires only when a search finds nothing at all, and the count it reads is the one before your pins are added. A search that found three products is never widened.
  • A word is corrected only from 4 characters long, one edit up to 7, two from 8. Below that a single edit is a different word rather than a typo, which also makes the feature effectively inert for Chinese, Japanese and Korean queries, where a word is commonly two characters. Synonym groups remain the tool there.
  • The first two characters have to be right. labtop is corrected to laptop; ablatop is not.
  • At most 5 words of one query are corrected, and each correction adds at most 5 alternatives.
  • Corrections are drawn from your own catalogue: product names, manufacturer names, category names and your synonym terms. Nothing is drawn from a dictionary of English, so a word your shop does not use is not a correction it can offer.
  • It reads a bounded slice of that catalogue: at most 500 product names per search, and at most 1,000 manufacturer and 1,000 category names. On a catalogue larger than that, the right spelling can sit outside what was read. The numbers are fixed; the settings reference lists them.
  • It is set per language, and a language you have never answered for is on.

The search report

  • It is daily totals and nothing else: a keyword, a day, how many searches and how many of them came back with nothing. There is no IP address, no customer, no session and no per-search detail, and there is no column to put one in. OpenCart's own Reports → Customer Searches does that job if you want it.
  • Only the first page of results is counted. Paging through results is not searching again.
  • A search is filed under its normalised keyword, so Pants, pants and pants are one row.
  • Counters are per store and per language. The report shows one store and one language at a time, and Clear now throws away exactly that store and language, every day of it, whatever the From and To dates say.
  • The report opens on the last 30 days unless you pick your own dates or change Report opens on on the default store's Settings tab. It is read live: there is no Generate button and nothing is cached.
  • Two behaviours read as data loss and are not. A search rescued by typo tolerance stops appearing as a zero-result search, and a query carrying pins stops being reported as zero-result. The report is a queue of terms nothing handled, not a log of what the database did. For the same reason, a search that matched products and came back empty only because the shopper ticked an Advanced Product Filters facet that excludes them all is not counted as zero-result: that was the shopper's choice, not a gap in your vocabulary.
  • Download CSV, above the report's table, writes every keyword the filter matches, not only the page on screen, in the screen's order, with the columns keyword, searches, zero_results and last_searched. The store, language, dates and the zero-only box are in the file's name rather than in a column. Downloading does not prune.
  • A keyword starting with =, +, - or @ gets a leading apostrophe in the file (so does one starting with a tab or a carriage return). Keywords are typed by shoppers, and a spreadsheet would otherwise run one as a formula. The apostrophe is visible in the cell; every other keyword, and every number and date, is written as it is.
  • Counters older than the retention window (Keep Search Totals For, a year unless you change it, 0 for forever) are thrown away when Product Search's screen is opened. A store nobody opens the screen on never prunes.

The API, and what it can read

Off until you switch it on, under API on the default store's settings, and while it is off every route answers as though this extension had no API at all. One read resource: term is one row of the search counters, which is one normalised keyword in one store, language and category on one day, with how many searches it had and how many found nothing. The API is generated from what the extension actually answers.

  • It only ever reads. Nothing can count a search, clear the report, or write a synonym group or a pin through it.
  • Synonym groups and merchandising rules are not readable through it. They are your configuration, authored on a screen with a preview beside it. Adding them later is possible without breaking anything that reads term today.
  • A row is a day's total, not a search. The same thing the report says above: no IP address, no customer, no session, and nothing to put one in.
  • Today is still counting; every earlier day is closed. There is no "changed since" filter, because a search adds to today's row with nothing stamped. Reading from yesterday once a day is a complete incremental read, because no earlier day ever changes again.
  • Days disappear when the retention window prunes them, or when you clear the report. Both happen only when Product Search's screen is opened (see above), so a sync should reconcile by absence: a full walk is the authoritative list.
  • The keyword is shopper text. The report's CSV puts an apostrophe in front of anything a spreadsheet would run as a formula; the API returns the keyword as typed, because that guard changes the value. Treat it as untrusted wherever you display or open it.
  • The credential is OpenCart's own API user, and it is not scoped. One is enough to read every extension's API on the store and core's own order API besides, across every storefront. Restrict it by IP address under System → Users → API, and treat it the way you would treat an admin login.
  • Nothing records that anybody read anything. No read log, no per-credential audit trail, no rate limiting.

Which settings are per store

The Store picker at the top of the screen chooses which storefront the Settings tab is saving for.

  • Per store: Status, Count Searches and Forgive Misspellings. One storefront can run this search while another keeps OpenCart's, and one can be counted while another is not. A store you have never saved falls back to the default store's answers. Forgive Misspellings is also answered per language.
  • One answer for the whole install: Keep Search Totals For, Most products one rule may pin, Report opens on and detailed logging. The last three are on the default store's form only.
  • No store at all: synonym groups and merchandising rules belong to a language, and every storefront in that language shares them.

The settings reference lists every key and its scope.

Your data survives an uninstall

Product Search creates four tables and drops none of them, ever. Uninstalling removes its settings and the event registrations that put it in front of your search, so the storefront is OpenCart's again in the same request. It leaves your synonym groups, merchandising rules and search counters exactly where they are. Reinstalling finds them, and finds your settings too, because the extension keeps its own copy of those.

That is deliberate rather than untidy. An upgrade in OpenCart is an uninstall followed by an install, and nothing tells an extension which of the two it is taking part in, so a tidy-up here would throw a year of authored work away on a point release.

As a consequence, removing the data is a thing you do on purpose. The Clear now button on the report throws away counters for the store and language on screen. Groups and rules are deleted one at a time on their own panes. Dropping the four tables outright is a job for whoever administers your database, and they are named in the reference.

The OpenCart releases

4.0.2.0 is the floor. Below it the extension installs nothing at all (no settings, no event rows), because everything it does rides an event seam that release is the first to have. 4.0.2.0, 4.0.2.1, 4.0.2.2, 4.0.2.3, 4.1.0.1, 4.1.0.2, 4.1.0.3 and 4.1.0.4 are the releases a full install-to-uninstall pass has been through, and the only ones claimed. 4.1.0.0 is the exception, for the reason the section above gives. Anything newer runs and says on its own screen that it is untested, which is not the same as known to be broken. Nothing here uses OpenCart's scheduler, so the broken cron.php on 4.1.0.4 costs this extension nothing.

OpenCart 4.1.0.0: Sort By → Price answers a database error

On that release OpenCart builds the price sort as invalid SQL: a CASE where the expression needed a WHEN. A shopper who picks Price from the Sort By box gets a database error instead of a page. It affects every listing OpenCart offers that sort on (search results, category pages, brand pages), not only the ones Product Search has rewritten.

  • Every other sort works, and so does the search itself: the two-word matching, the synonyms, the spelling corrections, the pins, the statistics and every screen here.
  • Nothing is mis-sorted. The page does not render at all, which is the better of the two failures: nobody is silently shown the wrong order.

It is OpenCart's, not ours: the same sort on a bare 4.1.0.0 store with nothing installed answers the same error, and the statement OpenCart assembles is refused by MySQL on its own. No extension can repair it, because the sort is built inside the query, and this extension is handed the result of that query rather than the building of it. That is why 4.1.0.0 is not one of the releases Product Search claims. OpenCart fixed it in 4.1.0.1; upgrading to that or newer is the whole of the remedy, and the settings screen says so while you are on 4.1.0.0.

Language

Product Search says nothing to a shopper. Every word it ships is on the screens this documentation describes; what it changes about your storefront is which products a search comes back with and how many it says there were, and those are your own product names in your own theme. So the only language question here is about the screens you work in, and it is answered and counted on the shared language promise page.

Three points follow. The first is the one most often misread:

  • Its storefront column on that page reads —, and that is an absence of shopper text rather than an absence of translation. There is no string here that a customer ever reads, in any language, so a zero and a full count would both tell you something untrue: one that the work is outstanding, the other that it was finished. Nothing is queued behind that dash.
  • The screens you work in are translated as far as somebody has read them and no further, and the rest is English. A string a named reviewer has signed off is served in your admin language; a string nobody has read is served in English rather than blank or as its own identifier; and a string whose English has since been reworded goes back to English until it is read again. Which string is in which state is on that page, per language, counted rather than claimed.
  • Your synonym groups and merchandising rules are yours, per language, and none of this reaches them. They are your store's data, authored against the language you picked on the screen, and an update leaves them where they are. Editing our .php files under extension/ is a different act: an update replaces those files and takes your change with them.

Anything not described on this page should be assumed absent. If you are buying on the strength of a behaviour you have not read here, ask first.