opex792 commited on
Commit
949a8a8
·
verified ·
1 Parent(s): f1a303f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -124
app.py CHANGED
@@ -10,7 +10,7 @@ import zlib
10
  from urllib.parse import urlparse
11
 
12
  # Настройки базы данных PostgreSQL
13
- DATABASE_URL = os.environ.get("DB_URL")
14
  if DATABASE_URL is None:
15
  raise ValueError("DATABASE_URL environment variable not set.")
16
 
@@ -69,13 +69,17 @@ def get_db_connection():
69
  print(f"Ошибка подключения к базе данных: {e}")
70
  return None
71
 
72
- def create_embeddings_table():
73
- """Создает таблицу для хранения эмбеддингов фильмов, если она не существует."""
74
  conn = get_db_connection()
75
  if conn is None:
76
  return
77
 
78
  with conn.cursor() as cur:
 
 
 
 
79
  cur.execute(f"""
80
  CREATE TABLE IF NOT EXISTS {embeddings_table} (
81
  movie_id INTEGER,
@@ -85,16 +89,8 @@ def create_embeddings_table():
85
  embedding vector(1024)
86
  );
87
  """)
88
- conn.commit()
89
- conn.close()
90
-
91
- def create_query_cache_table():
92
- """Создает таблицу для кэширования эмбеддингов запросов, если она не существует."""
93
- conn = get_db_connection()
94
- if conn is None:
95
- return
96
 
97
- with conn.cursor() as cur:
98
  cur.execute(f"""
99
  CREATE TABLE IF NOT EXISTS {query_cache_table} (
100
  query_crc32 BIGINT PRIMARY KEY,
@@ -106,49 +102,41 @@ def create_query_cache_table():
106
  CREATE INDEX IF NOT EXISTS idx_query_crc32 ON {query_cache_table} (query_crc32);
107
  CREATE INDEX IF NOT EXISTS idx_created_at ON {query_cache_table} (created_at);
108
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  conn.commit()
110
  conn.close()
111
 
112
- def create_trigger_function():
113
- """Создает функцию и триггер для автоматического удаления старых записей из таблицы кэша запросов"""
114
- conn = get_db_connection()
115
- if conn:
116
- with conn.cursor() as cur:
117
- cur.execute(f"""
118
- CREATE OR REPLACE FUNCTION manage_query_cache_size()
119
- RETURNS TRIGGER AS $$
120
- DECLARE
121
- table_size BIGINT;
122
- row_to_delete RECORD;
123
- BEGIN
124
- SELECT pg_total_relation_size('{query_cache_table}') INTO table_size;
125
- IF table_size > {MAX_CACHE_SIZE} THEN
126
- FOR row_to_delete IN
127
- SELECT query_crc32
128
- FROM {query_cache_table}
129
- ORDER BY created_at ASC
130
- LOOP
131
- DELETE FROM {query_cache_table} WHERE query_crc32 = row_to_delete.query_crc32;
132
- SELECT pg_total_relation_size('{query_cache_table}') INTO table_size;
133
- EXIT WHEN table_size <= {MAX_CACHE_SIZE};
134
- END LOOP;
135
- END IF;
136
- RETURN NEW;
137
- END;
138
- $$ LANGUAGE plpgsql;
139
-
140
- CREATE OR REPLACE TRIGGER trg_manage_query_cache_size
141
- AFTER INSERT ON {query_cache_table}
142
- FOR EACH ROW
143
- EXECUTE PROCEDURE manage_query_cache_size();
144
- """)
145
- conn.commit()
146
- conn.close()
147
-
148
- # Создаем таблицы, индексы и триггер при запуске приложения
149
- create_embeddings_table()
150
- create_query_cache_table()
151
- create_trigger_function()
152
 
153
  def calculate_crc32(text):
154
  """Вычисляет CRC32 для строки."""
@@ -158,64 +146,39 @@ def encode_string(text):
158
  """Кодирует строку в эмбеддинг."""
159
  return model.encode(text, convert_to_tensor=True, normalize_embeddings=True)
160
 
161
- def insert_embedding(conn, movie_id, embedding_string, model_name, embedding):
162
- """Вставляет эмбеддинг фильма в базу данных."""
163
- embedding_crc32 = calculate_crc32(str(embedding.tolist()))
164
- string_crc32 = calculate_crc32(embedding_string)
 
165
  with conn.cursor() as cur:
166
- try:
167
- cur.execute(
168
- f"""
169
- INSERT INTO {embeddings_table} (movie_id, embedding_crc32, string_crc32, model_name, embedding)
170
- VALUES (%s, %s, %s, %s, %s)
171
- ON CONFLICT (embedding_crc32) DO NOTHING;
172
- """,
173
- (movie_id, embedding_crc32, string_crc32, model_name, embedding.tolist())
174
- )
175
- conn.commit()
176
- return True
177
- except Exception as e:
178
- print(f"Ошибка при вставке эмбеддинга фильма: {e}")
179
- conn.rollback()
180
- return False
181
 
182
- def insert_query_embedding(conn, query, model_name, embedding):
183
- """Вставляет эмбеддинг запроса в таблицу кэша."""
184
- query_crc32 = calculate_crc32(query)
185
  with conn.cursor() as cur:
186
  try:
187
- cur.execute(
188
- f"""
189
- INSERT INTO {query_cache_table} (query_crc32, query, model_name, embedding)
190
- VALUES (%s, %s, %s, %s)
191
- ON CONFLICT (query_crc32) DO UPDATE SET created_at = DEFAULT;
192
- """,
193
- (query_crc32, query, model_name, embedding.tolist())
194
- )
195
  conn.commit()
196
- print(f"Эмбеддинг для запроса '{query}' сохранен в кэше.")
197
  return True
198
  except Exception as e:
199
- print(f"Ошибка при вставке эмбеддинга запроса: {e}")
200
  conn.rollback()
201
  return False
202
 
203
- def get_movie_embeddings(conn):
204
- """Загружает все эмбеддинги фильмов из базы данных."""
205
- movie_embeddings = {}
206
- with conn.cursor() as cur:
207
- cur.execute(f"SELECT movie_id, embedding FROM {embeddings_table}")
208
- rows = cur.fetchall()
209
- for row in rows:
210
- movie_id, embedding = row
211
- # Находим название фильма по его ID
212
- for movie in movies_data:
213
- if movie['id'] == movie_id:
214
- title = movie["name"]
215
- movie_embeddings[title] = torch.tensor(embedding)
216
- break
217
- return movie_embeddings
218
-
219
  def process_movies():
220
  """
221
  Обрабатывает фильмы из очереди, создавая для них эмбеддинги и сохраняя их в базу данных.
@@ -251,32 +214,45 @@ def process_movies():
251
  ]
252
 
253
  print(f"Создаются эмбеддинги для фильмов: {', '.join(titles)}...")
254
- embeddings = model.encode(embedding_strings, convert_to_tensor=True, batch_size=batch_size, normalize_embeddings=True)
255
-
256
  with db_lock:
257
- for movie, embedding, embedding_string in zip(batch, embeddings, embedding_strings):
258
- if insert_embedding(conn, movie['id'], embedding_string, model_name, embedding):
259
- print(f"Эмбеддинг для фильма '{movie['name']}' сохранен в базе данных.")
260
- else:
261
- print(f"Ошибка сохранения эмбеддинга для фильма '{movie['name']}'.")
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
  conn.close()
264
  print("Обработка фильмов завершена.")
265
 
266
- def get_query_embedding_from_db(conn, query):
267
- """
268
- Пытается получить эмбеддинг запроса из базы данных по CRC32.
269
- Возвращает эмбеддинг, если найден, иначе None.
270
- """
271
- query_crc32 = calculate_crc32(query)
272
  with conn.cursor() as cur:
273
- cur.execute(f"SELECT embedding FROM {query_cache_table} WHERE query_crc32 = %s AND model_name = %s", (query_crc32, model_name))
274
- result = cur.fetchone()
275
- if result:
276
- print(f"Эмбеддинг для запроса '{query}' найден в кэше.")
277
- return torch.tensor(result[0])
278
- else:
279
- return None
 
 
 
 
280
 
281
  def search_movies(query, top_k=10):
