The database contains a few movies, and an index, it is now possible to execute some queries.
Example : All the movies that contains the string "war"
> FT.SEARCH idx:movie "war"
1) (integer) 2
2) "movie:11005"
3) 1) "title"
2) "Star Wars: Episode VI - Return of the Jedi"
...
14) "tt0086190"
4) "movie:11002"
5) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
...
13) "imdb_id"
14) "tt0080684"
The FT.SEARCH commands returns a list of results starting with the number of results, then the list of elements (keys & fields).
As you can see the movie Star Wars: Episode V - The Empire Strikes Back is found, even though you used only the word “war” to match “Wars” in the title. This is because the title has been indexed as text, so the field is tokenized and stemmed.
Later when looking at the query syntax in more detail you will learn more about the search capabilities.
It is also possible to limit the list of fields returned by the query using the RETURN
parameter, let's run the same query, and return only the title and release_year:
> FT.SEARCH idx:movie "war" RETURN 2 title release_year
1) (integer) 2
2) "movie:11005"
3) 1) "title"
2) "Star Wars: Episode VI - Return of the Jedi"
3) "release_year"
4) "1983"
4) "movie:11002"
5) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
3) "release_year"
4) "1980"
This query does not specify any "field" and still returns some movies, this is because Search in Redis Stack will search all TEXT fields by default. In the current index only the title is present as a TEXT field. You will see later how to update an index, to add more fields to it.
If you need to perform a query on a specific field you can specify it using the @field
: syntax, for example:
> FT.SEARCH idx:movie "@title:war" RETURN 2 title release_year
Example :
Adding the string -jedi
(minus) will ask the query engine not to return values that contain jedi
.
> FT.SEARCH idx:movie "war -jedi" RETURN 2 title release_year
1) (integer) 1
2) "movie:11002"
3) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
3) "release_year"
4) "1980"
Example : All the movies that contains the string "gdfather
using fuzzy search"
As you can see the word godfather contains a spelling error, it can however be matched using fuzzy matching. Fuzzy matches are performed based on Levenshtein distance (LD).
> FT.SEARCH idx:movie " %gdfather% " RETURN 2 title release_year
1) (integer) 1
2) "movie:11003"
3) 1) "title"
2) "The Godfather"
3) "release_year"
4) "1972"
Example : All Thriller
movies"
The genre
fields is indexed as a TAG and allows exact match queries.
The syntax to query a TAG field is @field_name:{value}
> FT.SEARCH idx:movie "@genre:{Thriller}" RETURN 2 title release_year
1) (integer) 1
2) "movie:11004"
3) 1) "title"
2) "Heat"
3) "release_year"
4) "1995"
Example : All Thriller
or Action
movies"
> FT.SEARCH idx:movie "@genre:{Thriller|Action}" RETURN 2 title release_year
1) (integer) 3
2) "movie:11004"
3) 1) "title"
2) "Heat"
3) "release_year"
4) "1995"
4) "movie:11005"
5) 1) "title"
2) "Star Wars: Episode VI - Return of the Jedi"
3) "release_year"
4) "1983"
6) "movie:11002"
7) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
3) "release_year"
4) "1980"
You can find more information about the Tag filters in the documentation.
Example : All Thriller
or Action
movies that does not have Jedi
in the title"
> FT.SEARCH idx:movie "@genre:{Thriller|Action} @title:-jedi" RETURN 2 title release_year
1) (integer) 2
2) "movie:11004"
3) 1) "title"
2) "Heat"
3) "release_year"
4) "1995"
4) "movie:11002"
5) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
3) "release_year"
4) "1980"
Example : All the movies released between 1970 and 1980 (included)
The FT.SEARCH syntax has two ways to query numeric fields:
FILTER
parameteror
@field
in the query string.> FT.SEARCH idx:movie * FILTER release_year 1970 1980 RETURN 2 title release_year
> FT.SEARCH idx:movie "@release_year:[1970 1980]" RETURN 2 title release_year
1) (integer) 2
2) "movie:11003"
3) 1) "title"
2) "The Godfather"
3) "release_year"
4) "1972"
4) "movie:11002"
5) 1) "title"
2) "Star Wars: Episode V - The Empire Strikes Back"
3) "release_year"
4) "1980"
To exclude a value prepend it with ( in the FILTER or query string, for example to exclude 1980:
> FT.SEARCH idx:movie "@release_year:[1970 (1980]" RETURN 2 title release_year
As part of this tutorial you have:
movie:*
FT.CREATE
commandFT.SEARCH
When creating the index, using the idx:movie ON hash PREFIX 1 "movie:"
parameter you are asking the indexing engine to look at all existing keys and index them.
Also new information that matches this pattern/type, will be indexed.
Let's count the number of movies, add a new one, and count again:
> FT.SEARCH idx:movie "*" LIMIT 0 0
1) (integer) 4
> HSET movie:11033 title "Tomorrow Never Dies" plot "James Bond sets out to stop a media mogul's plan to induce war between China and the U.K in order to obtain exclusive global media coverage." release_year 1997 genre "Action" rating 6.5 votes 177732 imdb_id tt0120347
> FT.SEARCH idx:movie "*" LIMIT 0 0
1) (integer) 5
The new movie has been indexed. You can also search on any of the indexed fields:
> FT.SEARCH idx:movie "never" RETURN 2 title release_year
1) (integer) 1
2) "movie:11033"
3) 1) "title"
2) "Tomorrow Never Dies"
3) "release_year"
4) "1997"
Now you update one of the field, and search for 007
> HSET movie:11033 title "Tomorrow Never Dies - 007"
> FT.SEARCH idx:movie "007" RETURN 2 title release_year
1) (integer) 1
2) "movie:11033"
3) 1) "title"
2) "Tomorrow Never Dies - 007"
3) "release_year"
4) "1997"
When you delete the hash, the index is also updated, and the same happens when the key expires (TTL-Time To Live).
For example, set the James Bond movie to expire in 20 seconds time:
> EXPIRE "movie:11033" 20
You can run the following query, and you will that the document expires after 20 seconds and the search query will not return any results, showing that the index has been updated.
> FT.SEARCH idx:movie "007" RETURN 2 title release_year
1) (integer)
When you are using Redis as your primary database you are not necessarily using TTLs to delete records. However, if the data you are storing and indexing are transient (e.g a caching layer at the top of another datastore or Web service, query user sessions content, etc.), this is often qualified as an "Ephemeral Search" use case: lightweight, fast and expiration.
You have many additional features regarding indexing and searching that you can find in the documentation:
Let's see how to inspect, modify and drop an index.