server/tags: reduce number of queries

On a test page with 50 tags, 158 queries were reduced to 3:

1. Get the authenticated user
2. Get tags for given page
3. Count all tags

Looks just about right.
This commit is contained in:
rr- 2016-05-08 20:05:13 +02:00
parent d813601d92
commit 339c9a3333
8 changed files with 33 additions and 17 deletions

View file

@ -11,9 +11,12 @@ class BaseSearchConfig(object):
SORT_DESC = -1 SORT_DESC = -1
SORT_ASC = 1 SORT_ASC = 1
def create_query(self): def create_filter_query(self):
raise NotImplementedError() raise NotImplementedError()
def create_count_query(self):
return self.create_filter_query()
@property @property
def anonymous_filter(self): def anonymous_filter(self):
return None return None

View file

@ -3,7 +3,7 @@ from szurubooru import db
from szurubooru.search.base_search_config import BaseSearchConfig from szurubooru.search.base_search_config import BaseSearchConfig
class CommentSearchConfig(BaseSearchConfig): class CommentSearchConfig(BaseSearchConfig):
def create_query(self): def create_filter_query(self):
return db.session.query(db.Comment).join(db.User) return db.session.query(db.Comment).join(db.User)
def finalize_query(self, query): def finalize_query(self, query):

View file

@ -22,7 +22,7 @@ def _type_transformer(value):
value, available_types)) value, available_types))
class PostSearchConfig(BaseSearchConfig): class PostSearchConfig(BaseSearchConfig):
def create_query(self): def create_filter_query(self):
return db.session.query(db.Post) return db.session.query(db.Post)
def finalize_query(self, query): def finalize_query(self, query):

View file

@ -1,6 +1,6 @@
import re import re
import sqlalchemy import sqlalchemy
from szurubooru import errors from szurubooru import db, errors
from szurubooru.search import criteria from szurubooru.search import criteria
class SearchExecutor(object): class SearchExecutor(object):
@ -17,15 +17,22 @@ class SearchExecutor(object):
Parse input and return tuple containing total record count and filtered Parse input and return tuple containing total record count and filtered
entities. entities.
''' '''
filter_query = self._prepare(query_text) filter_query = self.config.create_filter_query()
filter_query = filter_query.options(sqlalchemy.orm.lazyload('*'))
filter_query = self._prepare(filter_query, query_text)
entities = filter_query \ entities = filter_query \
.offset((page - 1) * page_size).limit(page_size).all() .offset((page - 1) * page_size) \
count_query = filter_query.statement \ .limit(page_size) \
.all()
count_query = self.config.create_count_query()
count_query = count_query.options(sqlalchemy.orm.lazyload('*'))
count_query = self._prepare(count_query, query_text)
count_statement = count_query \
.statement \
.with_only_columns([sqlalchemy.func.count()]) \ .with_only_columns([sqlalchemy.func.count()]) \
.order_by(None) .order_by(None)
count = filter_query.session \ count = db.session.execute(count_statement).scalar()
.execute(count_query) \
.scalar()
return (count, entities) return (count, entities)
def execute_and_serialize(self, ctx, serializer): def execute_and_serialize(self, ctx, serializer):
@ -41,10 +48,8 @@ class SearchExecutor(object):
'results': [serializer(entity) for entity in entities], 'results': [serializer(entity) for entity in entities],
} }
def _prepare(self, query_text): def _prepare(self, query, query_text):
''' Parse input and return SQLAlchemy query. ''' ''' Parse input and return SQLAlchemy query. '''
query = self.config.create_query() \
.options(sqlalchemy.orm.lazyload('*'))
for token in re.split(r'\s+', (query_text or '').lower()): for token in re.split(r'\s+', (query_text or '').lower()):
if not token: if not token:
continue continue

View file

@ -2,7 +2,7 @@ from szurubooru import db
from szurubooru.search.base_search_config import BaseSearchConfig from szurubooru.search.base_search_config import BaseSearchConfig
class SnapshotSearchConfig(BaseSearchConfig): class SnapshotSearchConfig(BaseSearchConfig):
def create_query(self): def create_filter_query(self):
return db.session.query(db.Snapshot) return db.session.query(db.Snapshot)
def finalize_query(self, query): def finalize_query(self, query):

View file

@ -1,9 +1,18 @@
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from szurubooru import db from szurubooru import db
from szurubooru.search.base_search_config import BaseSearchConfig from szurubooru.search.base_search_config import BaseSearchConfig
class TagSearchConfig(BaseSearchConfig): class TagSearchConfig(BaseSearchConfig):
def create_query(self): def create_filter_query(self):
return self.create_count_query().options(
joinedload(db.Tag.names),
joinedload(db.Tag.category),
joinedload(db.Tag.suggestions).joinedload(db.Tag.names),
joinedload(db.Tag.implications).joinedload(db.Tag.names)
)
def create_count_query(self):
return db.session.query(db.Tag) return db.session.query(db.Tag)
def finalize_query(self, query): def finalize_query(self, query):

View file

@ -5,7 +5,7 @@ from szurubooru.search.base_search_config import BaseSearchConfig
class UserSearchConfig(BaseSearchConfig): class UserSearchConfig(BaseSearchConfig):
''' Executes searches related to the users. ''' ''' Executes searches related to the users. '''
def create_query(self): def create_filter_query(self):
return db.session.query(db.User) return db.session.query(db.User)
def finalize_query(self, query): def finalize_query(self, query):

View file

@ -56,7 +56,6 @@ def verify_unpaged(executor):
actual_count, actual_posts = executor.execute( actual_count, actual_posts = executor.execute(
input, page=1, page_size=100) input, page=1, page_size=100)
actual_post_ids = list([p.post_id for p in actual_posts]) actual_post_ids = list([p.post_id for p in actual_posts])
print(actual_post_ids, expected_post_ids)
assert actual_count == len(expected_post_ids) assert actual_count == len(expected_post_ids)
if not test_order: if not test_order:
actual_post_ids = sorted(actual_post_ids) actual_post_ids = sorted(actual_post_ids)