Skip to content

Commit 052a3c3

Browse files
committed
Implement full text search on all models
1 parent a85a0b0 commit 052a3c3

21 files changed

+253
-33
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""empty message
2+
3+
Revision ID: b6f593818541
4+
Revises: c2d7aa36d0e7
5+
Create Date: 2022-06-17 18:35:58.043728
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from doku.models import TSVector
11+
12+
13+
# revision identifiers, used by Alembic.
14+
15+
revision = 'b6f593818541'
16+
down_revision = 'c2d7aa36d0e7'
17+
branch_labels = None
18+
depends_on = None
19+
20+
21+
def upgrade():
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.add_column('doku_document', sa.Column('__ts_vector__', TSVector(), sa.Computed("to_tsvector('english', name)", persisted=True), nullable=True))
24+
op.create_index('doku_document___ts_vector__', 'doku_document', ['__ts_vector__'], unique=False, postgresql_using='gin')
25+
# ### end Alembic commands ###
26+
27+
28+
def downgrade():
29+
# ### commands auto generated by Alembic - please adjust! ###
30+
op.drop_index('doku_document___ts_vector__', table_name='doku_document', postgresql_using='gin')
31+
op.drop_column('doku_document', '__ts_vector__')
32+
# ### end Alembic commands ###
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""empty message
2+
3+
Revision ID: c7d3e7e86fe0
4+
Revises: b6f593818541
5+
Create Date: 2022-06-17 18:58:45.565407
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from doku.models import TSVector
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'c7d3e7e86fe0'
15+
down_revision = 'b6f593818541'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.add_column('doku_resource', sa.Column('__ts_vector__', TSVector(), sa.Computed("to_tsvector('english', name)", persisted=True), nullable=True))
23+
op.create_index('doku_resource___ts_vector__', 'doku_resource', ['__ts_vector__'], unique=False, postgresql_using='gin')
24+
op.add_column('doku_snippet', sa.Column('__ts_vector__', TSVector(), sa.Computed("to_tsvector('english', name)", persisted=True), nullable=True))
25+
op.create_index('doku_snippet___ts_vector__', 'doku_snippet', ['__ts_vector__'], unique=False, postgresql_using='gin')
26+
op.add_column('doku_stylesheet', sa.Column('__ts_vector__', TSVector(), sa.Computed("to_tsvector('english', name)", persisted=True), nullable=True))
27+
op.create_index('doku_stylesheet___ts_vector__', 'doku_stylesheet', ['__ts_vector__'], unique=False, postgresql_using='gin')
28+
op.add_column('doku_template', sa.Column('__ts_vector__', TSVector(), sa.Computed("to_tsvector('english', name)", persisted=True), nullable=True))
29+
op.create_index('doku_template___ts_vector__', 'doku_template', ['__ts_vector__'], unique=False, postgresql_using='gin')
30+
# ### end Alembic commands ###
31+
32+
33+
def downgrade():
34+
# ### commands auto generated by Alembic - please adjust! ###
35+
op.drop_index('doku_template___ts_vector__', table_name='doku_template', postgresql_using='gin')
36+
op.drop_column('doku_template', '__ts_vector__')
37+
op.drop_index('doku_stylesheet___ts_vector__', table_name='doku_stylesheet', postgresql_using='gin')
38+
op.drop_column('doku_stylesheet', '__ts_vector__')
39+
op.drop_index('doku_snippet___ts_vector__', table_name='doku_snippet', postgresql_using='gin')
40+
op.drop_column('doku_snippet', '__ts_vector__')
41+
op.drop_index('doku_resource___ts_vector__', table_name='doku_resource', postgresql_using='gin')
42+
op.drop_column('doku_resource', '__ts_vector__')
43+
# ### end Alembic commands ###

doku/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.5.0"
1+
__version__ = "0.5.1"
22
__author__ = "Jonas Drotleff <j.drotleff@desk-lab.de>"
33
__all__ = ["create_app", "cli"]
44

doku/blueprints/base.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, render_template
1+
from flask import Blueprint, render_template, request
22

33
from doku.models import db
44
from doku.models.document import Document
@@ -15,9 +15,15 @@ def index():
1515
ordering, order, direction = get_ordering(
1616
Document, default_order="last_updated", default_dir="desc"
1717
)
18-
documents = (
19-
db.session.query(Document).order_by(ordering).paginate(page=page, per_page=10)
20-
)
18+
query: str = request.args.get("query", "", type=str)
19+
documents = db.session.query(Document)
20+
if query != "":
21+
documents = documents.filter(Document.__ts_vector__.match(query))
22+
documents = documents.order_by(ordering).paginate(page=page, per_page=10)
2123
return render_template(
22-
"sites/index.html", documents=documents, order=order, direction=direction
24+
"sites/index.html",
25+
documents=documents,
26+
order=order,
27+
direction=direction,
28+
query=query,
2329
)

doku/blueprints/resources.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,18 @@ def index():
4343
db.session.add(resource)
4444
db.session.commit()
4545
page = get_pagination_page()
46-
resources = db.session.query(Resource).paginate(page=page, per_page=10)
46+
query: str = request.args.get("query", "", type=str)
47+
resources = db.session.query(Resource)
48+
if query != "":
49+
resources = resources.filter(Resource.__ts_vector__.match(query))
50+
resources = resources.paginate(page=page, per_page=10)
4751

4852
resource_schemas = ResourceSchema(session=db.session, many=True)
4953
return render_template(
5054
"sites/resources.html",
5155
resources_json=resource_schemas.dumps(resources.items),
5256
resources=resources,
57+
query=query,
5358
)
5459

5560

doku/blueprints/snippet.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
@login_required
1616
def index():
1717
page = get_pagination_page()
18-
snippets = db.session.query(Snippet).paginate(page=page, per_page=10)
19-
return render_template("sites/snippets.html", snippets=snippets)
18+
query: str = request.args.get("query", "", type=str)
19+
snippets = db.session.query(Snippet)
20+
if query != "":
21+
snippets = snippets.filter(Snippet.__ts_vector__.match(query))
22+
snippets = snippets.paginate(page=page, per_page=10)
23+
return render_template(
24+
"sites/snippets.html",
25+
snippets=snippets,
26+
query=query,
27+
)
2028

2129

2230
@bp.route("/<int:snippet_id>", methods=["GET", "POST"])

doku/blueprints/stylesheets.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,17 @@ def index():
4646
db.session.commit()
4747

4848
page = get_pagination_page()
49-
stylesheets = db.session.query(Stylesheet).paginate(page=page, per_page=10)
49+
query: str = request.args.get("query", "", type=str)
50+
stylesheets = db.session.query(Stylesheet)
51+
if query != "":
52+
stylesheets = stylesheets.filter(Stylesheet.__ts_vector__.match(query))
53+
stylesheets = stylesheets.paginate(page=page, per_page=10)
5054

5155
stylesheet_schemas = StylesheetSchema(
5256
session=db.session, many=True, include=("source",)
5357
)
5458
return render_template(
5559
"sites/stylesheets.html",
5660
stylesheets_json=stylesheet_schemas.dumps(stylesheets.items),
57-
stylesheets=stylesheets,
61+
stylesheets=stylesheets, query=query
5862
)

doku/blueprints/template.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, render_template
1+
from flask import Blueprint, render_template, request
22

33
from doku import db
44
from doku.models.schemas import TemplateSchema, StylesheetSchema
@@ -13,19 +13,17 @@
1313
@login_required
1414
def index_all():
1515
page = get_pagination_page()
16-
ordering, order, direction = get_ordering(
17-
Template, default_order="name", default_dir="asc"
18-
)
19-
templates = (
20-
db.session.query(Template).order_by(ordering).paginate(page=page, per_page=10)
21-
)
16+
query: str = request.args.get("query", "", type=str)
17+
templates = db.session.query(Template)
18+
if query != "":
19+
templates = templates.filter(Template.__ts_vector__.match(query))
20+
templates = templates.paginate(page=page, per_page=10)
2221
template_schema = TemplateSchema(session=db.session, many=True)
2322
return render_template(
2423
"sites/templates.html",
2524
templates_json=template_schema.dumps(templates.items),
2625
templates=templates,
27-
order=order,
28-
direction=direction,
26+
query=query,
2927
)
3028

3129

doku/models/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
from flask_babel import format_timedelta, format_datetime
44
from flask_sqlalchemy import SQLAlchemy
55
from marshmallow_sqlalchemy import auto_field
6+
from sqlalchemy import TypeDecorator
7+
from sqlalchemy.dialects.postgresql import TSVECTOR
68

79
db = SQLAlchemy(session_options={"autoflush": False})
810

911

12+
class TSVector(TypeDecorator):
13+
impl = TSVECTOR
14+
15+
1016
class DateMixin:
1117
created_on = db.Column(db.DateTime(timezone=True), default=datetime.now)
1218
last_updated = db.Column(

doku/models/document.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from flask import session
2+
from sqlalchemy import Index
23

3-
from doku.models import db, DateMixin
4+
from doku.models import db, DateMixin, TSVector
45

56

67
class Document(db.Model, DateMixin):
@@ -34,6 +35,18 @@ class Document(db.Model, DateMixin):
3435
overlaps="variables, document",
3536
)
3637

