This commit is contained in:
Classic298 2025-06-21 04:30:22 +00:00 committed by GitHub
commit 889f43b989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -12,6 +12,7 @@ from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
from sqlalchemy import or_, func, select, and_, text from sqlalchemy import or_, func, select, and_, text
from sqlalchemy.sql import exists from sqlalchemy.sql import exists
from sqlalchemy.sql.expression import bindparam
#################### ####################
# Chat DB Schema # Chat DB Schema
@ -232,6 +233,10 @@ class ChatTable:
if chat is None: if chat is None:
return None return None
# Sanitize message content for null characters before upserting
if isinstance(message.get("content"), str):
message["content"] = message["content"].replace("\x00", "")
chat = chat.chat chat = chat.chat
history = chat.get("history", {}) history = chat.get("history", {})
@ -580,7 +585,7 @@ class ChatTable:
""" """
Filters chats based on a search query using Python, allowing pagination using skip and limit. Filters chats based on a search query using Python, allowing pagination using skip and limit.
""" """
search_text = search_text.lower().strip() search_text = search_text.replace("\u0000", "").lower().strip()
if not search_text: if not search_text:
return self.get_chat_list_by_user_id( return self.get_chat_list_by_user_id(
@ -614,21 +619,19 @@ class ChatTable:
dialect_name = db.bind.dialect.name dialect_name = db.bind.dialect.name
if dialect_name == "sqlite": if dialect_name == "sqlite":
# SQLite case: using JSON1 extension for JSON searching # SQLite case: using JSON1 extension for JSON searching
sqlite_content_sql = (
"EXISTS ("
" SELECT 1 "
" FROM json_each(Chat.chat, '$.messages') AS message "
" WHERE LOWER(message.value->>'content') LIKE '%' || :content_key || '%'"
")"
)
sqlite_content_clause = text(sqlite_content_sql)
query = query.filter( query = query.filter(
( or_(
Chat.title.ilike( Chat.title.ilike(bindparam('title_key')),
f"%{search_text}%" sqlite_content_clause
) # Case-insensitive search in title ).params(title_key=f"%{search_text}%", content_key=search_text)
| text(
"""
EXISTS (
SELECT 1
FROM json_each(Chat.chat, '$.messages') AS message
WHERE LOWER(message.value->>'content') LIKE '%' || :search_text || '%'
)
"""
)
).params(search_text=search_text)
) )
# Check if there are any tags to filter, it should have all the tags # Check if there are any tags to filter, it should have all the tags
@ -663,21 +666,19 @@ class ChatTable:
elif dialect_name == "postgresql": elif dialect_name == "postgresql":
# PostgreSQL relies on proper JSON query for search # PostgreSQL relies on proper JSON query for search
postgres_content_sql = (
"EXISTS ("
" SELECT 1 "
" FROM json_array_elements(Chat.chat->'messages') AS message "
" WHERE LOWER(message->>'content') LIKE '%' || :content_key || '%'"
")"
)
postgres_content_clause = text(postgres_content_sql)
query = query.filter( query = query.filter(
( or_(
Chat.title.ilike( Chat.title.ilike(bindparam('title_key')),
f"%{search_text}%" postgres_content_clause
) # Case-insensitive search in title ).params(title_key=f"%{search_text}%", content_key=search_text)
| text(
"""
EXISTS (
SELECT 1
FROM json_array_elements(Chat.chat->'messages') AS message
WHERE LOWER(message->>'content') LIKE '%' || :search_text || '%'
)
"""
)
).params(search_text=search_text)
) )
# Check if there are any tags to filter, it should have all the tags # Check if there are any tags to filter, it should have all the tags