Skip to content
This repository was archived by the owner on May 20, 2018. It is now read-only.

Commit d92f295

Browse files
committed
Merge pull request #69 from levlaz/feature-search
Feature search
2 parents b566354 + ed37966 commit d92f295

File tree

7 files changed

+101
-0
lines changed

7 files changed

+101
-0
lines changed

app/main/forms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ class ShareForm(Form):
1919
class NotebookForm(Form):
2020
title = StringField('Title:', validators=[Required()])
2121
submit = SubmitField('Submit')
22+
23+
class SearchForm(Form):
24+
search_field = StringField()
25+
submit = SubmitField('Search')

app/main/views.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,18 @@ def notebook(id):
183183
abort(403)
184184
return render_template('app/notebook.html', notebook=notebook, notes=notebook._show_notes())
185185

186+
@main.route('/search')
187+
@login_required
188+
def search():
189+
form = SearchForm()
190+
if request.args.get('search_field', ''):
191+
query = request.args.get('search_field', '')
192+
results = Note.query.search(query).all()
193+
if len(results) == 0:
194+
flash('Hmm, we did not find any braindumps matching your search. Try again?')
195+
return render_template('app/search.html', form=form, notes=results)
196+
return render_template('app/search.html', form=form)
197+
186198

187199
@main.route('/shutdown')
188200
def server_shutdown():

app/models.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,17 @@
88
from datetime import datetime
99
from app.exceptions import ValidationError
1010

11+
# Full Text Search
12+
from flask.ext.sqlalchemy import SQLAlchemy, BaseQuery
13+
from sqlalchemy_searchable import SearchQueryMixin
14+
from sqlalchemy_searchable import make_searchable
15+
from sqlalchemy_utils.types import TSVectorType
16+
1117
import bleach
1218
import hashlib
1319

20+
make_searchable()
21+
1422
@login_manager.user_loader
1523
def load_user(user_id):
1624
return User.query.get(int(user_id))
@@ -179,7 +187,11 @@ def to_json(self):
179187
def __repr__(self):
180188
return '<User {0}>'.format(self.username)
181189

190+
class NoteQuery(BaseQuery, SearchQueryMixin):
191+
pass
192+
182193
class Note(db.Model):
194+
query_class = NoteQuery
183195
__tablename__ = 'notes'
184196
id = db.Column(db.Integer, primary_key=True)
185197
title = db.Column(db.String(200))
@@ -192,6 +204,9 @@ class Note(db.Model):
192204

193205
tags = db.relationship("Tag", secondary=note_tag, backref="Note", passive_deletes=True)
194206

207+
# Full Text Search
208+
search_vector = db.Column(TSVectorType('title', 'body'))
209+
195210
def to_json(self):
196211
json_note = {
197212
'url': url_for('api.get_note', id=self.id, _external=True),

app/templates/app/app_base.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<li class="sidebar-item">
2222
<a href="/"><img class="app-logo" src="{{ url_for('static', filename='images/logo.png')}}"></img></a>
2323
</li>
24+
<li class="sidebar-item" data-toggle="tooltip" data-placement="right" title="Search">
25+
<a href="/search"><span class="nav-item glyphicon glyphicon-search" aria-hidden="true"></a></span>
26+
</li>
2427
<li class="sidebar-item" data-toggle="tooltip" data-placement="right" title="Add a Note">
2528
<a href="/add"><span class="nav-item glyphicon glyphicon-plus" aria-hidden="true"></a></span>
2629
</li>

app/templates/app/search.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{% extends "app/app_base.html" %}
2+
{% import "bootstrap/wtf.html" as wtf %}
3+
{% import "_macros.html" as macros %}
4+
5+
{% block title %}BrainDump{% endblock %}
6+
7+
{% block page_content %}
8+
9+
<h1 class="top"> Search for Anything </h1>
10+
11+
<form class="form form-horizontal" action="/search" role="form">
12+
{{ wtf.form_field(form.search_field) }}
13+
{{ wtf.form_field(form.submit, class="btn btn-default form-group") }}
14+
</form>
15+
16+
{% include 'app/_notes.html' %}
17+
18+
19+
{% endblock %}
20+
21+
{% block scripts %}
22+
{{ super() }}
23+
<script src="{{ url_for('static', filename='js/libs/marked.js') }}"></script>
24+
{% endblock %}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Add Full Text Search
2+
3+
Revision ID: 488e3dae5a17
4+
Revises: 34fa673d7905
5+
Create Date: 2015-12-07 10:49:44.124657
6+
7+
"""
8+
9+
# revision identifiers, used by Alembic.
10+
revision = '488e3dae5a17'
11+
down_revision = '34fa673d7905'
12+
13+
from alembic import op
14+
import sqlalchemy as sa
15+
import sqlalchemy_utils
16+
from sqlalchemy_searchable import sync_trigger
17+
18+
def upgrade():
19+
### commands auto generated by Alembic - please adjust! ###
20+
conn = op.get_bind()
21+
op.drop_table('sections')
22+
op.add_column('notes', sa.Column('search_vector', sqlalchemy_utils.types.ts_vector.TSVectorType(), nullable=True))
23+
op.create_index('ix_notes_search_vector', 'notes', ['search_vector'], unique=False, postgresql_using='gin')
24+
### end Alembic commands ###
25+
sync_trigger(conn, 'notes', 'search_vector', ['title', 'body'])
26+
27+
def downgrade():
28+
### commands auto generated by Alembic - please adjust! ###
29+
op.drop_index('ix_notes_search_vector', table_name='notes')
30+
op.drop_column('notes', 'search_vector')
31+
op.create_table('sections',
32+
sa.Column('id', sa.INTEGER(), nullable=False),
33+
sa.Column('title', sa.VARCHAR(length=200), autoincrement=False, nullable=True),
34+
sa.Column('notebook_id', sa.INTEGER(), autoincrement=False, nullable=True),
35+
sa.ForeignKeyConstraint(['notebook_id'], [u'notebooks.id'], name=u'sections_notebook_id_fkey'),
36+
sa.PrimaryKeyConstraint('id', name=u'sections_pkey')
37+
)
38+
### end Alembic commands ###

requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,28 @@ Markdown==2.6.4
1616
MarkupSafe==0.23
1717
Pygments==2.0.2
1818
SQLAlchemy==1.0.9
19+
SQLAlchemy-Searchable==0.9.3
20+
SQLAlchemy-Utils==0.31.4
1921
WTForms==2.0.2
2022
Werkzeug==0.11.2
2123
alembic==0.8.3
2224
argparse==1.2.1
2325
bleach==1.4.2
2426
blinker==1.4
2527
coverage==4.0.2
28+
decorator==4.0.4
2629
dominate==2.1.16
2730
html5lib==0.9999999
2831
httpie==0.9.2
2932
itsdangerous==0.24
3033
psycopg2==2.6.1
34+
pyparsing==2.0.6
3135
python-editor==0.4
3236
requests==2.8.1
3337
selenium==2.48.0
3438
six==1.10.0
3539
unittest-xml-reporting==1.13.0
40+
validators==0.9
3641
visitor==0.1.2
3742
wheel==0.24.0
3843
wsgiref==0.1.2

0 commit comments

Comments
 (0)