mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
Archive search counts from the search index (#814)
The total for an archive search joined `archive LEFT JOIN file_group` to the FTS table. A LEFT JOIN fixes `archive` as the outer table, so SQLite scanned every Archive and ran the MATCH once per row: about 20 seconds of CPU on a Pi with 128k Archives, every time the 60-second totals cache expired. An inner join lets the planner start from the FTS matches (the same count in milliseconds). Every Archive has a FileGroup, so the count is unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
99a0fc275d
commit
8e2ab935af
2 changed files with 30 additions and 1 deletions
|
|
@ -1491,8 +1491,10 @@ def search_archives(session: Session, search_str: str, domain: str, limit: int,
|
|||
)
|
||||
else:
|
||||
if file_group_search:
|
||||
# An inner join lets SQLite start from the FTS matches; a LEFT JOIN pins `archive` as the outer
|
||||
# table, which runs the MATCH once per Archive.
|
||||
count_from = f'''FROM archive a
|
||||
LEFT JOIN file_group fg ON fg.id = a.file_group_id
|
||||
JOIN file_group fg ON fg.id = a.file_group_id
|
||||
{file_group_search.join}'''
|
||||
count_wheres = list(wheres)
|
||||
count_wheres.append(file_group_search.where)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ import pathlib
|
|||
import tempfile
|
||||
from datetime import datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
from PIL import Image
|
||||
from pytz import utc
|
||||
from sqlalchemy import text
|
||||
|
||||
from modules.archive import lib
|
||||
from modules.archive.lib import ArchiveDownloaderConfigValidator
|
||||
|
|
@ -20,6 +22,7 @@ from wrolpi.api_utils import CustomJSONEncoder
|
|||
from wrolpi.collections import Collection
|
||||
from wrolpi.common import get_wrolpi_config
|
||||
from wrolpi.db import get_db_session
|
||||
from wrolpi.files.lib import count_file_groups
|
||||
from wrolpi.files.models import FileGroup
|
||||
from wrolpi.test.common import skip_ci
|
||||
|
||||
|
|
@ -1425,3 +1428,27 @@ async def test_singlefile_to_archive_result_outlives_its_session(async_client, t
|
|||
assert name == 'upload title'
|
||||
assert url == 'https://example.com/upload'
|
||||
assert uploaded.file_group_id == archive.file_group_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize('deep', [False, True])
|
||||
def test_search_archives_count_is_driven_by_the_search_index(test_directory, test_session, archive_factory, deep):
|
||||
"""A search's total starts from the FTS matches. Starting from `archive` runs the MATCH once per Archive,
|
||||
which took ~20 seconds on a Pi with 128k Archives."""
|
||||
archive_factory(domain='example.com', title='solar panels', contents='solar panels')
|
||||
archive_factory(domain='example.com', title='wind turbines', contents='wind turbines')
|
||||
test_session.commit()
|
||||
|
||||
counts = []
|
||||
|
||||
def capture_count(session, statement, params):
|
||||
counts.append((statement, params))
|
||||
return count_file_groups(session, statement, params)
|
||||
|
||||
with mock.patch('modules.archive.lib.count_file_groups', capture_count):
|
||||
file_groups, total = lib.search_archives(test_session, search_str='solar', domain=None, limit=10, offset=0,
|
||||
order=None, tag_names=None, deep=deep)
|
||||
assert total == 1 and len(file_groups) == 1
|
||||
|
||||
(statement, params), = counts
|
||||
plan = [row[3] for row in test_session.execute(text(f'EXPLAIN QUERY PLAN {statement}'), params).fetchall()]
|
||||
assert plan[0].startswith('SCAN fts VIRTUAL TABLE'), plan
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue