├── .gitignore
├── README.md
├── docs
├── 001-introduction.md
├── 002-install-redisearch.md
├── 003-create-index.md
├── 004-query-data.md
├── 005-manage-index.md
├── 006-import-dataset.md
├── 007-query-movies.md
├── 008-aggregation.md
├── 009-advanced-features.md
├── 010-application-development.md
└── images
│ ├── logo.svg
│ ├── sample-app-archi.png
│ └── secondary-index.png
└── sample-app
├── README.md
├── docker-compose.yaml
├── front-end
├── .browserslistrc
├── .env.development
├── .eslintrc.js
├── .gitignore
├── Dockerfile
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── imgs
│ │ ├── forkme_left_red.svg
│ │ ├── redis-logo.svg
│ │ ├── redis.svg
│ │ └── redislabs.png
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── Comments.vue
│ ├── lib
│ │ ├── SearchClient.js
│ │ └── search-samples.json
│ ├── main.js
│ ├── router
│ │ └── index.js
│ └── views
│ │ ├── FacetedSearch.vue
│ │ ├── Home.vue
│ │ ├── MovieForm.vue
│ │ └── Search.vue
└── vue.config.js
├── redisearch-docker
└── dataset
│ ├── import-data.sh
│ ├── import_actors.redis
│ ├── import_create_index.redis
│ ├── import_movies.redis
│ ├── import_theaters.redis
│ └── import_users.redis
├── redisearch-jedis-rest
├── .gitignore
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── Dockerfile
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── redislabs
│ │ └── search
│ │ └── demo
│ │ └── jedis
│ │ ├── RediSearchRestController.java
│ │ ├── RediSearchService.java
│ │ ├── RedisearchJedisRestApplication.java
│ │ └── util
│ │ └── RediSearchCommands.java
│ └── resources
│ └── application.properties
├── redisearch-node-rest
├── Dockerfile
├── NodeSearchService.js
├── README.md
├── package-lock.json
├── package.json
└── server.js
└── redisearch-python-rest
├── .flaskenv
├── Dockerfile
├── README.md
├── requirements.txt
└── server.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
3 |
4 | .idea
5 | *.iml
6 |
7 | # User-specific stuff
8 | .idea/**/workspace.xml
9 | .idea/**/tasks.xml
10 | .idea/**/usage.statistics.xml
11 | .idea/**/dictionaries
12 | .idea/**/shelf
13 |
14 | # Generated files
15 | .idea/**/contentModel.xml
16 |
17 | # Sensitive or high-churn files
18 | .idea/**/dataSources/
19 | .idea/**/dataSources.ids
20 | .idea/**/dataSources.local.xml
21 | .idea/**/sqlDataSources.xml
22 | .idea/**/dynamic.xml
23 | .idea/**/uiDesigner.xml
24 | .idea/**/dbnavigator.xml
25 |
26 | # Gradle
27 | .idea/**/gradle.xml
28 | .idea/**/libraries
29 |
30 | # Gradle and Maven with auto-import
31 | # When using Gradle or Maven with auto-import, you should exclude module files,
32 | # since they will be recreated, and may cause churn. Uncomment if using
33 | # auto-import.
34 | .idea/artifacts
35 | .idea/compiler.xml
36 | .idea/jarRepositories.xml
37 | .idea/modules.xml
38 | .idea/*.iml
39 | .idea/modules
40 | *.iml
41 | *.ipr
42 |
43 | # CMake
44 | cmake-build-*/
45 |
46 | # Mongo Explorer plugin
47 | .idea/**/mongoSettings.xml
48 |
49 | # File-based project format
50 | *.iws
51 |
52 | # IntelliJ
53 | out/
54 |
55 | # mpeltonen/sbt-idea plugin
56 | .idea_modules/
57 |
58 | # JIRA plugin
59 | atlassian-ide-plugin.xml
60 |
61 | # Cursive Clojure plugin
62 | .idea/replstate.xml
63 |
64 | # Crashlytics plugin (for Android Studio and IntelliJ)
65 | com_crashlytics_export_strings.xml
66 | crashlytics.properties
67 | crashlytics-build.properties
68 | fabric.properties
69 |
70 | # Editor-based Rest Client
71 | .idea/httpRequests
72 |
73 | # Android studio 3.1+ serialized cache file
74 | .idea/caches/build_file_checksums.ser
75 |
76 |
77 | target/
78 | pom.xml.tag
79 | pom.xml.releaseBackup
80 | pom.xml.versionsBackup
81 | pom.xml.next
82 | release.properties
83 | dependency-reduced-pom.xml
84 | buildNumber.properties
85 | .mvn/timing.properties
86 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar
87 | .mvn/wrapper/maven-wrapper.jar
88 |
89 | # General
90 | .DS_Store
91 | .AppleDouble
92 | .LSOverride
93 |
94 | # Icon must end with two \r
95 | Icon
96 |
97 |
98 | # Thumbnails
99 | ._*
100 |
101 | # Files that might appear in the root of a volume
102 | .DocumentRevisions-V100
103 | .fseventsd
104 | .Spotlight-V100
105 | .TemporaryItems
106 | .Trashes
107 | .VolumeIcon.icns
108 | .com.apple.timemachine.donotpresent
109 |
110 | # Directories potentially created on remote AFP share
111 | .AppleDB
112 | .AppleDesktop
113 | Network Trash Folder
114 | Temporary Items
115 | .apdisk
116 |
117 | # Logs
118 | logs
119 | *.log
120 | npm-debug.log*
121 | yarn-debug.log*
122 | yarn-error.log*
123 | lerna-debug.log*
124 |
125 | # Diagnostic reports (https://nodejs.org/api/report.html)
126 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
127 |
128 | # Runtime data
129 | pids
130 | *.pid
131 | *.seed
132 | *.pid.lock
133 |
134 | # Directory for instrumented libs generated by jscoverage/JSCover
135 | lib-cov
136 |
137 | # Coverage directory used by tools like istanbul
138 | coverage
139 | *.lcov
140 |
141 | # nyc test coverage
142 | .nyc_output
143 |
144 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
145 | .grunt
146 |
147 | # Bower dependency directory (https://bower.io/)
148 | bower_components
149 |
150 | # node-waf configuration
151 | .lock-wscript
152 |
153 | # Compiled binary addons (https://nodejs.org/api/addons.html)
154 | build/Release
155 |
156 | # Dependency directories
157 | node_modules/
158 | jspm_packages/
159 |
160 | # Snowpack dependency directory (https://snowpack.dev/)
161 | web_modules/
162 |
163 | # TypeScript cache
164 | *.tsbuildinfo
165 |
166 | # Optional npm cache directory
167 | .npm
168 |
169 | # Optional eslint cache
170 | .eslintcache
171 |
172 | # Microbundle cache
173 | .rpt2_cache/
174 | .rts2_cache_cjs/
175 | .rts2_cache_es/
176 | .rts2_cache_umd/
177 |
178 | # Optional REPL history
179 | .node_repl_history
180 |
181 | # Output of 'npm pack'
182 | *.tgz
183 |
184 | # Yarn Integrity file
185 | .yarn-integrity
186 |
187 | # dotenv environment variables file
188 | .env
189 | .env.test
190 |
191 | # parcel-bundler cache (https://parceljs.org/)
192 | .cache
193 | .parcel-cache
194 |
195 | # Next.js build output
196 | .next
197 | out
198 |
199 | # Nuxt.js build / generate output
200 | .nuxt
201 | dist
202 |
203 | # Gatsby files
204 | .cache/
205 | # Comment in the public line in if your project uses Gatsby and not Next.js
206 | # https://nextjs.org/blog/next-9-1#public-directory-support
207 | # public
208 |
209 | # vuepress build output
210 | .vuepress/dist
211 |
212 | # Serverless directories
213 | .serverless/
214 |
215 | # FuseBox cache
216 | .fusebox/
217 |
218 | # DynamoDB Local files
219 | .dynamodb/
220 |
221 | # TernJS port file
222 | .tern-port
223 |
224 | # Stores VSCode versions used for testing VSCode extensions
225 | .vscode-test
226 |
227 | # yarn v2
228 | .yarn/cache
229 | .yarn/unplugged
230 | .yarn/build-state.yml
231 | .yarn/install-state.gz
232 | .pnp.*
233 |
234 |
235 |
236 | # PYTHON
237 | # Byte-compiled / optimized / DLL files
238 | __pycache__/
239 | *.py[cod]
240 | *$py.class
241 |
242 | # C extensions
243 | *.so
244 |
245 | # Distribution / packaging
246 | .Python
247 | build/
248 | develop-eggs/
249 | dist/
250 | downloads/
251 | eggs/
252 | .eggs/
253 | lib/
254 | lib64/
255 | parts/
256 | sdist/
257 | var/
258 | wheels/
259 | share/python-wheels/
260 | *.egg-info/
261 | .installed.cfg
262 | *.egg
263 | MANIFEST
264 |
265 | # PyInstaller
266 | # Usually these files are written by a python script from a template
267 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
268 | *.manifest
269 | *.spec
270 |
271 | # Installer logs
272 | pip-log.txt
273 | pip-delete-this-directory.txt
274 |
275 | # Unit test / coverage reports
276 | htmlcov/
277 | .tox/
278 | .nox/
279 | .coverage
280 | .coverage.*
281 | .cache
282 | nosetests.xml
283 | coverage.xml
284 | *.cover
285 | *.py,cover
286 | .hypothesis/
287 | .pytest_cache/
288 | cover/
289 |
290 | # Translations
291 | *.mo
292 | *.pot
293 |
294 | # Django stuff:
295 | *.log
296 | local_settings.py
297 | db.sqlite3
298 | db.sqlite3-journal
299 |
300 | # Flask stuff:
301 | instance/
302 | .webassets-cache
303 |
304 | # Scrapy stuff:
305 | .scrapy
306 |
307 | # Sphinx documentation
308 | docs/_build/
309 |
310 | # PyBuilder
311 | .pybuilder/
312 | target/
313 |
314 | # Jupyter Notebook
315 | .ipynb_checkpoints
316 |
317 | # IPython
318 | profile_default/
319 | ipython_config.py
320 |
321 | # pyenv
322 | # For a library or package, you might want to ignore these files since the code is
323 | # intended to run in multiple environments; otherwise, check them in:
324 | # .python-version
325 |
326 | # pipenv
327 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
328 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
329 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
330 | # install all needed dependencies.
331 | #Pipfile.lock
332 |
333 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
334 | __pypackages__/
335 |
336 | # Celery stuff
337 | celerybeat-schedule
338 | celerybeat.pid
339 |
340 | # SageMath parsed files
341 | *.sage.py
342 |
343 | # Environments
344 | .env
345 | .venv
346 | env/
347 | venv/
348 | ENV/
349 | env.bak/
350 | venv.bak/
351 |
352 | # Spyder project settings
353 | .spyderproject
354 | .spyproject
355 |
356 | # Rope project settings
357 | .ropeproject
358 |
359 | # mkdocs documentation
360 | /site
361 |
362 | # mypy
363 | .mypy_cache/
364 | .dmypy.json
365 | dmypy.json
366 |
367 | # Pyre type checker
368 | .pyre/
369 |
370 | # pytype static type analyzer
371 | .pytype/
372 |
373 | # Cython debug symbols
374 | cython_debug/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://forum.redislabs.com/c/modules/redisearch/)
2 | [](https://discord.gg/xTbqgTB)
3 |
4 | # Getting Started with RediSearch 2.0
5 |
6 |
7 |
8 |
9 | [RediSearch](https://redisearch.io) is a real-time indexing and search engine.
10 |
11 | This project will let you discover the new major release (2.0) and see how to use it, in your application.
12 |
13 | 1. [Introduction](docs/001-introduction.md) (2mn)
14 | 1. [Install RediSearch 2.0 & Insert Data](docs/002-install-redisearch.md) (2mn)
15 | 1. [Create Index](docs/003-create-index.md) (5mn)
16 | 1. [Query Data](docs/004-query-data.md) (15mn)
17 | 1. [Manage Indexes](docs/005-manage-index.md) (5mn)
18 | 1. [Import Sample Dataset](docs/006-import-dataset.md) (2mn)
19 | 1. [Querying the Movie Dataset](docs/007-query-movies.md) (20mn)
20 | 1. [Aggregation](docs/008-aggregation.md) (15mn)
21 | 1. [Advanced Options](docs/009-advanced-features.md) (5mn)
22 | 1. [Sample Application](docs/010-application-development.md)
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/001-introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | In this tutorial you will learn how to use the [redis-stack](https://redis.io/docs/stack/get-started/install/docker/) image to use the [redisearch module](https://redis.io/docs/stack/search/) that provides an indexing and full text search engine for Redis.
4 |
5 | RediSearch provides a simple and fast way to index and query data using any field (secondary index), and do search and aggregation on an indexed dataset.
6 |
7 | ---
8 | Next: [Install RediSearch 2.0 & Insert Data](002-install-redisearch.md)
9 |
--------------------------------------------------------------------------------
/docs/002-install-redisearch.md:
--------------------------------------------------------------------------------
1 | # Install RediSearch
2 |
3 | You have multiple ways to run RediSearch:
4 |
5 | * building from [sources](https://github.com/RediSearch/RediSearch) and installing it inside an existing Redis Instance
6 | * using [Redis Cloud](https://redislabs.com/redis-enterprise-cloud/) _(when RediSearch module 2.0 available)_
7 | * using [Redis Enterprise](https://redislabs.com/redis-enterprise-software/) _(when RediSearch module 2.0 available)_
8 | * using [Docker](https://hub.docker.com/r/redis/redis-stack)
9 |
10 | Let's use Docker for now.
11 |
12 | **1.1 Open a terminal an run the following command**
13 |
14 |
15 | ```
16 | > docker run -it --rm --name redis-stack \
17 | -p 6379:6379 \
18 | redis/redis-stack:latest
19 | ```
20 |
21 | *Note: The container will automatically be removed when it exits (`--rm` parameter).*
22 |
23 | You have now a Redis instance running with RediSearch installed, let's discover the basics.
24 |
25 |
26 | ---
27 | Next: [Create Index](003-create-index.md)
28 |
--------------------------------------------------------------------------------
/docs/003-create-index.md:
--------------------------------------------------------------------------------
1 | # Create Index
2 |
3 | Before creating the index let's describe the dataset and insert entries.
4 |
5 | ## Sample Dataset
6 |
7 | In this project you will use a simple dataset describing movies, for now, all records are in English. You will learn more about other languages in another tutorial.
8 |
9 | A movie is represented by the following attributes:
10 |
11 | * **`movie_id`** : The unique ID of the movie, internal to this database
12 | * **`title`** : The title of the movie.
13 | * **`plot`** : A summary of the movie.
14 | * **`genre`** : The genre of the movie, for now a movie will only have a single genre.
15 | * **`release_year`** : The year the movie was released as a numerical value.
16 | * **`rating`** : A numeric value representing the public's rating for this movie.
17 | * **`votes`** : Number of votes.
18 | * **`poster`** : Link to the movie poster.
19 | * **`imdb_id`** : id of the movie in the [IMDB](https://imdb.com) database.
20 |
21 |
22 | ### Key and Data structure
23 |
24 | As a Redis developer, one of the first things to look when building your application is to define the structure of the key and data (data design/data modeling).
25 |
26 | A common way of defining the keys in Redis is to use specific patterns in them. For example in this application where the database will probably deal with various business objects: movies, actors, theaters, users, ... we can use the following pattern:
27 |
28 | * `business_object:key`
29 |
30 | For example:
31 | * `movie:001` for the movie with the id 001
32 | * `user:001` the user with the id 001
33 |
34 |
35 | and for the movies information you should use a Redis [Hash](https://redis.io/topics/data-types#hashes).
36 |
37 | A Redis Hash allows the application to structure all the movie attributes in individual fields; also RediSearch will index the fields based on the index definition.
38 |
39 | ## Insert Movies
40 |
41 |
42 | It is time now to add some data into your database, let's insert a few movies, using `redis-cli` or [Redis Insight](https://redislabs.com/redisinsight/).
43 |
44 | Once you are connected to your Redis instance run the following commands:
45 |
46 | ```
47 |
48 | > HSET movie:11002 title "Star Wars: Episode V - The Empire Strikes Back" plot "After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda, while his friends are pursued by Darth Vader and a bounty hunter named Boba Fett all over the galaxy." release_year 1980 genre "Action" rating 8.7 votes 1127635 imdb_id tt0080684
49 |
50 | > HSET movie:11003 title "The Godfather" plot "The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son." release_year 1972 genre "Drama" rating 9.2 votes 1563839 imdb_id tt0068646
51 |
52 | > HSET movie:11004 title "Heat" plot "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist." release_year 1995 genre "Thriller" rating 8.2 votes 559490 imdb_id tt0113277
53 |
54 | > HSET "movie:11005" title "Star Wars: Episode VI - Return of the Jedi" genre "Action" votes 906260 rating 8.3 release_year 1983 plot "The Rebels dispatch to Endor to destroy the second Empire's Death Star." ibmdb_id "tt0086190"
55 |
56 | ```
57 |
58 | Now it is possible to get information from the hash using the movie ID. For example if you want to get the title, and rating execute the following command:
59 |
60 | ```
61 | > HMGET movie:11002 title rating
62 |
63 | 1) "Star Wars: Episode V - The Empire Strikes Back"
64 | 2) "8.7"
65 | ```
66 |
67 | And you can increment the rating of this movie using:
68 |
69 | ```
70 | > HINCRBYFLOAT movie:11002 rating 0.1
71 | "8.8"
72 | ```
73 |
74 | But how do you get a movie or list of movies by year of release, rating or title?
75 |
76 | One option, would be to read all the movies, check all fields and then return only matching movies; no need to say that this is a really bad idea.
77 |
78 | Nevertheless this is where Redis developers often create custom secondary indexes using SET/SORTED SET structures that point back to the movie hash. This needs some heavy design and implementation.
79 |
80 | This is where the RediSearch module can help, and why it was created.
81 |
82 |
83 | ## RediSearch & Indexing
84 |
85 |
86 | RediSearch greatly simplifies this by offering a simple and automatic way to create secondary indices on Redis Hashes. (more datastructure will eventually come)
87 |
88 | 
89 |
90 | Using RediSearch if you want to query on a field, you must first index that field. Let's start by indexing the following fields for our movies:
91 |
92 | * Title
93 | * Release Year
94 | * Rating
95 | * Genre
96 |
97 | When creating a index you define:
98 |
99 | * which data you want to index: all *hashes* with a key starting with `movies`
100 | * which fields in the hashes you want to index using a Schema definition.
101 |
102 | > ***Warning: Do not index all fields***
103 | >
104 | > Indexes take space in memory, and must be updated when the primary data is updated. So create the index carefully and keep the definition up to date with your needs.
105 |
106 | ### Create the Index
107 |
108 | Create the index with the following command:
109 |
110 | ```
111 | > FT.CREATE idx:movie ON hash PREFIX 1 "movie:" SCHEMA title TEXT SORTABLE release_year NUMERIC SORTABLE rating NUMERIC SORTABLE genre TAG SORTABLE
112 | ```
113 |
114 | Before running some queries let's look at the command in detail:
115 |
116 | * [`FT.CREATE`](https://oss.redislabs.com/redisearch/master/Commands/#ftcreate) : creates an index with the given spec. The index name will be used in all the key names so keep it short.
117 | * `idx:movie` : the name of the index
118 | * `ON hash` : the type of structure to be indexed. *Note that in RediSearch 2.0 only hash structures are supported, this parameter will accept other Redis data types in future as RediSearch is updated to index them*
119 | * `PREFIX 1 "movie:"` : the prefix of the keys that should be indexed. This is a list, so since we want to only index movie:* keys the number is 1. Suppose you want to index movies and tv_show that have the same fields, you can use: `PREFIX 2 "movie:" "tv_show:"`
120 | * `SCHEMA ...`: defines the schema, the fields and their type, to index, as you can see in the command, we are using [TEXT](https://oss.redislabs.com/redisearch/Query_Syntax/#a_few_query_examples), [NUMERIC](https://oss.redislabs.com/redisearch/Query_Syntax/#numeric_filters_in_query) and [TAG](https://oss.redislabs.com/redisearch/Query_Syntax/#tag_filters), and [SORTABLE](https://oss.redislabs.com/redisearch/Sorting/) parameters.
121 |
122 | You can find information about the [FT.CREATE](https://oss.redislabs.com/redisearch/Commands/#ftcreate) command in the [documentation](https://oss.redislabs.com/redisearch/Commands/#ftcreate).
123 |
124 |
125 | You can look at the index information with the following command:
126 |
127 | ```
128 | > FT.INFO idx:movie
129 | ```
130 |
131 | ---
132 | Next: [Query Data](004-query-data.md)
133 |
--------------------------------------------------------------------------------
/docs/004-query-data.md:
--------------------------------------------------------------------------------
1 | # Query Data
2 |
3 | The database contains a few movies, and an index, it is now possible to execute some queries.
4 |
5 | ## Queries
6 |
7 | **Example : *All the movies that contains the string "`war`"***
8 |
9 | ```
10 | > FT.SEARCH idx:movie "war"
11 |
12 | 1) (integer) 2
13 | 2) "movie:11005"
14 | 3) 1) "title"
15 | 2) "Star Wars: Episode VI - Return of the Jedi"
16 | ...
17 | 14) "tt0086190"
18 | 4) "movie:11002"
19 | 5) 1) "title"
20 | 2) "Star Wars: Episode V - The Empire Strikes Back"
21 | ...
22 | 13) "imdb_id"
23 | 14) "tt0080684"
24 |
25 | ```
26 |
27 | The FT.SEARCH commands returns a list of results starting with the number of results, then the list of elements (keys & fields).
28 |
29 | 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](https://oss.redislabs.com/redisearch/Escaping/) and [stemmed](https://oss.redislabs.com/redisearch/Stemming/).
30 |
31 | Later when looking at the query syntax in more detail you will learn more about the search capabilities.
32 |
33 | 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:
34 |
35 | ```
36 | > FT.SEARCH idx:movie "war" RETURN 2 title release_year
37 |
38 | 1) (integer) 2
39 | 2) "movie:11005"
40 | 3) 1) "title"
41 | 2) "Star Wars: Episode VI - Return of the Jedi"
42 | 3) "release_year"
43 | 4) "1983"
44 | 4) "movie:11002"
45 | 5) 1) "title"
46 | 2) "Star Wars: Episode V - The Empire Strikes Back"
47 | 3) "release_year"
48 | 4) "1980"
49 | ```
50 |
51 | This query does not specify any "field" and still returns some movies, this is because RediSearch 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.
52 |
53 | If you need to perform a query on a specific field you can specify it using the `@field:` syntax, for example:
54 |
55 | ```
56 | > FT.SEARCH idx:movie "@title:war" RETURN 2 title release_year
57 | ```
58 |
59 | ---
60 | **Example : *All the movies that contains the string "`war` but NOT the `jedi` one"***
61 |
62 | Adding the string `-jedi` (minus) will ask the query engine not to return values that contain `jedi`.
63 |
64 | ```
65 | > FT.SEARCH idx:movie "war -jedi" RETURN 2 title release_year
66 |
67 | 1) (integer) 1
68 | 2) "movie:11002"
69 | 3) 1) "title"
70 | 2) "Star Wars: Episode V - The Empire Strikes Back"
71 | 3) "release_year"
72 | 4) "1980"
73 | ```
74 |
75 | ---
76 | **Example : *All the movies that contains the string "`gdfather` using fuzzy search"***
77 |
78 | As you can see the word godfather contains a speelling error, it can however be matched using [fuzzy matching](https://oss.redislabs.com/redisearch/Query_Syntax/#fuzzy_matching). Fuzzy matches are performed based on [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) (LD).
79 |
80 | ```
81 | > FT.SEARCH idx:movie " %gdfather% " RETURN 2 title release_year
82 |
83 | 1) (integer) 1
84 | 2) "movie:11003"
85 | 3) 1) "title"
86 | 2) "The Godfather"
87 | 3) "release_year"
88 | 4) "1972"
89 | ```
90 |
91 | ---
92 | **Example : *All `Thriller` movies"***
93 |
94 | The `genre` fields is indexed as a TAG and allows exact match queries.
95 |
96 | The syntax to query a TAG field is @field_name:{value}
97 |
98 | ```
99 | > FT.SEARCH idx:movie "@genre:{Thriller}" RETURN 2 title release_year
100 |
101 | 1) (integer) 1
102 | 2) "movie:11004"
103 | 3) 1) "title"
104 | 2) "Heat"
105 | 3) "release_year"
106 | 4) "1995"
107 |
108 | ```
109 |
110 | ---
111 | **Example : *All `Thriller` or `Action` movies"***
112 |
113 | ```
114 | > FT.SEARCH idx:movie "@genre:{Thriller|Action}" RETURN 2 title release_year
115 |
116 | 1) (integer) 3
117 | 2) "movie:11004"
118 | 3) 1) "title"
119 | 2) "Heat"
120 | 3) "release_year"
121 | 4) "1995"
122 | 4) "movie:11005"
123 | 5) 1) "title"
124 | 2) "Star Wars: Episode VI - Return of the Jedi"
125 | 3) "release_year"
126 | 4) "1983"
127 | 6) "movie:11002"
128 | 7) 1) "title"
129 | 2) "Star Wars: Episode V - The Empire Strikes Back"
130 | 3) "release_year"
131 | 4) "1980"
132 | ```
133 |
134 | You can find more information about the Tag filters in [the documentation](https://redis.io/docs/stack/search/reference/query_syntax/#tag-filters).
135 |
136 | ---
137 | **Example : *All `Thriller` or `Action` movies that does not have `Jedi` in the title"***
138 |
139 | ```
140 | > FT.SEARCH idx:movie "@genre:{Thriller|Action} @title:-jedi" RETURN 2 title release_year
141 |
142 | 1) (integer) 2
143 | 2) "movie:11004"
144 | 3) 1) "title"
145 | 2) "Heat"
146 | 3) "release_year"
147 | 4) "1995"
148 | 4) "movie:11002"
149 | 5) 1) "title"
150 | 2) "Star Wars: Episode V - The Empire Strikes Back"
151 | 3) "release_year"
152 | 4) "1980"
153 | ```
154 |
155 | ---
156 | **Example : *All the movies released between 1970 and 1980 (included)***
157 |
158 | The FT.SEARCH syntax has two ways to query numeric fields:
159 |
160 | * using the `FILTER` parameter
161 |
162 | or
163 |
164 | * using the `@field` in the query string.
165 |
166 |
167 | ```
168 | > FT.SEARCH idx:movie * FILTER release_year 1970 1980 RETURN 2 title release_year
169 | ```
170 |
171 | ```
172 | > FT.SEARCH idx:movie "@release_year:[1970 1980]" RETURN 2 title release_year
173 |
174 | 1) (integer) 2
175 | 2) "movie:11003"
176 | 3) 1) "title"
177 | 2) "The Godfather"
178 | 3) "release_year"
179 | 4) "1972"
180 | 4) "movie:11002"
181 | 5) 1) "title"
182 | 2) "Star Wars: Episode V - The Empire Strikes Back"
183 | 3) "release_year"
184 | 4) "1980"
185 |
186 | ```
187 |
188 | To exclude a value prepend it with `(` in the FILTER or query string, for example to exclude 1980:
189 |
190 | ```
191 | > FT.SEARCH idx:movie "@release_year:[1970 (1980]" RETURN 2 title release_year
192 | ```
193 |
194 | ---
195 | ## Insert, Update, Delete and Expire Documents
196 |
197 | As part of this tutorial you have:
198 |
199 | 1. Created few movies, as Redis hashes (*that we call document*) with the following key pattern `movie:*`
200 | 2. Created an index using the `FT.CREATE` command
201 | 3. Queried the data using `FT.SEARCH`
202 |
203 | 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.
204 |
205 | Also new information that matches this pattern/type, will be indexed.
206 |
207 | Let's count the number of movies, add a new one, and count again:
208 |
209 | ```
210 | > FT.SEARCH idx:movie "*" LIMIT 0 0
211 |
212 | 1) (integer) 4
213 |
214 |
215 | > 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
216 |
217 | > FT.SEARCH idx:movie "*" LIMIT 0 0
218 |
219 | 1) (integer) 5
220 |
221 | ```
222 |
223 | The new movie has been indexed. You can also search on any of the indexed fields:
224 |
225 | ```
226 | > FT.SEARCH idx:movie "never" RETURN 2 title release_year
227 |
228 | 1) (integer) 1
229 | 2) "movie:11033"
230 | 3) 1) "title"
231 | 2) "Tomorrow Never Dies"
232 | 3) "release_year"
233 | 4) "1997"
234 | ```
235 |
236 | Now you **update** one of the field, and search for `007`
237 |
238 | ```
239 | > HSET movie:11033 title "Tomorrow Never Dies - 007"
240 |
241 |
242 | > FT.SEARCH idx:movie "007" RETURN 2 title release_year
243 |
244 | 1) (integer) 1
245 | 2) "movie:11033"
246 | 3) 1) "title"
247 | 2) "Tomorrow Never Dies - 007"
248 | 3) "release_year"
249 | 4) "1997"
250 | ```
251 |
252 | When you *delete* the hash, the index is also updated, and the same happens when the key expires (TTL-Time To Live).
253 |
254 | For example, set the James Bond movie to expire in 20 seconds time:
255 |
256 | ```
257 | > EXPIRE "movie:11033" 20
258 |
259 | ```
260 |
261 | 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.
262 |
263 | ```
264 | > FT.SEARCH idx:movie "007" RETURN 2 title release_year
265 |
266 | 1) (integer)
267 |
268 | ```
269 |
270 | > Note: 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, for example a caching layer at the top of another datastore or Web service, query user sessions content, ... This is often qualified as a "[Ephemeral Search](https://redislabs.com/blog/the-case-for-ephemeral-search/)" use case: lightweight, fast and expiration.
271 |
272 | ---
273 | ## More
274 | You have many additional features regarding indexing and searching that you can find in the documentation:
275 |
276 | * [FT.SEARCH command](https://redis.io/commands/ft.search)
277 | * [Query Syntax](https://redis.io/docs/stack/search/reference/query_syntax/)
278 |
279 |
280 | Let's see how to inspect, modify and drop an index.
281 |
282 | ---
283 | Next: [Manage Indexes](005-manage-index.md)
284 |
--------------------------------------------------------------------------------
/docs/005-manage-index.md:
--------------------------------------------------------------------------------
1 | # Manage Index
2 |
3 |
4 | ### Listing and inspecting the indexes
5 |
6 | The `FT._LIST` command provides a list of all RediSearch indexes in your database:
7 |
8 | ```
9 | > FT._LIST
10 | 1) "idx:movie"
11 | ```
12 |
13 | `FT.INFO` provides information about a specific index:
14 |
15 | ```
16 | > FT.INFO "idx:movie"
17 |
18 | 1) "index_name"
19 | 2) "idx:movie"
20 | ...
21 | 5) "index_definition"
22 | ...
23 | 7) "fields"
24 | ...
25 | 9) "num_docs"
26 | 10) "4"
27 | ...
28 |
29 | ```
30 |
31 |
32 | ### Updating your Indexing
33 |
34 | As you build your application and add more information to the database you may need to add new fields to the index. The `FT.ALTER` command enables you to do this.
35 |
36 | ```
37 | > FT.ALTER idx:movie SCHEMA ADD plot TEXT WEIGHT 0.5
38 | "OK"
39 | ```
40 |
41 | The `WEIGHT` declares the importance of this field when calculating result accuracy. This is a multiplication factor (default is 1); so in this example the plot is less important than the title.
42 |
43 | Let's do a query with the new indexed field:
44 |
45 | ```
46 | > FT.SEARCH idx:movie "empire @genre:{Action}" RETURN 2 title plot
47 |
48 | ```
49 |
50 |
51 | ### Dropping the Index
52 |
53 |
54 | You can drop an index using the `FT.DROPINDEX` command.
55 |
56 | ```
57 | > FT.DROPINDEX idx:movie
58 |
59 | "OK"
60 | ```
61 |
62 | Dropping an index does not impact the indexed hashes, this means that the movies are still inside the database.
63 |
64 | ```
65 | >SCAN 0 MATCH movie:*
66 |
67 | 1) "0"
68 | 2) 1) "movie:11002"
69 | 2) "movie:11004"
70 | 3) "movie:11003"
71 | 4) "movie:11005"
72 | ```
73 |
74 |
75 | *Note: You can delete the indexed document/hashes by adding the `DD` parameter.*
76 |
77 |
78 | ---
79 | Next: [Import Sample Dataset](006-import-dataset.md)
80 |
--------------------------------------------------------------------------------
/docs/006-import-dataset.md:
--------------------------------------------------------------------------------
1 | # Sample Dataset
2 |
3 | In the previous steps you used only a few movies, let's now import:
4 |
5 | * More movies *to discover more queries*.
6 | * Theaters *to discover the geospatial capabilities*.
7 | * Users *to do some aggregations*.
8 |
9 | ## Dataset Description
10 |
11 | **Movies**
12 |
13 | The file `sample-app/redisearch-docker/dataset/import_movies.redis` is a script that creates 922 Hashes.
14 |
15 | The movie hashes contain the following fields.
16 |
17 | * **`movie:id`** : The unique ID of the movie, internal to this database (used as the key of the hash)
18 | * **`title`** : The title of the movie.
19 | * **`plot`** : A summary of the movie.
20 | * **`genre`** : The genre of the movie, for now a movie will only have a single genre.
21 | * **`release_year`** : The year the movie was released as a numerical value.
22 | * **`rating`** : A numeric value representing the public's rating for this movie.
23 | * **`votes`** : Number of votes.
24 | * **`poster`** : Link to the movie poster.
25 | * **`imdb_id`** : id of the movie in the [IMDB](https://imdb.com) database.
26 |
27 |
28 | Sample Data: movie:343
29 |
30 |
31 |
32 |
Field
33 |
Value
34 |
35 |
36 |
37 |
38 |
title
39 |
40 | Spider-Man
41 |
42 |
43 |
44 |
plot
45 |
46 | When bitten by a genetically modified spider a nerdy shy and awkward high school student gains spider-like abilities that he eventually must use to fight evil as a superhero after tragedy befalls his family.
47 |
87 |
88 |
89 | **Theaters**
90 |
91 | The file `sample-app/redisearch-docker/dataset/import_theaters.redis` is a script that creates 117 Hashes (used for Geospatial queries). *This dataset is a list of New York Theaters, and not movie theaters, but it is not that critical for this project ;).*
92 |
93 | The theater hashes contain the following fields.
94 |
95 | * **`theater:id`** : The unique ID of the theater, internal to this database (used as the key of the hash)
96 | * **`name`** : The name of the theater
97 | * **`address`** : The street address
98 | * **`city`** : The city, in this sample dataset all the theaters are in New York
99 | * **`zip`** : The zip code
100 | * **`phone`** : The phone number
101 | * **`url`** : The URL of the theater
102 | * **`location`** : Contains the `longitude,latitude` used to create the Geo-indexed field
103 |
104 |
105 |
106 | Sample Data: theater:20
107 |
159 |
160 |
161 |
162 | **Users**
163 |
164 | The file `sample-app/redisearch-docker/dataset/import_users.redis` is a script that creates 5996 Hashes.
165 |
166 | The user hashes contain the following fields.
167 |
168 | * **`user:id`** : The unique ID of the user.
169 | * **`first_name`** : The first name of the user.
170 | * **`last_name`** : The last name of the user.
171 | * **`email`** : The email of the user.
172 | * **`gender`** : The gender of the user (`female`/`male`).
173 | * **`country`** : The country name of the user.
174 | * **`country_code`** : The country code of the user.
175 | * **`city`** : The city of the user.
176 | * **`longitude`** : The longitude of the user.
177 | * **`latitude`** : The latitude of the user.
178 | * **`last_login`** : The last login time for the user, as EPOC time.
179 | * **`ip_address`** : The IP address of the user.
180 |
181 |
182 | Sample Data: user:3233
183 |
184 |
185 |
186 |
Field
187 |
Value
188 |
189 |
190 |
191 |
192 |
first_name
193 |
194 | Rosetta
195 |
196 |
197 |
198 |
last_name
199 |
200 | Olyff
201 |
202 |
203 |
204 |
email
205 |
206 | rolyff6g@163.com
207 |
208 |
209 |
210 |
gender
211 |
212 | female
213 |
214 |
215 |
216 |
country
217 |
218 | China
219 |
220 |
221 |
222 |
country_code
223 |
224 | CN
225 |
226 |
227 |
228 |
city
229 |
230 | Huangdao
231 |
232 |
233 |
234 |
longitude
235 |
236 | 120.04619
237 |
238 |
239 |
240 |
latitude
241 |
242 | 35.872664
243 |
244 |
245 |
246 |
last_login
247 |
248 | 1570386621
249 |
250 |
251 |
252 |
ip_address
253 |
254 | 218.47.90.79
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | ---
263 |
264 | ## Importing the Movies, Theaters and Users
265 |
266 | Before importing the data, flush the database:
267 |
268 | ```
269 | > FLUSHALL
270 | ```
271 |
272 |
273 | The easiest way to import the file is to use the `redis-cli`, using the following terminal command:
274 |
275 | ```
276 | $ redis-cli -h localhost -p 6379 < ./sample-app/redisearch-docker/dataset/import_movies.redis
277 |
278 | $ redis-cli -h localhost -p 6379 < ./sample-app/redisearch-docker/dataset/import_theaters.redis
279 |
280 |
281 | $ redis-cli -h localhost -p 6379 < ./sample-app/redisearch-docker/dataset/import_users.redis
282 |
283 | ```
284 |
285 |
286 | Using Redis Insight or the `redis-cli` you can look at the dataset:
287 |
288 | ```
289 | > HMGET "movie:343" title release_year genre
290 |
291 | 1) "Spider-Man"
292 | 2) "2002"
293 | 3) "Action"
294 |
295 |
296 | > HMGET "theater:20" name location
297 | 1) "Broadway Theatre"
298 | 2) "-73.98335054631019,40.763270202723625"
299 |
300 |
301 |
302 | > HMGET "user:343" first_name last_name last_login
303 | 1) "Umeko"
304 | 2) "Castagno"
305 | 3) "1574769122"
306 |
307 | ```
308 |
309 | You can also use the `DBSIZE` command to see how many keys you have in your database.
310 |
311 | ---
312 |
313 | ## Create Indexes
314 |
315 |
316 | **Create the `idx:movie` index:**
317 |
318 | ```
319 | > FT.CREATE idx:movie ON hash PREFIX 1 "movie:" SCHEMA title TEXT SORTABLE plot TEXT WEIGHT 0.5 release_year NUMERIC SORTABLE rating NUMERIC SORTABLE votes NUMERIC SORTABLE genre TAG SORTABLE
320 |
321 | "OK"
322 | ```
323 |
324 | The movies have now been indexed, you can run the `FT.INFO "idx:movie"` command and look at the `num_docs` returned value. (should be 922).
325 |
326 | **Create the `idx:theater` index:**
327 |
328 | This index will mostly be used to show the geospatial capabilties of RediSearch.
329 |
330 | In the previous examples we have created indexes with 3 types:
331 |
332 | * `Text`
333 | * `Numeric`
334 | * `Tag`
335 |
336 | You will now discover a new type of field: `Geo`.
337 |
338 | The `theater` hashes contains a field `location` with the longitude and latitude, that will be used in the index as follows:
339 |
340 | ```
341 | > FT.CREATE idx:theater ON hash PREFIX 1 "theater:" SCHEMA name TEXT SORTABLE location GEO
342 |
343 | "OK"
344 | ```
345 |
346 | The theaters have been indexed, you can run the `FT.INFO "idx:theater"` command and look at the `num_docs` returned value. (should be 117).
347 |
348 |
349 | **Create the `idx:user` index:**
350 |
351 |
352 | ```
353 | > FT.CREATE idx:user ON hash PREFIX 1 "user:" SCHEMA gender TAG country TAG SORTABLE last_login NUMERIC SORTABLE location GEO
354 |
355 | "OK"
356 | ```
357 |
358 |
359 | ---
360 | Next: [Querying the movie database](007-query-movies.md)
361 |
--------------------------------------------------------------------------------
/docs/007-query-movies.md:
--------------------------------------------------------------------------------
1 | # Querying the Movie Dataset
2 |
3 |
4 |
5 | As described earlier in the tutorial, one of the goals of RediSearch is to provide rich querying capabilities such as:
6 |
7 | * simple and complex conditions
8 | * sorting
9 | * pagination
10 | * counting
11 |
12 |
13 | ### Conditions
14 |
15 | The best way to start to work with RediSearch query capabilities is to look at the various conditions options.
16 |
17 |
18 |
19 |
20 |
21 | Find all the movies that contain the word 'heat' or related to 'heat'
22 |
23 |
24 |
25 | ```
26 | > FT.SEARCH "idx:movie" "heat" RETURN 2 title plot
27 |
28 | 1) (integer) 4
29 | 2) "movie:1141"
30 | 3) 1) "title"
31 | 2) "Heat"
32 | 3) "plot"
33 | 4) "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist."
34 | 4) "movie:818"
35 | 5) 1) "title"
36 | 2) "California Heat"
37 | 3) "plot"
38 | 4) "A lifeguard bets he can be true to just one woman."
39 | 6) "movie:736"
40 | 7) 1) "title"
41 | 2) "Chicago Justice"
42 | 3) "plot"
43 | 4) "The State's Attorney's dedicated team of prosecutors and investigators navigates heated city politics and controversy head-on,while fearlessly pursuing justice."
44 | 8) "movie:1109"
45 | 9) 1) "title"
46 | 2) "Love & Hip Hop: Miami"
47 | 3) "plot"
48 | 4) "'Love and Hip Hop Miami' turns up the heat and doesn't hold back in making the 305 the place to be. Multi-platinum selling hip-hop legend Trick Daddy is back in the studio collaborating ..."
49 |
50 | ```
51 |
52 | The first line contains the number of documents (`4`) that match the query condition, then the list of movies.
53 |
54 | This query is a "fieldless" condition, this means that the query engine has:
55 | * searched in all the TEXT fields of the index(`title` and `plot`)
56 | * for the word `heat` and related words, this is why the movie:736 is returned since it has the word `heated` in the plot ([stemming](https://oss.redislabs.com/redisearch/Stemming/))
57 | * returned the result sorted by score, remember that the title has a weight of 1.0, and the plot a weight of 0.5. So when the word or related words are found in the title the score is larger.
58 | ---
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Find all the movies with a title that contains the word 'heat' or related to 'heat'
67 |
68 |
69 |
70 | In this case you have to set the criteria to a the field title using the `@title` notation.
71 |
72 | ```
73 | > FT.SEARCH "idx:movie" "@title:heat" RETURN 2 title plot
74 | 1) (integer) 2
75 | 2) "movie:1141"
76 | 3) 1) "title"
77 | 2) "Heat"
78 | 3) "plot"
79 | 4) "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist."
80 | 4) "movie:818"
81 | 5) 1) "title"
82 | 2) "California Heat"
83 | 3) "plot"
84 | 4) "A lifeguard bets he can be true to just one woman."
85 |
86 | ```
87 |
88 | So only 2 movies are returned.
89 |
90 | ---
91 |
92 |
93 |
94 |
95 |
96 |
97 | Find all the movies where the title contains 'heat' and does NOT contains 'california'
98 |
99 |
100 |
101 | For this you add parentheses around the field condition and add the `-` sign to 'california'.
102 |
103 | ```
104 | > FT.SEARCH "idx:movie" "@title:(heat -california)" RETURN 2 title plot
105 | 1) (integer) 1
106 | 2) "movie:1141"
107 | 3) 1) "title"
108 | 2) "Heat"
109 | 3) "plot"
110 | 4) "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist."
111 |
112 | ```
113 |
114 | Only one movie is returned.
115 |
116 | If you do not put the `( .. )` the `-california` condition will be applied to all the text fields.
117 |
118 | You can do test this with the following queries:
119 |
120 | ```
121 | > FT.SEARCH "idx:movie" "@title:(heat -woman)" RETURN 2 title plot
122 | ```
123 |
124 | ```
125 | > FT.SEARCH "idx:movie" "@title:heat -woman" RETURN 2 title plot
126 | ```
127 |
128 | As you can see the first query only searches for woman in the title and returns two movies "Heat" and "California Heat", where the second query eliminates "California Heat" from the list since the plot contains the word `woman`.
129 |
130 | ---
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | Find all the 'Drama' movies that have 'heat' in the title
139 |
140 |
141 |
142 | As you have seen earlier the movie index contains:
143 | * the `title` and plot as TEXT
144 | * the `genre` as TAG.
145 |
146 | You saw earlier how to place a condition on a TEXT field.
147 |
148 | The [TAG](https://oss.redislabs.com/redisearch/Tags/) is a little bit different as the index engine does not do any stemming.
149 |
150 | To set a condition on this field you must use the `@field:{value}` notation, the `{...}` indicates that it is a TAG condition
151 |
152 |
153 | ```
154 | > FT.SEARCH "idx:movie" "@title:(heat) @genre:{Drama} " RETURN 3 title plot genre
155 | 1) (integer) 1
156 | 2) "movie:1141"
157 | 3) 1) "title"
158 | 2) "Heat"
159 | 3) "plot"
160 | 4) "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist."
161 | 5) "genre"
162 | 6) "Drama"
163 | ```
164 |
165 | As you can see this query applies conditions to two different fields with an exact match on the TAG.
166 |
167 | TAG is the structure to use when you want to do exact matches on strings/words.
168 | ---
169 |
170 |
171 |
172 |
173 |
174 |
175 | Find all the 'Drama' or 'Comedy' movies that have 'heat' in the title
176 |
177 |
178 |
179 | This is similar to the previous query, you can pass a list of values with the `|` to represent the OR.
180 |
181 |
182 | ```
183 | > FT.SEARCH "idx:movie" "@title:(heat) @genre:{Drama|Comedy} " RETURN 3 title plot genre
184 |
185 | 1) (integer) 2
186 | 2) "movie:1141"
187 | 3) 1) "title"
188 | 2) "Heat"
189 | 3) "plot"
190 | 4) "A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist."
191 | 5) "genre"
192 | 6) "Drama"
193 | 4) "movie:818"
194 | 5) 1) "title"
195 | 2) "California Heat"
196 | 3) "plot"
197 | 4) "A lifeguard bets he can be true to just one woman."
198 | 5) "genre"
199 | 6) "Comedy"
200 | ```
201 |
202 |
203 | You can also put the '|' between all the conditions to search for example all movies that have "heat" in the title, or that are Comedy or that are Drama. The query will look like:
204 |
205 | ```
206 | FT.SEARCH "idx:movie" "@title:(heat) | @genre:{Drama|Comedy} " RETURN 3 title plot genre
207 | ```
208 |
209 | ---
210 |
211 |
212 |
213 |
214 |
215 | Find all 'Mystery' OR 'Thriller' movies, released in 2014 OR 2018
216 |
217 |
218 | In this query, the new item is the query on a numeric field (release_year).
219 |
220 | Like before, for the condition you have to use the `@field:` notation, but for a numeric field you have to put the interval of the condition.
221 |
222 | In this query it will be two conditions with an OR (`|`).
223 |
224 | ```
225 | > FT.SEARCH "idx:movie" "@genre:{Mystery|Thriller} (@release_year:[2018 2018] | @release_year:[2014 2014] )" RETURN 3 title release_year genre
226 |
227 | 1) (integer) 3
228 | 2) "movie:461"
229 | 3) 1) "title"
230 | 2) "The Boat ()"
231 | 3) "release_year"
232 | 4) "2018"
233 | 5) "genre"
234 | 6) "Mystery"
235 | 4) "movie:65"
236 | 5) 1) "title"
237 | 2) "The Loft"
238 | 3) "release_year"
239 | 4) "2014"
240 | 5) "genre"
241 | 6) "Mystery"
242 | 6) "movie:989"
243 | 7) 1) "title"
244 | 2) "Los Angeles Overnight"
245 | 3) "release_year"
246 | 4) "2018"
247 | 5) "genre"
248 | 6) "Thriller"
249 | ```
250 |
251 |
252 | ---
253 |
254 |
255 | Summary
256 |
257 | * Fieldless queries apply to all TEXT fields and use the words and their base form (stemming)
258 | * To apply a condition to a specific field you must use the `@field:` notation
259 | * Multiple conditions are "intersection" (AND condition), to do a "union" (OR condition), you have to use the "`|`" character.
260 |
261 | ----
262 | ### Sort
263 |
264 | A very common use case when querying data is to sort the data on a specific field, and paginate over the result.
265 |
266 |
267 |
268 | Query all the `Action` movies, sorted by release year from most recent to the oldest
269 |
270 |
271 | ```
272 | > FT.SEARCH "idx:movie" "@genre:{Action}" SORTBY release_year DESC RETURN 2 title release_year
273 | 1) (integer) 186
274 | 2) "movie:360"
275 | 3) 1) "release_year"
276 | 2) "2019"
277 | 3) "title"
278 | 4) "Spider-Man: Far from Home"
279 | ...
280 | 20) "movie:278"
281 | 21) 1) "release_year"
282 | 2) "2016"
283 | 3) "title"
284 | 4) "Mechanic: Resurrection"
285 | ```
286 |
287 | The first line contains the number of documents (`186`) that match the query condition.
288 |
289 | The FT.SEARCH command, by default, returns the first ten documents. You will see in the next query how to paginate.
290 |
291 | You can only use one SORTBY clause in an FT.SEARCH query, if you want to sort on multiple fields, for example sorting movies by `genre` ascending and `release_year` descending, you have to use an FT.AGGREGATE, this is covered in the [next section](008-aggregation.md).
292 |
293 | Note: The field used in the [SORTBY](https://oss.redislabs.com/redisearch/Sorting/#specifying_sortby) should be part of the index schema and defined as SORTABLE.
294 | ---
295 |
296 |
297 | ----
298 | ### Paginate
299 |
300 |
301 |
302 | Query all the `Action` movies, sorted by release year from the oldest to the most recent one, returning the record by batch of 100 movies
303 |
304 |
305 | ```
306 | > FT.SEARCH "idx:movie" "@genre:{Action}" LIMIT 0 100 SORTBY release_year ASC RETURN 2 title release_year
307 | 1) (integer) 186
308 | 2) "movie:892"
309 | 3) 1) "release_year"
310 | 2) "1966"
311 | 3) "title"
312 | 4) "Texas,Adios"
313 | ...
314 | 200) "movie:12"
315 | 201) 1) "release_year"
316 | 2) "2014"
317 | 3) "title"
318 | 4) "Fury"
319 | ```
320 |
321 | The result is very similar to the previous query:
322 | * 186 documents found
323 | * the first document is the oldest one, released in 1966
324 | * the latest movie of the batch was released in 2014
325 |
326 |
327 | To paginate to the next batch you need to change the limit as follows:
328 |
329 | ```
330 | > FT.SEARCH "idx:movie" "@genre:{Action}" LIMIT 100 200 SORTBY release_year ASC RETURN 2 title release_year
331 | ```
332 | ---
333 |
334 |
335 |
336 | ----
337 | ### Count
338 |
339 |
340 |
341 |
342 | Count the number of 'Action' movies
343 |
344 |
345 | Based on the sample queries that you have seen earlier, if you specify `LIMIT 0 0` it will give you the number of documents based on the query condition.
346 |
347 | ```
348 | > FT.SEARCH "idx:movie" "@genre:{Action}" LIMIT 0 0
349 |
350 | 1) (integer) 186
351 | ```
352 | ---
353 |
354 |
355 |
356 |
357 |
358 | Count the number of 'Action' movies released in 2017
359 |
360 |
361 | Based on the sample queries that you have seen earlier, if you specify `LIMIT 0 0` it will give you the number of documents based on the query condition.
362 |
363 | ```
364 | > FT.SEARCH "idx:movie" "@genre:{Action}" FILTER release_year 2017 2017 LIMIT 0 0
365 |
366 | 1) (integer) 5
367 | ```
368 |
369 | You can also use the following syntax:
370 |
371 | ```
372 | > FT.SEARCH "idx:movie" "@genre:{Action} @release_year:[2017 2017]" LIMIT 0 0
373 |
374 | 1) (integer) 5
375 | ```
376 |
377 | ---
378 |
379 |
380 |
381 | ----
382 | ### Geospatial Queries
383 |
384 |
385 |
386 | Find theaters, name and address, that are at less than 400 meters from MOMA
387 |
388 |
389 | Suppose you are at the MOMA, located at "11 W 53rd St, New York", and you want to find all the theaters located in a 400m radius.
390 |
391 | For this you need to determine the lat/long position of your current location `-73.9798156,40.7614367`, and execute the following query:
392 |
393 | ```
394 | > FT.SEARCH "idx:theater" "@location:[-73.9798156 40.7614367 400 m]" RETURN 2 name address
395 |
396 | 1) (integer) 5
397 | 2) "theater:30"
398 | 3) 1) "name"
399 | 2) "Ed Sullivan Theater"
400 | 3) "address"
401 | 4) "1697 Broadway"
402 | ...
403 | 10) "theater:115"
404 | 11) 1) "name"
405 | 2) "Winter Garden Theatre"
406 | 3) "address"
407 | 4) "1634 Broadway"
408 | ```
409 |
410 | ---
411 |
412 |
413 |
414 |
415 | ----
416 | Next: [Aggregation](008-aggregation.md)
417 |
--------------------------------------------------------------------------------
/docs/008-aggregation.md:
--------------------------------------------------------------------------------
1 | # Aggregation
2 |
3 | A common need for applications, in addition to retrieving information as a document list, like you have done with the "`FT.SEARCH`" command, is to do some "aggregation".
4 |
5 | For example if we look at the movie documents, you may want to retrieve the number of movies grouped by release year starting with the most recent ones.
6 |
7 | For this, RediSearch provides the FT.AGGREGATE command, with aggregations described as a data processing pipeline.
8 |
9 | Let's check out some examples.
10 |
11 | ## Group By & Sort By
12 |
13 |
14 |
15 |
16 | Number of movies by year
17 |
18 |
19 |
20 | ```
21 | > FT.AGGREGATE "idx:movie" "*" GROUPBY 1 @release_year REDUCE COUNT 0 AS nb_of_movies
22 |
23 | 1) (integer) 60
24 | 2) 1) "release_year"
25 | 2) "1964"
26 | 3) "nb_of_movies"
27 | 4) "9"
28 | ...
29 | 61) 1) "release_year"
30 | 2) "2010"
31 | 3) "nb_of_movies"
32 | 4) "15"
33 | ```
34 |
35 |
36 |
37 | ---
38 |
39 |
40 |
41 |
42 |
43 |
44 | Number of movies by year from the most recent to the oldest
45 |
46 |
47 |
48 | ```
49 | > FT.AGGREGATE "idx:movie" "*" GROUPBY 1 @release_year REDUCE COUNT 0 AS nb_of_movies SORTBY 2 @release_year DESC
50 |
51 | 1) (integer) 60
52 | 2) 1) "release_year"
53 | 2) "2019"
54 | 3) "nb_of_movies"
55 | 4) "14"
56 | ...
57 | 11) 1) "release_year"
58 | 2) "2010"
59 | 3) "nb_of_movies"
60 | 4) "15"
61 | ```
62 | ---
63 |
64 |
65 |
66 |
67 |
68 | Number of movies by genre, with the total number of votes, and average rating
69 |
70 |
71 |
72 | ```
73 | > FT.AGGREGATE idx:movie "*" GROUPBY 1 @genre REDUCE COUNT 0 AS nb_of_movies REDUCE SUM 1 votes AS nb_of_votes REDUCE AVG 1 rating AS avg_rating SORTBY 4 @avg_rating DESC @nb_of_votes DESC
74 |
75 |
76 | 1) (integer) 26
77 | 2) 1) "genre"
78 | 2) "fantasy"
79 | 3) "nb_of_movies"
80 | 4) "1"
81 | 5) "nb_of_votes"
82 | 6) "1500090"
83 | 7) "avg_rating"
84 | 8) "8.8"
85 | ...
86 | 11) 1) "genre"
87 | 2) "romance"
88 | 3) "nb_of_movies"
89 | 4) "2"
90 | 5) "nb_of_votes"
91 | 6) "746"
92 | 7) "avg_rating"
93 | 8) "6.65"
94 | ```
95 |
96 | ---
97 |
98 |
99 |
100 |
101 |
102 |
103 | Count the number of females by country sorted from the biggest to smallest number.
104 |
105 |
106 |
107 | ```
108 | > FT.AGGREGATE idx:user "@gender:{female}" GROUPBY 1 @country REDUCE COUNT 0 AS nb_of_users SORTBY 2 @nb_of_users DESC
109 |
110 | 1) (integer) 193
111 | 2) 1) "country"
112 | 2) "china"
113 | 3) "nb_of_users"
114 | 4) "537"
115 | ...
116 | 11) 1) "country"
117 | 2) "ukraine"
118 | 3) "nb_of_users"
119 | 4) "72"
120 | ```
121 |
122 | ---
123 |
124 |
125 |
126 | ## Apply Functions
127 |
128 |
129 |
130 |
131 |
132 | Number of logins per year and month
133 |
134 |
135 |
136 | The `idx:user` index contains the last_login field. This field stores the last login time as an EPOC timestamp.
137 |
138 | RediSearch aggregation allows you to apply transformations to each record. This is done using the [APPLY](https://oss.redislabs.com/redisearch/Aggregations/#apply_expressions) parameter.
139 |
140 | For this example you have to use a [date/time](https://oss.redislabs.com/redisearch/Aggregations/#list_of_datetime_apply_functions) function to extract the month and year from the timestamp.
141 |
142 | ```
143 | > FT.AGGREGATE idx:user * APPLY year(@last_login) AS year APPLY "monthofyear(@last_login) + 1" AS month GROUPBY 2 @year @month REDUCE count 0 AS num_login SORTBY 4 @year ASC @month ASC
144 |
145 | 1) (integer) 13
146 | 2) 1) "year"
147 | 2) "2019"
148 | 3) "month"
149 | 4) "9"
150 | 5) "num_login"
151 | 6) "230"
152 | ...
153 | 14) 1) "year"
154 | 2) "2020"
155 | 3) "month"
156 | 4) "9"
157 | 5) "num_login"
158 | 6) "271"
159 |
160 | ```
161 |
162 | ---
163 |
164 |
165 |
166 |
167 |
168 | Number of logins per weekday
169 |
170 |
171 |
172 | Using the date/time Apply functions it is possible to extract the day of the week from the timestamp, so let's see how the logins are distributed over the week.
173 |
174 | ```
175 | > FT.AGGREGATE idx:user * APPLY "dayofweek(@last_login) +1" AS dayofweek GROUPBY 1 @dayofweek REDUCE count 0 AS num_login SORTBY 2 @dayofweek ASC
176 |
177 | 1) (integer) 7
178 | 2) 1) "dayofweek"
179 | 2) "1"
180 | 3) "num_login"
181 | 4) "815"
182 | ...
183 | 8) 1) "dayofweek"
184 | 2) "7"
185 | 3) "num_login"
186 | 4) "906"
187 |
188 | ```
189 |
190 | ---
191 |
192 |
193 | ## Filter
194 |
195 | In the previous example you used the `query string` parameter to select all documents (`"*"`) or a subset of the documents (`"@gender:{female}"`)
196 |
197 | It is also possible to filter the results using a predicate expression relating to values in each result. This is applied post-query and relates to the current state of the pipeline. This is done using the [FILTER](https://oss.redislabs.com/redisearch/Aggregations/#filter_expressions) parameter.
198 |
199 |
200 |
201 |
202 |
203 | Count the number of females by country, except China, with more than 100 users, and sorted from the biggest to lowest number
204 |
205 |
206 |
207 | ```
208 | > FT.AGGREGATE idx:user "@gender:{female}" GROUPBY 1 @country REDUCE COUNT 0 AS nb_of_users FILTER "@country!='china' && @nb_of_users > 100" SORTBY 2 @nb_of_users DESC
209 |
210 | 1) (integer) 163
211 | 2) 1) "country"
212 | 2) "indonesia"
213 | 3) "nb_of_users"
214 | 4) "309"
215 | ...
216 | 6) 1) "country"
217 | 2) "brazil"
218 | 3) "nb_of_users"
219 | 4) "108"
220 | ```
221 |
222 | ---
223 |
224 |
225 |
226 |
227 |
228 |
229 | Number of login per month, for year 2020
230 |
231 |
232 |
233 | This is similar to the previous query with the addition of a filter on the year.
234 |
235 | ```
236 | > FT.AGGREGATE idx:user * APPLY year(@last_login) AS year APPLY "monthofyear(@last_login) + 1" AS month GROUPBY 2 @year @month REDUCE count 0 AS num_login FILTER "@year==2020" SORTBY 2 @month ASC
237 |
238 | 1) (integer) 13
239 | 2) 1) "year"
240 | 2) "2020"
241 | 3) "month"
242 | 4) "1"
243 | 5) "num_login"
244 | 6) "520"
245 | ...
246 | 10) 1) "year"
247 | 2) "2020"
248 | 3) "month"
249 | 4) "9"
250 | 5) "num_login"
251 | 6) "271"
252 |
253 | ```
254 |
255 | ---
256 |
257 |
258 |
259 | ----
260 | Next: [Advanced Options](009-advanced-features.md)
261 |
--------------------------------------------------------------------------------
/docs/009-advanced-features.md:
--------------------------------------------------------------------------------
1 | # Other Options
2 |
3 | ## Create an index using a Filter
4 |
5 | In the previous examples, the indices were created using a `PREFIX`, where all the keys matching the type and prefix are indexed.
6 |
7 | It is also possible to create an index using a filter, for example create an index with all the "Drama" movies released between 1990 and 2000 (2000 not included).
8 |
9 | The [`FILTER`](https://oss.redislabs.com/redisearch/Aggregations/#filter_expressions)` expression is using the [aggregation filter syntax(https://oss.redislabs.com/redisearch/Aggregations/#filter_expressions)], for example for the genre and release year it will be
10 |
11 | * `FILTER "@genre=='Drama' && @release_year>=1990 && @release_year<2000"`
12 |
13 | So when you create the index:
14 |
15 | `FT.CREATE idx:drama ON Hash PREFIX 1 "movie:" FILTER "@genre=='Drama' && @release_year>=1990 && @release_year<2000" SCHEMA title TEXT SORTABLE release_year NUMERIC SORTABLE `
16 |
17 | You can run the `FT.INFO idx:drama` command to look at the index definitions and statistics.
18 |
19 | Notes
20 | * The `PREFIX` is not optional.
21 | * In this appliation this index is not useful since you can get the same data from the `idx:movie`
22 |
23 |
24 | You can check that the data has been indexed by running the following queries that should return the same number of documents.
25 |
26 | On `idx:drama`
27 |
28 | ```
29 | > FT.SEARCH idx:drama " @release_year:[1990 (2000]" LIMIT 0 0
30 |
31 | 1) (integer) 24
32 | ```
33 |
34 | On `idx"movie`
35 |
36 | ```
37 | > FT.SEARCH idx:movie "@genre:{Drama} @release_year:[1990 (2000]" LIMIT 0 0
38 |
39 | 1) (integer) 24
40 | ```
41 |
42 |
43 |
44 | ----
45 | Next: [Sample Application](010-application-development.md)
46 |
47 |
--------------------------------------------------------------------------------
/docs/010-application-development.md:
--------------------------------------------------------------------------------
1 | # Application Development
2 |
3 | It is time now to see how to use RediSearch in your application.
4 |
5 | You can find the same REST Service that uses RediSearch developed with different programming languages.
6 |
7 | * [RediSearch & Java : Jedis/JRediSearch](../../../tree/master/sample-app/redisearch-jedis-rest)
8 |
9 | * [Node.js : Node RediSearch](../../../tree/master/sample-app/redisearch-node-rest)
10 |
11 | * [Python : Python RediSearch](../../../tree/master/sample-app/redisearch-python-rest)
12 |
13 | The frontend is created using a Vue.js application that let you run search queries using each of the REST backend.
14 |
15 | 
16 |
17 |
18 | ## Run the Sample Application
19 |
20 | The application and all the services, including RediSearch, are available as a Docker Compose application.
21 |
22 |
23 | If you have not already downloaded the project, clone it:
24 |
25 | ```
26 | > git clone https://github.com/RediSearch/redisearch-getting-started.git
27 |
28 | > cd redisearch-getting-started
29 | ```
30 |
31 |
32 | To run the application:
33 |
34 | ```
35 | > cd sample-app
36 |
37 | > docker-compose up --force-recreate --build
38 |
39 | ```
40 |
41 | This Docker Compose will start:
42 |
43 | 1. A Redis Stack container on port 6379. The redis-cli can be used with this container once the ports are exposed
44 | 1. The Java, Node and Python REST Services available on port 8085, 8086, 8087
45 | 1. The frontend on port 8084
46 | 1. A second RediStack container will start on port 6380 just to load the sample data to the redis stack instance running on port 6379. This container exits once the sample data has been loaded to the 6379 container
47 |
48 | Once started you can access the application and its services using the following URLs:
49 |
50 | * http://localhost:8084
51 | * http://localhost:8085/api/1.0/movies/search?q=star&offset=0&limit=10
52 | * http://localhost:8086/api/1.0/movies/search?q=star&offset=0&limit=10
53 | * http://localhost:8087/api/1.0/movies/search?q=star&offset=0&limit=10
54 |
55 |
56 |
57 | #### Stop and Delete Everything
58 |
59 | Run the following command to delete the containers & images:
60 |
61 | ```
62 | > docker-compose down -v --rmi local --remove-orphans
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
33 |
--------------------------------------------------------------------------------
/docs/images/sample-app-archi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RediSearch/redisearch-getting-started/0b5d84954f1ad9557aece8907d1ea303d62bba90/docs/images/sample-app-archi.png
--------------------------------------------------------------------------------
/docs/images/secondary-index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RediSearch/redisearch-getting-started/0b5d84954f1ad9557aece8907d1ea303d62bba90/docs/images/secondary-index.png
--------------------------------------------------------------------------------
/sample-app/README.md:
--------------------------------------------------------------------------------
1 | # Sample Application
2 |
3 | Go to [Sample Application](../docs/010-application-development.md)
4 |
--------------------------------------------------------------------------------
/sample-app/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3.10'
2 | services:
3 |
4 | # Start RediSearch
5 | redis-stack:
6 | image: redis/redis-stack:latest
7 | container_name: redis-stack
8 | ports:
9 | - "6379:6379"
10 | - "8001:8001"
11 | networks:
12 | - redisearch-gettingstarted
13 | volumes:
14 | - ./redisearch-docker/dataset:/dataset
15 | - ./redis:/data`
16 |
17 | # Start redis-stack to run load script and exit
18 | redis-stack-2:
19 | image: redis/redis-stack:latest
20 | container_name: redis-stack-2
21 | ports:
22 | - "6380:6379"
23 | networks:
24 | - redisearch-gettingstarted
25 | volumes:
26 | - ./redisearch-docker/dataset:/dataset
27 | command:
28 | [ "sh", "./dataset/import-data.sh" ]
29 | depends_on:
30 | - redis-stack
31 |
32 | rest-java:
33 | build:
34 | context: ./redisearch-jedis-rest
35 | dockerfile: Dockerfile
36 | container_name: rest-java
37 | ports:
38 | - "8085:8085"
39 | environment:
40 | - REDIS_URL=redis://redis-stack:6379
41 | - REDIS_INDEX=idx:movie
42 | networks:
43 | - redisearch-gettingstarted
44 | restart: always
45 | depends_on:
46 | - redis-stack
47 |
48 | rest-node:
49 | build:
50 | context: ./redisearch-node-rest
51 | dockerfile: Dockerfile
52 | container_name: rest-node
53 | ports:
54 | - "8086:8086"
55 | environment:
56 | - REDIS_URL=redis://redis-stack:6379
57 | - REDIS_INDEX=idx:movie
58 | networks:
59 | - redisearch-gettingstarted
60 | restart: always
61 | depends_on:
62 | - redis-stack
63 |
64 |
65 | rest-python:
66 | build:
67 | context: ./redisearch-python-rest
68 | dockerfile: Dockerfile
69 | container_name: rest-python
70 | ports:
71 | - "8087:8087"
72 | environment:
73 | - REDIS_SERVER=redis-stack
74 | - REDIS_PORT=6379
75 | - REDIS_INDEX=idx:movie
76 | networks:
77 | - redisearch-gettingstarted
78 | restart: always
79 | depends_on:
80 | - redis-stack
81 |
82 | search-frontend:
83 | build:
84 | context: ./front-end
85 | dockerfile: Dockerfile
86 | container_name: search-frontend
87 | ports:
88 | - "8084:8084"
89 | environment:
90 | - VUE_APP_SEARCH_API_JAVA=http://rest-java:8085
91 | - VUE_APP_SEARCH_API_NODE=http://rest-node:8086
92 | - VUE_APP_SEARCH_API_PYTHON=http://rest-python:8087
93 | networks:
94 | - redisearch-gettingstarted
95 | restart: always
96 | depends_on:
97 | - redis-stack
98 | - rest-java
99 | - rest-node
100 | - rest-python
101 |
102 | networks:
103 | redisearch-gettingstarted:
104 | driver: bridge
105 |
--------------------------------------------------------------------------------
/sample-app/front-end/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/sample-app/front-end/.env.development:
--------------------------------------------------------------------------------
1 |
2 | VUE_APP_SEARCH_API_JAVA=http://127.0.0.1:8085
3 | VUE_APP_SEARCH_API_NODE=http://127.0.0.1:8086
4 | VUE_APP_SEARCH_API_PYTHON=http://127.0.0.1:8087
5 |
--------------------------------------------------------------------------------
/sample-app/front-end/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample-app/front-end/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/sample-app/front-end/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:lts-alpine
2 |
3 | # install simple http server for serving static content
4 | RUN npm install -g http-server
5 |
6 | # make the 'app' folder the current working directory
7 | WORKDIR /app
8 |
9 | # copy both 'package.json' and 'package-lock.json' (if available)
10 | COPY package*.json ./
11 |
12 | # Environment Variables
13 | ARG VUE_APP_SEARCH_API_JAVA
14 | ENV VUE_APP_SEARCH_API_JAVA $VUE_APP_SEARCH_API_JAVA
15 | ARG VUE_APP_SEARCH_API_NODE
16 | ENV VUE_APP_SEARCH_API_NODE $VUE_APP_SEARCH_API_NODE
17 | ARG VUE_APP_SEARCH_API_PYTHON
18 | ENV VUE_APP_SEARCH_API_PYTHON $VUE_APP_SEARCH_API_PYTHON
19 |
20 | # install project dependencies
21 | RUN npm install
22 |
23 | # copy project files and folders to the current working directory (i.e. 'app' folder)
24 | COPY . .
25 |
26 | # build app for production with minification
27 | RUN npm run build
28 |
29 | EXPOSE 8084
30 | CMD [ "http-server", "dist", "--port", "8084" ]
31 |
32 |
--------------------------------------------------------------------------------
/sample-app/front-end/README.md:
--------------------------------------------------------------------------------
1 | # front-end
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
26 |
27 | ### Running the application in Docker
28 |
29 | You can run build and run the application from docker using the following commands:
30 |
31 | **Build**
32 |
33 | ```shell script
34 |
35 | > docker build -t redis/search-frontend .
36 |
37 | ```
38 |
39 | This command will create a new image and build the maven project into it.
40 |
41 | **Run**
42 |
43 | ```shell script
44 | > docker run --rm \
45 | --env "VUE_APP_SEARCH_API_JAVA=http://host.docker.internal:8085" \
46 | --env "VUE_APP_SEARCH_API_NODE=http://host.docker.internal:8086" \
47 | --env "VUE_APP_SEARCH_API_PYTHON=http://host.docker.internal:8087" \
48 | --name "redisearch-frontend"\
49 | -p 8084:8084 redis/search-frontend
50 | ```
51 |
52 | Access the Web application with the following URL:
53 |
54 | * http://localhost:8084
55 |
--------------------------------------------------------------------------------
/sample-app/front-end/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/sample-app/front-end/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "front-end",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.21.1",
12 | "bootstrap": "^4.5.2",
13 | "bootstrap-vue": "^2.16.0",
14 | "core-js": "^3.6.5",
15 | "vue": "^2.6.11",
16 | "vue-router": "^3.2.0",
17 | "vue-slider-component": "^3.2.5"
18 | },
19 | "devDependencies": {
20 | "@vue/cli-plugin-babel": "~4.5.0",
21 | "@vue/cli-plugin-eslint": "~4.5.0",
22 | "@vue/cli-plugin-router": "~4.5.0",
23 | "@vue/cli-service": "~4.5.0",
24 | "babel-eslint": "^10.1.0",
25 | "eslint": "^6.7.2",
26 | "eslint-plugin-vue": "^6.2.2",
27 | "vue-template-compiler": "^2.6.11"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/sample-app/front-end/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RediSearch/redisearch-getting-started/0b5d84954f1ad9557aece8907d1ea303d62bba90/sample-app/front-end/public/favicon.ico
--------------------------------------------------------------------------------
/sample-app/front-end/public/imgs/forkme_left_red.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | ]>
7 |
104 |
--------------------------------------------------------------------------------
/sample-app/front-end/public/imgs/redis-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample-app/front-end/public/imgs/redis.svg:
--------------------------------------------------------------------------------
1 |
2 |
176 |
--------------------------------------------------------------------------------
/sample-app/front-end/public/imgs/redislabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RediSearch/redisearch-getting-started/0b5d84954f1ad9557aece8907d1ea303d62bba90/sample-app/front-end/public/imgs/redislabs.png
--------------------------------------------------------------------------------
/sample-app/front-end/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |