feat(ttp): E.3.3 repository — insert_tags + listing rollups (dual backend)
Dialect-split: portable rollup queries on TTPMixin; bulk insert with ON CONFLICT DO NOTHING / INSERT IGNORE in the per-dialect repos. Confidence-floor (< 0.3) drop applied at mixin layer before the dialect hook. BaseRepository now declares the six TTP methods abstract. Tests in tests/web/db/test_ttp_repo.py flipped from pytest.fail stubs to real dual-backend behavioral tests; tests/ttp/test_confidence.py drop-below-floor xfail removed.
This commit is contained in:
@@ -15,10 +15,11 @@ from __future__ import annotations
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from sqlalchemy import func, select, text, literal_column
|
||||
from sqlalchemy.dialects.mysql import insert as mysql_insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
|
||||
from decnet.web.db.models import Log
|
||||
from decnet.web.db.models import Log, TTPTag
|
||||
from decnet.web.db.mysql.database import get_async_engine
|
||||
from decnet.web.db.sqlmodel_repo import SQLModelRepository
|
||||
|
||||
@@ -151,6 +152,26 @@ class MySQLRepository(SQLModelRepository):
|
||||
# TEXT-stored JSON, same behavior we rely on in SQLite.
|
||||
return text(f"JSON_UNQUOTE(JSON_EXTRACT(fields, '$.{key}')) = :val")
|
||||
|
||||
async def _insert_tags_or_ignore(self, rows: list[TTPTag]) -> int:
|
||||
"""Bulk-insert with MySQL's ``INSERT IGNORE`` on the ``uuid`` PK.
|
||||
|
||||
``rowcount`` returns the number of NEW rows; duplicates are
|
||||
silently ignored (matching the SQLite ``ON CONFLICT DO NOTHING``
|
||||
contract).
|
||||
"""
|
||||
if not rows:
|
||||
return 0
|
||||
payload = [r.model_dump() for r in rows]
|
||||
stmt = (
|
||||
mysql_insert(TTPTag.__table__) # type: ignore[attr-defined]
|
||||
.values(payload)
|
||||
.prefix_with("IGNORE")
|
||||
)
|
||||
async with self._session() as session:
|
||||
result = await session.execute(stmt)
|
||||
await session.commit()
|
||||
return int(result.rowcount or 0)
|
||||
|
||||
async def get_log_histogram(
|
||||
self,
|
||||
search: Optional[str] = None,
|
||||
|
||||
Reference in New Issue
Block a user