-- Create custom text search configurationCREATETEXTSEARCHCONFIGURATIONenglish_custom(COPY=english);-- Add custom dictionary for domain-specific termsCREATETEXTSEARCHDICTIONARYdomain_terms(TEMPLATE=simple,STOPWORDS=domain_stopwords);-- Add custom stopwordsCREATETEXTSEARCHDICTIONARYdomain_stopwords(TEMPLATE=simple,STOPWORDS='the, a, an, and, or, but, in, on, at, to, for, of, with, by');-- Configure text search with custom dictionaryALTERTEXTSEARCHCONFIGURATIONenglish_customALTERMAPPINGFORasciiword,asciihword,hword_asciipart,word,hword,hword_partWITHdomain_terms,english_stem;-- Test the configurationSELECTto_tsvector('english_custom','PostgreSQL is a powerful database system');
-- Create multilingual text search configurationCREATETEXTSEARCHCONFIGURATIONmultilingual(COPY=simple);-- Add language-specific mappingsALTERTEXTSEARCHCONFIGURATIONmultilingualALTERMAPPINGFORasciiword,asciihword,hword_asciipart,word,hword,hword_partWITHunaccent,simple;-- Create function to detect language and searchCREATEORREPLACEFUNCTIONsearch_multilingual(search_textTEXT,content_columnTEXT)RETURNSTABLE(idINTEGER,rankREAL,snippetTEXT)AS$$BEGINRETURNQUERYSELECTt.id,ts_rank(t.search_vector,plainto_tsquery('multilingual',search_text))asrank,ts_headline('multilingual',t.content,plainto_tsquery('multilingual',search_text))assnippetFROMdocumentstWHEREt.search_vector@@plainto_tsquery('multilingual',search_text)ORDERBYrankDESC;END;$$LANGUAGEplpgsql;
-- Create documents table with full-text searchCREATETABLEdocuments(idSERIALPRIMARYKEY,titleVARCHAR(200)NOTNULL,contentTEXTNOTNULL,authorVARCHAR(100),categoryVARCHAR(50),tagsTEXT[],search_vectorTSVECTOR,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create GIN index for search vectorCREATEINDEXidx_documents_search_vectorONdocumentsUSINGGIN(search_vector);-- Create B-tree index for category filteringCREATEINDEXidx_documents_categoryONdocuments(category);-- Create GIN index for tagsCREATEINDEXidx_documents_tagsONdocumentsUSINGGIN(tags);-- Function to update search vectorCREATEORREPLACEFUNCTIONupdate_document_search_vector()RETURNSTRIGGERAS$$BEGINNEW.search_vector:=to_tsvector('english_custom',COALESCE(NEW.title,'')||' '||COALESCE(NEW.content,'')||' '||COALESCE(NEW.author,'')||' '||COALESCE(array_to_string(NEW.tags,' '),''));NEW.updated_at:=CURRENT_TIMESTAMP;RETURNNEW;END;$$LANGUAGEplpgsql;-- Create trigger to update search vectorCREATETRIGGERupdate_documents_search_vectorBEFOREINSERTORUPDATEONdocumentsFOREACHROWEXECUTEFUNCTIONupdate_document_search_vector();-- Insert sample documentsINSERTINTOdocuments(title,content,author,category,tags)VALUES('PostgreSQL Performance Tuning','Learn how to optimize PostgreSQL queries and improve database performance.','John Doe','Database',ARRAY['postgresql','performance','optimization']),('Database Design Principles','Essential principles for designing efficient and scalable database schemas.','Jane Smith','Database',ARRAY['database','design','schema']),('Full-Text Search Implementation','Implementing powerful search functionality using PostgreSQL full-text search.','Bob Johnson','Search',ARRAY['search','fulltext','postgresql']),('Advanced SQL Techniques','Master advanced SQL patterns and techniques for complex queries.','Alice Brown','SQL',ARRAY['sql','advanced','queries']);
-- Create custom ranking functionCREATEORREPLACEFUNCTIONcustom_rank(search_vectorTSVECTOR,queryTSQUERY,title_weightREALDEFAULT1.0,content_weightREALDEFAULT0.5,author_weightREALDEFAULT0.3)RETURNSREALAS$$DECLAREtitle_rankREAL;content_rankREAL;author_rankREAL;BEGIN-- Calculate rank for titletitle_rank:=ts_rank(search_vector,query)*title_weight;-- Calculate rank for contentcontent_rank:=ts_rank(search_vector,query)*content_weight;-- Calculate rank for authorauthor_rank:=ts_rank(search_vector,query)*author_weight;RETURNtitle_rank+content_rank+author_rank;END;$$LANGUAGEplpgsql;-- Use custom ranking in searchSELECTid,title,author,custom_rank(search_vector,plainto_tsquery('english_custom','postgresql'),2.0,1.0,0.5)ascustom_rankFROMdocumentsWHEREsearch_vector@@plainto_tsquery('english_custom','postgresql')ORDERBYcustom_rankDESC;
-- Create search analytics tableCREATETABLEsearch_analytics(idSERIALPRIMARYKEY,search_queryTEXTNOTNULL,user_idINTEGER,results_countINTEGER,execution_time_msINTEGER,timestampTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function to log search analyticsCREATEORREPLACEFUNCTIONlog_search_analytics(p_search_queryTEXT,p_user_idINTEGER,p_results_countINTEGER,p_execution_time_msINTEGER)RETURNSVOIDAS$$BEGININSERTINTOsearch_analytics(search_query,user_id,results_count,execution_time_ms)VALUES(p_search_query,p_user_id,p_results_count,p_execution_time_ms);END;$$LANGUAGEplpgsql;-- Create search function with analyticsCREATEORREPLACEFUNCTIONsearch_documents_with_analytics(search_textTEXT,user_idINTEGERDEFAULTNULL,limit_countINTEGERDEFAULT10)RETURNSTABLE(idINTEGER,titleVARCHAR(200),authorVARCHAR(100),rankREAL,snippetTEXT)AS$$DECLAREstart_timeTIMESTAMP;end_timeTIMESTAMP;execution_timeINTEGER;result_countINTEGER;BEGINstart_time:=clock_timestamp();RETURNQUERYSELECTd.id,d.title,d.author,ts_rank(d.search_vector,plainto_tsquery('english_custom',search_text))asrank,ts_headline('english_custom',d.content,plainto_tsquery('english_custom',search_text))assnippetFROMdocumentsdWHEREd.search_vector@@plainto_tsquery('english_custom',search_text)ORDERBYrankDESCLIMITlimit_count;end_time:=clock_timestamp();execution_time:=EXTRACT(EPOCHFROM(end_time-start_time))*1000;GETDIAGNOSTICSresult_count=ROW_COUNT;-- Log analyticsPERFORMlog_search_analytics(search_text,user_id,result_count,execution_time);END;$$LANGUAGEplpgsql;
# monitoring/search_monitor.pyimportpsycopg2importjsonfromdatetimeimportdatetime,timedeltaimportloggingclassSearchPerformanceMonitor:def__init__(self,connection_params):self.conn_params=connection_paramsself.setup_logging()defsetup_logging(self):"""Setup logging."""logging.basicConfig(level=logging.INFO)self.logger=logging.getLogger(__name__)defget_search_statistics(self,days=7):"""Get search statistics for the last N days."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT DATE(timestamp) as search_date, COUNT(*) as total_searches, AVG(results_count) as avg_results, AVG(execution_time_ms) as avg_execution_time, COUNT(DISTINCT user_id) as unique_users FROM search_analytics WHERE timestamp >= CURRENT_DATE - INTERVAL '%s days' GROUP BY DATE(timestamp) ORDER BY search_date DESC """,(days,))statistics=cur.fetchall()returnstatisticsexceptExceptionase:self.logger.error(f"Error getting search statistics: {e}")return[]finally:conn.close()defget_popular_searches(self,limit=10):"""Get most popular search queries."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT search_query, COUNT(*) as search_count, AVG(results_count) as avg_results, AVG(execution_time_ms) as avg_execution_time FROM search_analytics WHERE timestamp >= CURRENT_DATE - INTERVAL '7 days' GROUP BY search_query ORDER BY search_count DESC LIMIT %s """,(limit,))popular_searches=cur.fetchall()returnpopular_searchesexceptExceptionase:self.logger.error(f"Error getting popular searches: {e}")return[]finally:conn.close()defget_search_performance_metrics(self):"""Get search performance metrics."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:# Get average execution timecur.execute(""" SELECT AVG(execution_time_ms) as avg_execution_time, MAX(execution_time_ms) as max_execution_time, MIN(execution_time_ms) as min_execution_time, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY execution_time_ms) as p95_execution_time FROM search_analytics WHERE timestamp >= CURRENT_DATE - INTERVAL '1 day' """)performance_metrics=cur.fetchone()# Get search success ratecur.execute(""" SELECT COUNT(*) as total_searches, COUNT(*) FILTER (WHERE results_count > 0) as successful_searches, COUNT(*) FILTER (WHERE results_count = 0) as failed_searches FROM search_analytics WHERE timestamp >= CURRENT_DATE - INTERVAL '1 day' """)success_metrics=cur.fetchone()return{'performance_metrics':performance_metrics,'success_metrics':success_metrics}exceptExceptionase:self.logger.error(f"Error getting search performance metrics: {e}")return{}finally:conn.close()defgenerate_search_report(self):"""Generate comprehensive search report."""statistics=self.get_search_statistics(7)popular_searches=self.get_popular_searches(10)performance_metrics=self.get_search_performance_metrics()report={'report_timestamp':datetime.now().isoformat(),'statistics':statistics,'popular_searches':popular_searches,'performance_metrics':performance_metrics}returnreport# Usageif__name__=="__main__":monitor=SearchPerformanceMonitor({'host':'localhost','database':'production','user':'monitor_user','password':'monitor_password'})report=monitor.generate_search_report()print(json.dumps(report,indent=2))
-- Create search suggestions tableCREATETABLEsearch_suggestions(idSERIALPRIMARYKEY,suggestionTEXTNOTNULL,frequencyINTEGERDEFAULT1,last_searchedTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create index for suggestionsCREATEINDEXidx_search_suggestions_textONsearch_suggestions(suggestion);CREATEINDEXidx_search_suggestions_frequencyONsearch_suggestions(frequencyDESC);-- Function to get search suggestionsCREATEORREPLACEFUNCTIONget_search_suggestions(partial_queryTEXT,limit_countINTEGERDEFAULT10)RETURNSTABLE(suggestionTEXT,frequencyINTEGER)AS$$BEGINRETURNQUERYSELECTs.suggestion,s.frequencyFROMsearch_suggestionssWHEREs.suggestionILIKEpartial_query||'%'ORDERBYs.frequencyDESC,s.suggestionLIMITlimit_count;END;$$LANGUAGEplpgsql;-- Function to update search suggestionsCREATEORREPLACEFUNCTIONupdate_search_suggestions(search_queryTEXT)RETURNSVOIDAS$$BEGININSERTINTOsearch_suggestions(suggestion,frequency)VALUES(search_query,1)ONCONFLICT(suggestion)DOUPDATESETfrequency=search_suggestions.frequency+1,last_searched=CURRENT_TIMESTAMP;END;$$LANGUAGEplpgsql;
# search/autocomplete.pyimportpsycopg2importjsonfromtypingimportList,Dict,AnyclassSearchAutocomplete:def__init__(self,connection_params):self.conn_params=connection_paramsdefget_suggestions(self,partial_query:str,limit:int=10)->List[Dict[str,Any]]:"""Get search suggestions for autocomplete."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT suggestion, frequency, last_searched FROM search_suggestions WHERE suggestion ILIKE %s ORDER BY frequency DESC, suggestion LIMIT %s """,(f"{partial_query}%",limit))suggestions=[]forrowincur.fetchall():suggestions.append({'suggestion':row[0],'frequency':row[1],'last_searched':row[2].isoformat()ifrow[2]elseNone})returnsuggestionsexceptExceptionase:print(f"Error getting suggestions: {e}")return[]finally:conn.close()defupdate_suggestion(self,search_query:str):"""Update search suggestion frequency."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" INSERT INTO search_suggestions (suggestion, frequency) VALUES (%s, 1) ON CONFLICT (suggestion) DO UPDATE SET frequency = search_suggestions.frequency + 1, last_searched = CURRENT_TIMESTAMP """,(search_query,))conn.commit()exceptExceptionase:print(f"Error updating suggestion: {e}")conn.rollback()finally:conn.close()defget_popular_suggestions(self,limit:int=10)->List[Dict[str,Any]]:"""Get most popular search suggestions."""conn=psycopg2.connect(**self.conn_params)try:withconn.cursor()ascur:cur.execute(""" SELECT suggestion, frequency, last_searched FROM search_suggestions ORDER BY frequency DESC, last_searched DESC LIMIT %s """,(limit,))suggestions=[]forrowincur.fetchall():suggestions.append({'suggestion':row[0],'frequency':row[1],'last_searched':row[2].isoformat()ifrow[2]elseNone})returnsuggestionsexceptExceptionase:print(f"Error getting popular suggestions: {e}")return[]finally:conn.close()# Usageif__name__=="__main__":autocomplete=SearchAutocomplete({'host':'localhost','database':'production','user':'search_user','password':'search_password'})# Get suggestions for partial querysuggestions=autocomplete.get_suggestions("postgresql")print(json.dumps(suggestions,indent=2))# Update suggestionautocomplete.update_suggestion("postgresql performance")