38+
__ts_vector__ = db.Column(
39+
TSVector(),
40+
db.Computed(
41+
"to_tsvector('english', name)",
42+
persisted=True
43+
)
44+
)
45+
46+
__table_args__ = (
47+
Index('doku_document___ts_vector__', __ts_vector__, postgresql_using='gin'),
48+
)
49+
3750
def __init__(self, *args, author_id=None, author=None, **kwargs):
3851
if author_id is None and author is None:
3952
author_id = session.get("id", None)

doku/models/resource.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import string
33

44
from flask import url_for
5+
from sqlalchemy import Index
56
from werkzeug.utils import secure_filename
67

7-
from doku.models import db, DateMixin
8+
from doku.models import db, DateMixin, TSVector
89

910

1011
class Resource(db.Model, DateMixin):
@@ -16,6 +17,18 @@ class Resource(db.Model, DateMixin):
1617
name = db.Column(db.String(255), unique=False, nullable=False)
1718
filename = db.Column(db.String(255), unique=True, nullable=False)
1819

20+
__ts_vector__ = db.Column(
21+
TSVector(),
22+
db.Computed(
23+
"to_tsvector('english', name)",
24+
persisted=True
25+
)
26+
)
27+
28+
__table_args__ = (
29+
Index('doku_resource___ts_vector__', __ts_vector__, postgresql_using='gin'),
30+
)
31+
1932
@property
2033
def url(self):
2134
return url_for("resources.view", resource_id=self.id)

doku/models/snippet.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from sqlalchemy import event
1+
from sqlalchemy import event, Index
22

3-
from doku.models import db, DateMixin
3+
from doku.models import db, DateMixin, TSVector
44
from doku.utils.markdown import compile_content
55

66

@@ -23,6 +23,18 @@ class Snippet(db.Model, DateMixin):
2323

2424
used_by = db.relationship("Variable")
2525

26+
__ts_vector__ = db.Column(
27+
TSVector(),
28+
db.Computed(
29+
"to_tsvector('english', name)",
30+
persisted=True
31+
)
32+
)
33+
34+
__table_args__ = (
35+
Index('doku_snippet___ts_vector__', __ts_vector__, postgresql_using='gin'),
36+
)
37+
2638
def __str__(self):
2739
return f"Snippet {self.name}"
2840

doku/models/template.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from jinja2 import Environment, meta
55
from jinja2 import Template as Jinja2Template
66
from pygments.formatters.html import HtmlFormatter
7+
from sqlalchemy import Index
78
from weasyprint import HTML, CSS
89

9-
from doku.models import db, DateMixin
10+
from doku.models import db, DateMixin, TSVector
1011
from doku.utils.weasyfetch import url_fetcher
1112

1213
DEFAULT_TEMPLATE = """<!doctype html>
@@ -49,6 +50,18 @@ class Template(db.Model, DateMixin):
4950
"Stylesheet", secondary=template_stylesheet_relation, back_populates="templates"
5051
)
5152

53+
__ts_vector__ = db.Column(
54+
TSVector(),
55+
db.Computed(
56+
"to_tsvector('english', name)",
57+
persisted=True
58+
)
59+
)
60+
61+
__table_args__ = (
62+
Index('doku_template___ts_vector__', __ts_vector__, postgresql_using='gin'),
63+
)
64+
5265
def __str__(self):
5366
return self.name
5467

@@ -110,6 +123,18 @@ class Stylesheet(db.Model, DateMixin):
110123

111124
MAX_CONTENT_LENGTH = 125000
112125

126+
__ts_vector__ = db.Column(
127+
TSVector(),
128+
db.Computed(
129+
"to_tsvector('english', name)",
130+
persisted=True
131+
)
132+
)
133+
134+
__table_args__ = (
135+
Index('doku_stylesheet___ts_vector__', __ts_vector__, postgresql_using='gin'),
136+
)
137+
113138
def __str__(self):
114139
return self.name
115140

doku/static/src/sites/templates.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
Create new template
1010
</button>
1111
</div>
12+
<form action="" class="mb-2" method="get">
13+
<input type="hidden" name="page" :value="formPage"/>
14+
<div class="form-inline">
15+
<input class="form-input input-sm" type="text" name="query" :value="formQuery" placeholder="Search">
16+
</div>
17+
<button class="btn btn-sm btn-primary" type="submit">
18+
Search
19+
</button>
20+
</form>
1221

1322
<template-item
1423
v-for="template in templates"
@@ -61,6 +70,12 @@ export default {
6170
components: {
6271
TemplateItem, Modal, PlusIcon
6372
},
73+
data() {
74+
return {
75+
formPage: window._formPage,
76+
formQuery: window._formQuery,
77+
};
78+
},
6479
computed: mapState({
6580
templates: state => state.template.templates,
6681
}),

0 commit comments

Comments
 (0)