@@ -22,9 +22,14 @@ pub type Document {
2222 ref : String ,
2323 title : String ,
2424 type_ : String ,
25+ headers : List ( Header ) ,
2526 )
2627}
2728
29+ pub type Header {
30+ Header ( ref : String , title : String )
31+ }
32+
2833pub fn packages ( ) {
2934 let endpoint = endpoints . packages ( )
3035 let assert Ok ( request ) = request . from_uri ( endpoint )
@@ -58,13 +63,91 @@ pub fn typesense_decoder() {
5863 use ref <- decode . field ( "ref" , decode . string )
5964 use title <- decode . field ( "title" , decode . string )
6065 use type_ <- decode . field ( "type" , decode . string )
61- Document ( doc : , id : , package : , proglang : , ref : , title : , type_ : )
66+ Document (
67+ doc : ,
68+ id : ,
69+ package : ,
70+ proglang : ,
71+ ref : ,
72+ title : ,
73+ type_ : ,
74+ headers : [ ] ,
75+ )
6276 |> decode . success
6377 } )
6478 decode . success ( document )
6579 } )
6680 } )
67- decode . success ( # ( found , hits ) )
81+ let grouped_results = group_headers ( hits )
82+ let removed_count = list . length ( hits ) - list . length ( grouped_results )
83+ let max_results = list . take ( grouped_results , config . per_page ( ) )
84+ decode . success ( # ( found - removed_count , max_results ) )
85+ }
86+
87+ fn group_headers ( documents : List ( Document ) ) -> List ( Document ) {
88+ // Convert to indexed tuples
89+ let indexed_docs = list . index_map ( documents , fn ( doc , index ) { # ( index , doc ) } )
90+
91+ // First pass: separate parents and children with their indexes
92+ let # ( parents , children ) =
93+ list . partition ( indexed_docs , fn ( indexed_doc ) {
94+ let # ( _index , doc ) = indexed_doc
95+ ! list . any ( indexed_docs , fn ( other_indexed ) {
96+ let # ( _other_index , other ) = other_indexed
97+ string . starts_with ( doc . ref , other . ref <> "-" )
98+ && doc . package == other . package
99+ && doc . id != other . id
100+ } )
101+ } )
102+
103+ // Second pass: attach children to parents and compute min index
104+ let grouped_with_index =
105+ list . map ( parents , fn ( parent_indexed ) {
106+ let # ( parent_index , parent ) = parent_indexed
107+
108+ let matching_headers =
109+ list . filter_map ( children , fn ( child_indexed ) {
110+ let # ( _child_index , child ) = child_indexed
111+ case
112+ string . starts_with ( child . ref , parent . ref <> "-" )
113+ && child . package == parent . package
114+ {
115+ True -> {
116+ let cleaned_title =
117+ string . replace ( child . title , " - " <> parent . title , "" )
118+ Ok ( # ( child_indexed , Header ( ref : child . ref , title : cleaned_title ) ) )
119+ }
120+ False -> Error ( Nil )
121+ }
122+ } )
123+
124+ let child_indexes =
125+ list . map ( matching_headers , fn ( header_data ) {
126+ let # ( # ( child_index , _child ) , _header ) = header_data
127+ child_index
128+ } )
129+
130+ let min_index = list . fold ( child_indexes , parent_index , int . min )
131+ let headers =
132+ list . map ( matching_headers , fn ( header_data ) {
133+ let # ( _child_indexed , header ) = header_data
134+ header
135+ } )
136+
137+ # ( min_index , Document ( .. parent , headers : headers ) )
138+ } )
139+
140+ // Sort by index and return documents
141+ grouped_with_index
142+ |> list . sort ( fn ( a , b ) {
143+ let # ( index_a , _doc_a ) = a
144+ let # ( index_b , _doc_b ) = b
145+ int . compare ( index_a , index_b )
146+ } )
147+ |> list . map ( fn ( indexed_doc ) {
148+ let # ( _index , doc ) = indexed_doc
149+ doc
150+ } )
68151}
69152
70153fn new_search_query_params (
@@ -77,7 +160,8 @@ fn new_search_query_params(
77160 |> list . key_set ( "query_by" , "title,doc,type" )
78161 |> list . key_set ( "query_by_weights" , "3,1,1" )
79162 |> list . key_set ( "page" , int . to_string ( page ) )
80- |> list . key_set ( "per_page" , int . to_string ( config . per_page ( ) ) )
163+ // We multiply per 2 because we group results
164+ |> list . key_set ( "per_page" , int . to_string ( config . per_page ( ) * 2 ) )
81165 |> list . key_set ( "highlight_fields" , "none" )
82166 |> add_filter_by_packages_param ( packages )
83167 |> uri . query_to_string
0 commit comments