Greetings!
Search functionality is a common feature in most applications today. Elasticsearch, one of the most powerful and widely used search engines, offers efficient solutions for building search applications. In this article, we will create a simple REST application with a search endpoint using Elasticsearch and Python Flask. The code is straightforward and easy to understand.
You can verify the setup by accessing localhost:9200.
Mappings define the structure and data types of fields within an index, specifying how data should be indexed and stored to enable precise search and analysis.
We will create a "movies" index with the required fields. To ensure the index exists, we can define a separate function to create it if it does not already exist. Additionally, we will provide custom mappings to Elasticsearch instead of relying on auto-detection. This approach offers greater control over the search functionality, particularly for searching by genre in this example.Once the function is defined, we can use it to insert movies from a JSON file for testing.
movies.jsonWhile Elasticsearch provides a bulk endpoint for inserting large amounts of data in scenarios like this, I’m using this approach for learning purposes.
Search functionality is a common feature in most applications today. Elasticsearch, one of the most powerful and widely used search engines, offers efficient solutions for building search applications. In this article, we will create a simple REST application with a search endpoint using Elasticsearch and Python Flask. The code is straightforward and easy to understand.
Setup Elasticsearch
We are using the Elasticsearch Docker image to set up a local cluster; however, you can use any method you prefer to set up an Elasticsearch cluster.version: "3.9"services:elasticsearch:image: elasticsearch:8.6.2environment:- xpack.security.enabled=false- discovery.type=single-node- ES_JAVA_OPTS=-Xms1g -Xmx1gvolumes:- es_data:/usr/share/elasticsearch/dataports:- target: 9200published: 9200networks:- elastickibana:image: kibana:8.6.2ports:- target: 5601published: 5601depends_on:- elasticsearchnetworks:- elasticvolumes:es_data:driver: localnetworks:elastic:name: elasticdriver: bridgedocker-compose -f es-docker.yml up
docker-compose -f es-docker.yml up
Install dependencies
First, let’s install the necessary dependencies.python3 -m venv venvsource venv/bin/activatepip install Flask elasticsearch
Initialize Flask
Now, we can verify the setup by creating a hello route.from flask import Flask, request, jsonifyapp = Flask(__name__)@app.route('/hello', methods=['GET'])def hello():return jsonify({ "message": "Hello Elasticsearch" }), 200if __name__ == '__main__':app.run(port=5000, debug=True)
python3 app.py
Elasticsearch Mappings and Index Creation
An Elasticsearch index is a logical namespace that stores a collection of related documents, similar to a database in relational systems.Mappings define the structure and data types of fields within an index, specifying how data should be indexed and stored to enable precise search and analysis.
We will create a "movies" index with the required fields. To ensure the index exists, we can define a separate function to create it if it does not already exist. Additionally, we will provide custom mappings to Elasticsearch instead of relying on auto-detection. This approach offers greater control over the search functionality, particularly for searching by genre in this example.
from jsonfrom elasticsearch import Elasticsearches = Elasticsearch("http://localhost:9200")index_name = "movies"def create_index():mappings = {"mappings": {"properties": {"id": {"type": "keyword"},"title": {"type": "text"},"director": {"type": "text"},"year": {"type": "integer"},"language": {"type": "keyword"},"genre": {"type": "keyword"},"rating": {"type": "float"},}}}if not es.indices.exists(index=index_name):es.indices.create(index=index_name, body=mappings)print(f"Index '{index_name}' created.")else:print(f"Index '{index_name}' already exists.")
movies.json
[{"id": "tt0133093","title": "The Matrix","director": "Lana Wachowski, Lilly Wachowski","year": 1999,"language": "English","genre": ["Sci-Fi","Action"],"rating": 8.7},{"id": "tt0245429","title": "Spirited Away","director": "Hayao Miyazaki","year": 2001,"language": "Japanese","genre": ["Animation","Fantasy"],"rating": 8.6}]
@app.route('/init', methods=['GET'])def init():create_index()with open("movies.json") as movies_file:movies = json.load(movies_file)for movie in movies:es.index(document=movie, id=movie["id"], index=index_name)return jsonify({ "message": "Movies initiated in movies index"}), 200
Get all movies
This endpoint provides a simple way to view the entire movie collection. We are using match_all, which returns all the movies.@app.route('/movies', methods=['GET'])def get_movies():query = {"query": {"match_all": {}}}result = es.search(index=index_name, body=query)return jsonify([hit["_source"] for hit in result["hits"]["hits"]]), 200
Get a movie by ID
Fetch details of a specific movie using its unique identifier.GET /movies/_doc/tt1375666
@app.route('/movies/<id>', methods=['GET'])def get_movie_by_id(id):try:result = es.get(index=index_name, id=id)return jsonify(result["_source"])except Exception as e:return jsonify(str(e)), 404
Create a movie
Add a new movie to the Elasticsearch index with details like title, director, year, language, genre, and rating.@app.route('/movies', methods=['POST'])def create_movie():data = request.jsonres = es.index(index=index_name, document=data)return jsonify({"message": "Movie added successfully.", "id": res['_id']}), 201
Update, Delete a movie
Modify the details of an existing movie or remove a movie from the index.@app.route('/movies/<id>', methods=['PUT'])def update_movie(id):data = request.jsontry:res = es.update(index=index_name, id=id, body={"doc": data})return jsonify({"message": "Movie updated successfully.", "id": res['_id']}), 200except Exception as e:return jsonify({"error": str(e)}), 404@app.route('/movies/<id>', methods=['DELETE'])def delete_movie(id):try:es.delete(index=index_name, id=id)return jsonify({"message": "Movie deleted successfully."}), 204except Exception as e:return jsonify({"error": str(e)}), 204
Search a movie
Search is the primary functionality when using Elasticsearch. For this exercise, we are building a search feature to find movies by either the title or the director. We perform the search by querying both the title and director fields simultaneously using the multi_match query. To support prefix searches, we use the phrase_prefix type.http://127.0.0.1:5000/movies/search?q=king
@app.route('/movies/search', methods=['GET'])def search():search_term = request.args.get("q", "")query = {"query": {"multi_match": {"query": search_term,"fields": [ "title", "director" ],"type": "phrase_prefix"}}}result = es.search(index=index_name, body=query)return jsonify([hit["_source"] for hit in result["hits"]["hits"]]), 200
Comments
Post a Comment