feat(trash): exclude trashed rows from notes + knowledge read paths
This commit is contained in:
@@ -68,7 +68,10 @@ def _apply_type_filter(stmt, note_type: str | None):
|
|||||||
|
|
||||||
'task' = any task (status not null); 'plan' = a task with task_kind='plan';
|
'task' = any task (status not null); 'plan' = a task with task_kind='plan';
|
||||||
any other non-empty type = a non-task note of that note_type; None = all.
|
any other non-empty type = a non-task note of that note_type; None = all.
|
||||||
|
|
||||||
|
Trashed rows (deleted_at set) are always excluded.
|
||||||
"""
|
"""
|
||||||
|
stmt = stmt.where(Note.deleted_at.is_(None))
|
||||||
if note_type == "task":
|
if note_type == "task":
|
||||||
return stmt.where(Note.status.isnot(None))
|
return stmt.where(Note.status.isnot(None))
|
||||||
if note_type == "plan":
|
if note_type == "plan":
|
||||||
@@ -173,6 +176,8 @@ async def _semantic_knowledge_search(
|
|||||||
is_task=is_task_filter,
|
is_task=is_task_filter,
|
||||||
)
|
)
|
||||||
for _score, note in candidates:
|
for _score, note in candidates:
|
||||||
|
if note.deleted_at is not None:
|
||||||
|
continue
|
||||||
if note_type == "task" and not note.is_task:
|
if note_type == "task" and not note.is_task:
|
||||||
continue
|
continue
|
||||||
elif note_type == "plan" and (not note.is_task or note.task_kind != "plan"):
|
elif note_type == "plan" and (not note.is_task or note.task_kind != "plan"):
|
||||||
@@ -223,6 +228,7 @@ async def get_knowledge_counts(user_id: int, tags: list[str] | None = None) -> d
|
|||||||
select(Note.note_type, func.count(Note.id))
|
select(Note.note_type, func.count(Note.id))
|
||||||
.where(Note.user_id == user_id)
|
.where(Note.user_id == user_id)
|
||||||
.where(Note.status.is_(None))
|
.where(Note.status.is_(None))
|
||||||
|
.where(Note.deleted_at.is_(None))
|
||||||
.where(Note.note_type.in_(["note", "person", "place", "list"]))
|
.where(Note.note_type.in_(["note", "person", "place", "list"]))
|
||||||
.group_by(Note.note_type)
|
.group_by(Note.note_type)
|
||||||
)
|
)
|
||||||
@@ -237,6 +243,7 @@ async def get_knowledge_counts(user_id: int, tags: list[str] | None = None) -> d
|
|||||||
select(func.count(Note.id))
|
select(func.count(Note.id))
|
||||||
.where(Note.user_id == user_id)
|
.where(Note.user_id == user_id)
|
||||||
.where(Note.status.isnot(None))
|
.where(Note.status.isnot(None))
|
||||||
|
.where(Note.deleted_at.is_(None))
|
||||||
)
|
)
|
||||||
if tags:
|
if tags:
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@@ -251,6 +258,7 @@ async def get_knowledge_counts(user_id: int, tags: list[str] | None = None) -> d
|
|||||||
.where(Note.user_id == user_id)
|
.where(Note.user_id == user_id)
|
||||||
.where(Note.status.isnot(None))
|
.where(Note.status.isnot(None))
|
||||||
.where(Note.task_kind == "plan")
|
.where(Note.task_kind == "plan")
|
||||||
|
.where(Note.deleted_at.is_(None))
|
||||||
)
|
)
|
||||||
if tags:
|
if tags:
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@@ -314,6 +322,7 @@ async def get_knowledge_by_ids(user_id: int, ids: list[int]) -> list[dict]:
|
|||||||
select(Note)
|
select(Note)
|
||||||
.where(Note.user_id == user_id)
|
.where(Note.user_id == user_id)
|
||||||
.where(Note.id.in_(ids))
|
.where(Note.id.in_(ids))
|
||||||
|
.where(Note.deleted_at.is_(None))
|
||||||
)
|
)
|
||||||
rows = list((await session.execute(stmt)).scalars().all())
|
rows = list((await session.execute(stmt)).scalars().all())
|
||||||
by_id = {n.id: n for n in rows}
|
by_id = {n.id: n for n in rows}
|
||||||
@@ -328,6 +337,7 @@ async def get_people_and_places_context(user_id: int) -> str:
|
|||||||
.where(Note.user_id == user_id)
|
.where(Note.user_id == user_id)
|
||||||
.where(Note.note_type.in_(["person", "place"]))
|
.where(Note.note_type.in_(["person", "place"]))
|
||||||
.where(Note.status.is_(None))
|
.where(Note.status.is_(None))
|
||||||
|
.where(Note.deleted_at.is_(None))
|
||||||
.order_by(Note.title.asc())
|
.order_by(Note.title.asc())
|
||||||
.limit(50)
|
.limit(50)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ async def create_note(
|
|||||||
async def get_note(user_id: int, note_id: int) -> Note | None:
|
async def get_note(user_id: int, note_id: int) -> Note | None:
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
result = await session.execute(
|
result = await session.execute(
|
||||||
select(Note).where(Note.id == note_id, Note.user_id == user_id)
|
select(Note).where(
|
||||||
|
Note.id == note_id, Note.user_id == user_id, Note.deleted_at.is_(None)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return result.scalars().first()
|
return result.scalars().first()
|
||||||
|
|
||||||
@@ -135,8 +137,10 @@ async def list_notes(
|
|||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
) -> tuple[list[Note], int]:
|
) -> tuple[list[Note], int]:
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
query = select(Note).where(Note.user_id == user_id)
|
query = select(Note).where(Note.user_id == user_id, Note.deleted_at.is_(None))
|
||||||
count_query = select(func.count(Note.id)).where(Note.user_id == user_id)
|
count_query = select(func.count(Note.id)).where(
|
||||||
|
Note.user_id == user_id, Note.deleted_at.is_(None)
|
||||||
|
)
|
||||||
|
|
||||||
# Filter by task vs note
|
# Filter by task vs note
|
||||||
if is_task is True:
|
if is_task is True:
|
||||||
@@ -239,6 +243,7 @@ async def get_note_by_title(user_id: int, title: str) -> Note | None:
|
|||||||
select(Note).where(
|
select(Note).where(
|
||||||
Note.user_id == user_id,
|
Note.user_id == user_id,
|
||||||
func.lower(Note.title) == func.lower(title.strip()),
|
func.lower(Note.title) == func.lower(title.strip()),
|
||||||
|
Note.deleted_at.is_(None),
|
||||||
).limit(1)
|
).limit(1)
|
||||||
)
|
)
|
||||||
return result.scalars().first()
|
return result.scalars().first()
|
||||||
@@ -402,7 +407,9 @@ async def search_notes_for_context(
|
|||||||
pattern = f"%{escaped}%"
|
pattern = f"%{escaped}%"
|
||||||
keyword_filters.append(or_(Note.title.ilike(pattern), Note.body.ilike(pattern)))
|
keyword_filters.append(or_(Note.title.ilike(pattern), Note.body.ilike(pattern)))
|
||||||
|
|
||||||
query = select(Note).where(Note.user_id == user_id, or_(*keyword_filters))
|
query = select(Note).where(
|
||||||
|
Note.user_id == user_id, or_(*keyword_filters), Note.deleted_at.is_(None)
|
||||||
|
)
|
||||||
if orphan_only:
|
if orphan_only:
|
||||||
query = query.where(Note.project_id.is_(None))
|
query = query.where(Note.project_id.is_(None))
|
||||||
elif project_id is not None:
|
elif project_id is not None:
|
||||||
@@ -421,7 +428,9 @@ async def get_notes_by_ids(user_id: int, note_ids: list[int]) -> dict[int, Note]
|
|||||||
return {}
|
return {}
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
result = await session.execute(
|
result = await session.execute(
|
||||||
select(Note).where(Note.user_id == user_id, Note.id.in_(note_ids))
|
select(Note).where(
|
||||||
|
Note.user_id == user_id, Note.id.in_(note_ids), Note.deleted_at.is_(None)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return {n.id: n for n in result.scalars().all()}
|
return {n.id: n for n in result.scalars().all()}
|
||||||
|
|
||||||
@@ -444,6 +453,7 @@ async def get_backlinks(user_id: int, note_id: int) -> list[dict]:
|
|||||||
select(Note.id, Note.title, Note.status).where(
|
select(Note.id, Note.title, Note.status).where(
|
||||||
Note.user_id == user_id,
|
Note.user_id == user_id,
|
||||||
Note.id != note_id,
|
Note.id != note_id,
|
||||||
|
Note.deleted_at.is_(None),
|
||||||
or_(Note.body.like(pattern), Note.body.like(pattern_alias)),
|
or_(Note.body.like(pattern), Note.body.like(pattern_alias)),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user