282
  """
@@ -299,14 +275,20 @@ def search_movies(query, top_k=10):
299
  search_in_progress = False
300
  return "<p>Ошибка подключения к базе данных.</p>"
301
 
302
- print(f"Начало создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
303
- query_embedding_tensor = get_query_embedding_from_db(conn, query)
 
 
 
 
304
 
305
  if query_embedding_tensor is None:
 
306
  query_embedding_tensor = encode_string(query)
 
 
307
  # Вставляем эмбеддинг запроса в базу данных
308
- insert_query_embedding(conn, query, model_name, query_embedding_tensor)
309
- print(f"Окончание создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
310
 
311
  with db_lock:
312
  current_movie_embeddings = get_movie_embeddings(conn)
 
10
  from urllib.parse import urlparse
11
 
12
  # Настройки базы данных PostgreSQL
13
+ DATABASE_URL = os.environ.get("DATABASE_URL")
14
  if DATABASE_URL is None:
15
  raise ValueError("DATABASE_URL environment variable not set.")
16
 
 
69
  print(f"Ошибка подключения к базе данных: {e}")
70
  return None
71
 
72
+ def setup_database():
73
+ """Настраивает базу данных: создает расширение, таблицы и триггер."""
74
  conn = get_db_connection()
75
  if conn is None:
76
  return
77
 
78
  with conn.cursor() as cur:
79
+ # Создаем расширение pgvector
80
+ cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
81
+
82
+ # Создаем таблицу для хранения эмбеддингов фильмов
83
  cur.execute(f"""
84
  CREATE TABLE IF NOT EXISTS {embeddings_table} (
85
  movie_id INTEGER,
 
89
  embedding vector(1024)
90
  );
91
  """)
 
 
 
 
 
 
 
 
92
 
93
+ # Создаем таблицу для кэширования эмбеддингов запросов
94
  cur.execute(f"""
95
  CREATE TABLE IF NOT EXISTS {query_cache_table} (
96
  query_crc32 BIGINT PRIMARY KEY,
 
102
  CREATE INDEX IF NOT EXISTS idx_query_crc32 ON {query_cache_table} (query_crc32);
103
  CREATE INDEX IF NOT EXISTS idx_created_at ON {query_cache_table} (created_at);
104
  """)
105
+
106
+ # Создаем функцию и триггер для автоматического удаления старых записей из таблицы кэша запросов
107
+ cur.execute(f"""
108
+ CREATE OR REPLACE FUNCTION manage_query_cache_size()
109
+ RETURNS TRIGGER AS $$
110
+ DECLARE
111
+ table_size BIGINT;
112
+ row_to_delete RECORD;
113
+ BEGIN
114
+ SELECT pg_total_relation_size('{query_cache_table}') INTO table_size;
115
+ IF table_size > {MAX_CACHE_SIZE} THEN
116
+ FOR row_to_delete IN
117
+ SELECT query_crc32
118
+ FROM {query_cache_table}
119
+ ORDER BY created_at ASC
120
+ LOOP
121
+ DELETE FROM {query_cache_table} WHERE query_crc32 = row_to_delete.query_crc32;
122
+ SELECT pg_total_relation_size('{query_cache_table}') INTO table_size;
123
+ EXIT WHEN table_size <= {MAX_CACHE_SIZE};
124
+ END LOOP;
125
+ END IF;
126
+ RETURN NEW;
127
+ END;
128
+ $$ LANGUAGE plpgsql;
129
+
130
+ CREATE OR REPLACE TRIGGER trg_manage_query_cache_size
131
+ AFTER INSERT ON {query_cache_table}
132
+ FOR EACH ROW
133
+ EXECUTE PROCEDURE manage_query_cache_size();
134
+ """)
135
  conn.commit()
136
  conn.close()
137
 
138
+ # Настраиваем базу данных при запуске приложения
139
+ setup_database()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  def calculate_crc32(text):
142
  """Вычисляет CRC32 для строки."""
 
146
  """Кодирует строку в эмбеддинг."""
147
  return model.encode(text, convert_to_tensor=True, normalize_embeddings=True)
148
 
149
+ def get_embedding_from_db(conn, table_name, crc32_column, crc32_value, model_name):
150
+ """
151
+ Пытается получить эмбеддинг из указанной таблицы по CRC32.
152
+ Возвращает эмбеддинг, если найден, иначе None.
153
+ """
154
  with conn.cursor() as cur:
155
+ cur.execute(f"SELECT embedding FROM {table_name} WHERE {crc32_column} = %s AND model_name = %s", (crc32_value, model_name))
156
+ result = cur.fetchone()
157
+ if result:
158
+ return torch.tensor(result[0])
159
+ else:
160
+ return None
161
+
162
+ def insert_embedding(conn, table_name, crc32_column, crc32_value, other_columns, embedding):
163
+ """Вставляет эмбеддинг в указанную таблицу."""
164
+ columns = ', '.join([crc32_column] + list(other_columns.keys()) + ['model_name', 'embedding'])
165
+ placeholders = ', '.join(['%s'] * (len(other_columns) + 3))
166
+ values = (crc32_value,) + tuple(other_columns.values()) + (model_name, embedding.tolist())
 
 
 
167
 
 
 
 
168
  with conn.cursor() as cur:
169
  try:
170
+ cur.execute(f"""
171
+ INSERT INTO {table_name} ({columns})
172
+ VALUES ({placeholders})
173
+ ON CONFLICT ({crc32_column}) DO NOTHING;
174
+ """, values)
 
 
 
175
  conn.commit()
 
176
  return True
177
  except Exception as e:
178
+ print(f"Ошибка при вставке эмбеддинга в таблицу {table_name}: {e}")
179
  conn.rollback()
180
  return False
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  def process_movies():
183
  """
184
  Обрабатывает фильмы из очереди, создавая для них эмбеддинги и сохраняя их в базу данных.
 
214
  ]
215
 
216
  print(f"Создаются эмбеддинги для фильмов: {', '.join(titles)}...")
217
+
 
218
  with db_lock:
219
+ for movie, embedding_string in zip(batch, embedding_strings):
220
+ movie_id = movie['id']
221
+ embedding_crc32 = calculate_crc32(str(embedding.tolist()))
222
+ string_crc32 = calculate_crc32(embedding_string)
223
+
224
+ # Проверяем, есть ли уже эмбеддинг для этого фильма в базе данных
225
+ existing_embedding = get_embedding_from_db(conn, embeddings_table, "embedding_crc32", embedding_crc32, model_name)
226
+
227
+ if existing_embedding is None:
228
+ # Создаем эмбеддинг, только если его нет в базе данных
229
+ embedding = encode_string(embedding_string)
230
+
231
+ if insert_embedding(conn, embeddings_table, "embedding_crc32", embedding_crc32, {"movie_id": movie_id, "string_crc32": string_crc32}, embedding):
232
+ print(f"Эмбеддинг для фильма '{movie['name']}' сохранен в базе данных.")
233
+ else:
234
+ print(f"Ошибка сохранения эмбеддинга для фильма '{movie['name']}'.")
235
+ else:
236
+ print(f"Эмбеддинг для фильма '{movie['name']}' уже существует в базе данных.")
237
 
238
  conn.close()
239
  print("Обработка фильмов завершена.")
240
 
241
+ def get_movie_embeddings(conn):
242
+ """Загружает все эмбеддинги фильмов из базы данных."""
243
+ movie_embeddings = {}
 
 
 
244
  with conn.cursor() as cur:
245
+ cur.execute(f"SELECT movie_id, embedding FROM {embeddings_table}")
246
+ rows = cur.fetchall()
247
+ for row in rows:
248
+ movie_id, embedding = row
249
+ # Находим название фильма по его ID
250
+ for movie in movies_data:
251
+ if movie['id'] == movie_id:
252
+ title = movie["name"]
253
+ movie_embeddings[title] = torch.tensor(embedding)
254
+ break
255
+ return movie_embeddings
256
 
257
  def search_movies(query, top_k=10):
258
  """
 
275
  search_in_progress = False
276
  return "<p>Ошибка подключения к базе данных.</p>"
277
 
278
+ query_crc32 = calculate_crc32(query)
279
+
280
+ # Проверяем, есть ли уже эмбеддинг для этого запроса в кэше
281
+ print(f"Начало поиска эмбеддинга запроса в кэше: {time.strftime('%Y-%m-%d %H:%M:%S')}")
282
+ query_embedding_tensor = get_embedding_from_db(conn, query_cache_table, "query_crc32", query_crc32, model_name)
283
+ print(f"Окончание поиска эмбеддинга запроса в кэше: {time.strftime('%Y-%m-%d %H:%M:%S')}")
284
 
285
  if query_embedding_tensor is None:
286
+ print(f"Начало создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
287
  query_embedding_tensor = encode_string(query)
288
+ print(f"Окончание создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
289
+
290
  # Вставляем эмбеддинг запроса в базу данных
291
+ insert_embedding(conn, query_cache_table, "query_crc32", query_crc32, {"query": query}, query_embedding_tensor)
 
292
 
293
  with db_lock:
294
  current_movie_embeddings = get_movie_embeddings(conn)