@@ -202,11 +202,34 @@ def search
202202 return
203203 end
204204
205- load_cached_search_results
205+ @search_warnings = [ ]
206+
207+ begin
208+ # Parse the search query
209+ parser = Search ::QueryParser . new
210+ ast = parser . parse ( @search_query )
211+
212+ # Validate and collect warnings
213+ validator = Search ::QueryValidator . new ( ast )
214+ validated = validator . validate
215+ @search_warnings += validated . warnings
216+
217+ # Build the ActiveRecord query
218+ builder = Search ::QueryBuilder . new ( ast : validated . ast , user : current_user )
219+ result = builder . build
220+ @search_warnings += result . warnings
221+
222+ # Load results
223+ load_search_results ( result . relation )
224+ rescue Parslet ::ParseFailed => e
225+ @search_error = format_parse_error ( e )
226+ @topics = [ ]
227+ end
206228
207229 preload_topic_participants
208230 preload_commitfest_summaries
209231 preload_participation_flags if user_signed_in?
232+ load_visible_tags if user_signed_in?
210233
211234 respond_to do |format |
212235 format . html
@@ -857,41 +880,19 @@ def load_star_state
857880 @is_starred = TopicStar . exists? ( user : current_user , topic : @topic )
858881 end
859882
860- def load_cached_search_results
883+ SEARCH_PAGE_SIZE = 1000
884+
885+ def load_search_results ( base_relation )
861886 @viewing_since = viewing_since_param
862887 longpage = params [ :longpage ] . to_i
863- cache = SearchResultCache . new ( query : @search_query , scope : "title_body" , viewing_since : @viewing_since , longpage : longpage )
864-
865- result = cache . fetch do |limit , offset |
866- build_search_query ( @search_query )
867- . joins ( :messages )
868- . where ( messages : { created_at : ..@viewing_since } )
869- . group ( 'topics.id' )
870- . select ( 'topics.id, topics.creator_id, MAX(messages.created_at) as last_activity' )
871- . order ( 'MAX(messages.created_at) DESC, topics.id DESC' )
872- . limit ( limit )
873- . offset ( offset )
874- . load
875- end
876888
877- entries = result [ :entries ] || [ ]
889+ entries = execute_search_query ( base_relation , longpage )
878890 sliced = slice_cached_entries ( entries , params [ :cursor ] )
879891
880- if sliced [ :entries ] . empty? && entries . size >= SearchResultCache ::LONGPAGE_SIZE
892+ # Handle pagination to next longpage if needed
893+ if sliced [ :entries ] . empty? && entries . size >= SEARCH_PAGE_SIZE
881894 longpage += 1
882- cache = SearchResultCache . new ( query : @search_query , scope : "title_body" , viewing_since : @viewing_since , longpage : longpage )
883- next_result = cache . fetch do |limit , offset |
884- build_search_query ( @search_query )
885- . joins ( :messages )
886- . where ( messages : { created_at : ..@viewing_since } )
887- . group ( 'topics.id' )
888- . select ( 'topics.id, topics.creator_id, MAX(messages.created_at) as last_activity' )
889- . order ( 'MAX(messages.created_at) DESC, topics.id DESC' )
890- . limit ( limit )
891- . offset ( offset )
892- . load
893- end
894- entries = next_result [ :entries ] || [ ]
895+ entries = execute_search_query ( base_relation , longpage )
895896 sliced = slice_cached_entries ( entries , params [ :cursor ] )
896897 end
897898
@@ -901,6 +902,38 @@ def load_cached_search_results
901902 @new_topics_count = 0
902903 end
903904
905+ def execute_search_query ( base_relation , longpage )
906+ results = base_relation
907+ . joins ( :messages )
908+ . where ( messages : { created_at : ..@viewing_since } )
909+ . group ( "topics.id" )
910+ . select ( "topics.id, topics.creator_id, MAX(messages.created_at) as last_activity" )
911+ . order ( "MAX(messages.created_at) DESC, topics.id DESC" )
912+ . limit ( SEARCH_PAGE_SIZE )
913+ . offset ( SEARCH_PAGE_SIZE * longpage )
914+ . load
915+
916+ results . map do |row |
917+ {
918+ id : row . id ,
919+ last_activity : row . try ( :last_activity ) &.to_time || row . try ( :created_at ) &.to_time
920+ }
921+ end
922+ end
923+
924+ def format_parse_error ( error )
925+ # Extract user-friendly error message from Parslet error
926+ cause = error . parse_failure_cause
927+ if cause
928+ line = cause . pos . line_and_column . first rescue 1
929+ "Syntax error at position #{ line } : #{ cause . message } "
930+ else
931+ "Invalid search syntax"
932+ end
933+ rescue StandardError
934+ "Invalid search syntax"
935+ end
936+
904937 def preload_commitfest_summaries
905938 topic_ids = @topics . map ( &:id )
906939 @commitfest_summaries = Topic . commitfest_summaries ( topic_ids )
0 commit comments