# Text.Like

> Matches a string against a wildcard pattern, where % stands for any run of characters and _ for exactly one. It is the only wildcard search in the language — the instance methods Contains, StartsWith and EndsWith are literal — and it is the one that a database can answer with an index. It gives the same answer in the browser, in a function body, and pushed down into a query.

<!-- id: function-text-like · area: function · stability: stable · html: https://osysharp.com/reference/function/text-like/ -->

## Summary        {#summary}
`Text.Like(s, pattern)` tests a string against a **wildcard pattern**. It is the deliberate opposite of
[Contains, StartsWith, EndsWith](https://osysharp.com/reference/function/string-search/): there the argument is data, here it is a pattern.

## Signature      {#signature}
```osy syntax
Text.Like(<string>, <string>) -> bool
```

## Description    {#description}

### The pattern   {#pattern}

| in the pattern | matches |
|---|---|
| `%` | any run of characters, including none |
| `_` | exactly one character |
| `\%` `\_` `\\` | a literal `%`, `_` or `\` |
| anything else | itself |

```osy title="what each wildcard matches, and escaping a literal percent" syntax
Text.Like(code, "RUSH-%")        // begins with RUSH-
Text.Like(code, "%-2026")        // ends with -2026
Text.Like(code, "A_-%")          // A, then any one character, then "-", then anything
Text.Like(label, @"50\% off")    // a LITERAL percent sign
```

### Why it is not a method on the string   {#vs-contains}

`s.Contains(x)`, `s.StartsWith(x)` and `s.EndsWith(x)` are **literal** searches — a `%` in the argument is a percent
sign. `Text.Like` is the wildcard one. They are spelled differently on purpose: the difference between "find this text"
and "find things shaped like this" should be visible where you read the call, not something you have to remember. C#
has no `Like` at all, and EF Core makes the same split for the same reason (`EF.Functions.Like`, never a method on
`string`).

### It is case-sensitive   {#case}

`Text.Like("ACME Ltd", "acme%")` is **false**. To ignore case, lower both sides:

```osy title="case-sensitive too — lower both sides" syntax
Text.Like(c.Name.ToLower(), "acme%")
```

### Inside a query, it can use an index   {#index}

This is the practical reason it exists. `Contains` becomes a substring search that has to look at every row; a `Like`
whose pattern is anchored at the front (`"RUSH-%"`) can be answered from a btree index on the column. A pattern that
starts with `%` cannot — it has nothing to seek to — so prefer an anchored pattern when the table is large.

### A pattern that ends with a bare `\`   {#trailing-escape}

A trailing escape character escapes nothing, so such a pattern can never match anything. When the pattern is written
out in the source it is a **compile error**; double it (`\\`) if you meant a literal backslash.

### One answer, wherever it runs   {#same-everywhere}

The same call gives the same result in a browser action, in a function body on the server, and compiled into SQL — the
three implementations are tested against each other over a corpus that includes `%`, `_`, `\`, newlines and characters
outside the Basic Multilingual Plane.

## Examples       {#examples}
```osy title="find codes by shape" test app=text-like
entity Product { string Code; string Name; }

List<Product> RushCodes() {
  return Product.Where(p => Text.Like(p.Code, "RUSH-%")).ToList();   // anchored: an index can serve it
}

bool LooksLikeABatch(string code) {
  return Text.Like(code, "B__-____");        // B, two characters, a dash, four characters
}

bool MentionsAPercentage(string label) {
  return Text.Like(label, @"%\%%");          // contains a literal percent sign
}
```

## See also       {#see-also}
- [Contains, StartsWith, EndsWith](https://osysharp.com/reference/function/string-search/) — `Contains`/`StartsWith`/`EndsWith`, the LITERAL searches
- [Regex](https://osysharp.com/reference/stdlib/regex/) — full regular expressions, in memory only (no query form)
- [execution side](https://osysharp.com/reference/function/execution-side/) — why this runs in the browser too
