Search and filter your data

Use FT.SEARCH to find documents by full text, tags, and numeric ranges, and to return only the fields you want.

This is step 3 of the Redis Search tutorial. You need the catalog loaded and the index created before the queries below will work.

Now the fun part: asking questions. The FT.SEARCH command does two jobs:

  • Selection — choose which documents to return, by matching text, tags, and numeric ranges.
  • Projection — choose which fields of each matching document to return.

Every query has the same basic shape: FT.SEARCH <index> "<query>", optionally followed by clauses that control what comes back.

Return everything

The * query matches every document. Use it to confirm the index is working. The first line of the result is the total number of matches:

Foundational: Match every document with the wildcard query to confirm the index works
FT.SEARCH idx:catalog "*" LIMIT 0 0
res = index.search(Query("*").paging(0, 0))
print("Total products:", res.total)  # >>> Total products: 12

The LIMIT 0 0 clause asks for zero documents, so you get just the count. By default FT.SEARCH returns the full document for each match, which is verbose. The rest of this page uses RETURN to keep the output readable.

Fields you indexed as TEXT support full-text search: matching by word, regardless of position or surrounding text. To search a specific field, prefix the term with @fieldname:.

This finds products whose name contains the word headphones:

Full-text search: Match a word within a TEXT field using the @field:term syntax
FT.SEARCH idx:catalog "@name:headphones" RETURN 1 name
res = index.search(Query("@name:headphones").return_field("name"))
print(res.docs)
# >>> [Document {'id': 'product:1', ... 'name': 'Aurora AcousticPro Headphones'}]

Full-text matching is case-insensitive and word-based, so headphones matches Headphones. You can also search across all TEXT fields at once by leaving off the @field: prefix; for example, FT.SEARCH idx:catalog "wireless" matches any product with wireless in its name or description.

Match an exact phrase

To match an exact phrase — several words that must appear together and in order — wrap the phrase in escaped double quotes. This finds products whose description contains the phrase noise cancelling:

Exact phrase: Match an ordered, contiguous phrase in a TEXT field by wrapping it in escaped double quotes
FT.SEARCH idx:catalog "@description:\"noise cancelling\"" RETURN 1 name
res = index.search(Query('@description:"noise cancelling"').return_field("name"))
print(res.total, [d.name for d in res.docs])
# >>> 1 ['Aurora AcousticPro Headphones']

Without the quotes, @description:noise cancelling matches the two words independently: both must be present, but they can appear anywhere in the field and in any order. The quotes are what require them to sit together as a phrase.

Filter by tag

Fields you indexed as TAG match on exact values. Tag values go inside curly braces: @field:{value}.

This finds every product in the Audio category and returns the name and price of each:

Tag filter: Match an exact TAG value using the @field:{value} syntax
FT.SEARCH idx:catalog "@category:{Audio}" RETURN 2 name price
res = index.search(Query("@category:{Audio}").return_fields("name", "price"))
print(res.total, [d.name for d in res.docs])
# >>> 3 ['Aurora BudsMini Earbuds', 'Sonus Boom Portable Speaker', ...]

Because features was indexed as a multi-value tag (the [*] from the previous step), the same syntax filters on individual list elements. This finds every waterproof product:

Tag filter on arrays: Match a single element of a multi-value TAG field
FT.SEARCH idx:catalog "@features:{waterproof}" RETURN 1 name
res = index.search(Query("@features:{waterproof}").return_field("name"))
print(res.total, [d.name for d in res.docs])
# >>> 2 ['Sonus Boom Portable Speaker', 'Vista Action Cam 4K']

Filter by numeric range

Fields you indexed as NUMERIC match on ranges, written as @field:[min max]. This finds products priced at $100 or less, sorted from cheapest to most expensive with SORTBY:

Numeric range: Match a NUMERIC field within [min max] and order results with SORTBY
FT.SEARCH idx:catalog "@price:[0 100]" SORTBY price ASC RETURN 2 name price
res = index.search(
    Query("@price:[0 100]").sort_by("price", asc=True).return_fields("name", "price")
)
print([(d.name, d.price) for d in res.docs])
# >>> [('Lumi Glow Smart Bulb', '24.99'), ('Glide Pro Wireless Mouse', '59.99'), ...]

Use -inf and +inf for open-ended ranges. For example, @price:[1000 +inf] matches everything priced $1000 or more.

Combine conditions

Real questions usually combine several conditions. Listing expressions one after another means AND: every condition must match. This finds Audio products that also cost $100 or less:

Combined query: AND multiple conditions by listing them together (tag plus numeric range)
FT.SEARCH idx:catalog "@category:{Audio} @price:[0 100]" RETURN 2 name price
res = index.search(
    Query("@category:{Audio} @price:[0 100]").return_fields("name", "price")
)
print(res.total, [d.name for d in res.docs])
# >>> 1 ['Aurora BudsMini Earbuds']

Only the BudsMini Earbuds satisfy both conditions. You can also express OR with | and negation with -. See Combined queries for the full set of operators.

Projection: return only what you need

You have already been using RETURN to pick fields. It is worth calling out on its own, because returning only the fields you need keeps responses small and fast:

  • RETURN 2 name price returns just those two fields.
  • Without RETURN, the full document comes back for every match.
  • LIMIT <offset> <count> controls how many results you get and is the basis for pagination. By default, FT.SEARCH returns the first 10 matches.

This returns the three most expensive products, newest pricing first, with only their name and price:

Projection and paging: Return only chosen fields, sort, and page results with RETURN, SORTBY, and LIMIT
FT.SEARCH idx:catalog "*" SORTBY price DESC RETURN 2 name price LIMIT 0 3
res = index.search(
    Query("*").sort_by("price", asc=False).return_fields("name", "price").paging(0, 3)
)
print([(d.name, d.price) for d in res.docs])
# >>> [('Pixma Vortex 15 Laptop', '1399'), ('Pixma UltraView 27 Monitor', '329.99'), ...]

The total is still 12 (the count of all matches), but only three documents are returned because of LIMIT 0 3.

Try it in Redis Insight:
The Redis Insight Search workspace has a query editor that understands your index schema. As you type @, it suggests field names and tag values, and it renders results as a table instead of the numbered list you see in redis-cli. It is a comfortable place to experiment with the queries on this page.

Next steps

FT.SEARCH finds and returns documents. When you need to summarize across many documents — counts, averages, totals per group — you use a different command. Continue to aggregation.

RATE THIS PAGE
Back to top ↑