The analyzer decides what matching even means

A search that fails to find an obvious result is almost never a query problem. Text is tokenised and normalised at index time, and the query goes through the same treatment — so if the two disagree, nothing matches and the query looks correct.

POST /products/_analyze
{ "analyzer": "standard", "text": "Ray-Ban RB2140" }

# standard: [ray, ban, rb2140]
# keyword:  [Ray-Ban RB2140]
# whitespace: [Ray-Ban, RB2140]

The _analyze endpoint is the debugging tool nobody uses, and it answers the question directly: this is what your text became. A SKU searched as a phrase and indexed with the standard analyzer will have been split on the hyphen, which is why partial SKU search silently fails. A field frequently needs both treatments, indexed twice — analysed for searching and keyword for exact matching and aggregation.