How do I structure a Flask/MySQL query to allow a search of a column for text submitted by a user?

Viewed 102

I have a python/Flask site with a MySQL database containing tropes. I would like to create a form that will allow users to search the tropes for key words. This is what I have so far:

HTML form for submitting search:

<form action="/search_tropes" method="POST">
  <label for="search_query">Search:</label>
  <input type="text" class="form-control" id="search_query" aria-describedby="search_query_Help" name="search_query">
  <button type="submit" class="btn btn-primary">Search</button>
</form>

This is the route for the search:

@app.route('/search_tropes', methods = ['POST'])
def searchQuery():
    data = {
        'search': request.form['search_query']
    }
    searched_tropes = Trope.searchTropes(data)
    return render_template('search.html', searched_tropes = searched_tropes)

This is the class method for the search with the query:

@classmethod
def searchTropes(cls, data):
    query = "SELECT * FROM tropes WHERE content LIKE %(search)s;"
    results = connectToMySQL('htb_schema').query_db(query, data)
    return results

This is currently working to search the database, but it will only return results that are an exact match, which is not very helpful. I would like to have it search for tropes that contain the search anywhere in the content column, which I know I can do using '%search%' in the query to allow characters before and after the search term; however, I am having trouble implementing this with the prepared statement to avoid SQL injections. This is what I have tried so far:

Modifying the data to insert the % around the form data:

data = {
        'search': '%' + request.form['search_query'] + '%'
    }

Which returns a Boolean result instead of the list of tropes.

I've also tried using Concat and another variable for the queries:

# query = "SET @search = %(search)s; SELECT * FROM tropes WHERE content LIKE CONCAT("%",@search,"%");"
# query = "SET @search = %(search)s; SELECT * FROM tropes WHERE content LIKE "%"+@search+"%";"
# query = "SELECT * FROM tropes WHERE content LIKE CONCAT('%', %(search)s, '%');"
# query = "SELECT * FROM tropes WHERE content LIKE CONCAT(%(search)s,"%");"
# query = "SELECT * FROM tropes WHERE content LIKE "%"+%(search)s+"%";"

But these also return a Boolean or I get the error: TypeError: not all arguments converted during string formatting.

Please let me know if you have any suggestions to make this search work, or if there is an easier/better method for searching the database for tropes with content that contains the search.

Edit: I just found out about FULLTEXT and using MATCH() and AGAINST() which has improved the situation, but it will only search for that word if it is isolated; searching for 'car' will return 'car accident' or 'buys a car' but will not return 'racecar' or 'cartoon'. I'm not sure if there is a way to add on to the FULLTEXT query to allow this.

0 Answers
Related