@@ -18,6 +18,7 @@ package model
1818
1919import (
2020 "bytes"
21+ "context"
2122 "encoding/json"
2223 "errors"
2324 "fmt"
@@ -2743,27 +2744,108 @@ type SearchAttributeViewOptions struct {
27432744 IncludeViewMatches bool
27442745}
27452746
2746- var attributeViewSearchCacheWarmups sync.Map
2747+ type attributeViewSearchCacheWarmup struct {
2748+ signature uint64
2749+ cancel context.CancelFunc
2750+ running bool
2751+ }
2752+
2753+ var attributeViewSearchCacheWarmups = struct {
2754+ sync.Mutex
2755+ states map [string ]* attributeViewSearchCacheWarmup
2756+ }{states : map [string ]* attributeViewSearchCacheWarmup {}}
2757+
2758+ var attributeViewSearchCacheWarmupDelay = 500 * time .Millisecond
2759+ var loadAttributeViewSearchInfo = av .GetAttributeViewSearchInfoInBox
27472760
2748- func warmAttributeViewSearchCache (boxID string , avIDs []string ) {
2761+ const attributeViewSearchCacheWarmupHashOffset = uint64 (1469598103934665603 )
2762+ const attributeViewSearchCacheWarmupHashPrime = uint64 (1099511628211 )
2763+
2764+ func warmAttributeViewSearchCache (boxID string , avIDs []string , signature uint64 ) {
27492765 if len (avIDs ) == 0 {
27502766 return
27512767 }
2752- if _ , loaded := attributeViewSearchCacheWarmups .LoadOrStore (boxID , struct {}{}); loaded {
2768+
2769+ attributeViewSearchCacheWarmups .Lock ()
2770+ if state := attributeViewSearchCacheWarmups .states [boxID ]; state != nil && state .signature == signature {
2771+ attributeViewSearchCacheWarmups .Unlock ()
27532772 return
27542773 }
2774+ if state := attributeViewSearchCacheWarmups .states [boxID ]; state != nil && state .cancel != nil {
2775+ state .cancel ()
2776+ }
2777+ ctx , cancel := context .WithCancel (context .Background ())
2778+ state := & attributeViewSearchCacheWarmup {signature : signature , cancel : cancel , running : true }
2779+ attributeViewSearchCacheWarmups .states [boxID ] = state
2780+ attributeViewSearchCacheWarmups .Unlock ()
2781+
27552782 ids := slices .Clone (avIDs )
2783+ delay := attributeViewSearchCacheWarmupDelay
2784+ loader := loadAttributeViewSearchInfo
27562785 go func () {
2757- defer attributeViewSearchCacheWarmups .Delete (boxID )
2786+ completed := false
2787+ defer func () {
2788+ attributeViewSearchCacheWarmups .Lock ()
2789+ defer attributeViewSearchCacheWarmups .Unlock ()
2790+ if attributeViewSearchCacheWarmups .states [boxID ] != state {
2791+ return
2792+ }
2793+ if completed {
2794+ state .cancel = nil
2795+ state .running = false
2796+ } else {
2797+ delete (attributeViewSearchCacheWarmups .states , boxID )
2798+ }
2799+ }()
2800+
2801+ timer := time .NewTimer (delay )
2802+ defer timer .Stop ()
2803+ select {
2804+ case <- ctx .Done ():
2805+ return
2806+ case <- timer .C :
2807+ }
2808+
27582809 for _ , avID := range ids {
2810+ select {
2811+ case <- ctx .Done ():
2812+ return
2813+ default :
2814+ }
27592815 if isSyncingStorages () {
27602816 return
27612817 }
2762- _ , _ = av . GetAttributeViewSearchInfoInBox (avID , boxID )
2818+ _ , _ = loader (avID , boxID )
27632819 }
2820+ completed = true
27642821 }()
27652822}
27662823
2824+ func stopAttributeViewSearchCacheWarmup (boxID string ) {
2825+ attributeViewSearchCacheWarmups .Lock ()
2826+ defer attributeViewSearchCacheWarmups .Unlock ()
2827+ state := attributeViewSearchCacheWarmups .states [boxID ]
2828+ if state == nil || ! state .running {
2829+ return
2830+ }
2831+ state .cancel ()
2832+ delete (attributeViewSearchCacheWarmups .states , boxID )
2833+ }
2834+
2835+ func updateAttributeViewSearchCacheWarmupSignature (signature uint64 , avID string , info os.FileInfo ) uint64 {
2836+ for i := 0 ; i < len (avID ); i ++ {
2837+ signature ^= uint64 (avID [i ])
2838+ signature *= attributeViewSearchCacheWarmupHashPrime
2839+ }
2840+ if info != nil {
2841+ signature ^= uint64 (info .ModTime ().UnixNano ())
2842+ signature *= attributeViewSearchCacheWarmupHashPrime
2843+ signature ^= uint64 (info .Size ())
2844+ signature *= attributeViewSearchCacheWarmupHashPrime
2845+ }
2846+ return signature
2847+ }
2848+
27672849func matchAttributeViewSearchName (name string , keywords []string ) (score float64 , hit bool ) {
27682850 if name == "" || len (keywords ) == 0 {
27692851 return
@@ -2781,6 +2863,23 @@ func matchAttributeViewSearchName(name string, keywords []string) (score float64
27812863 return
27822864}
27832865
2866+ func sortAndLimitAttributeViewSearchResults (results []* AvSearchTempResult , keyword string ) []* AvSearchTempResult {
2867+ if keyword == "" {
2868+ sort .Slice (results , func (i , j int ) bool { return results [i ].AvUpdated > results [j ].AvUpdated })
2869+ } else {
2870+ sort .SliceStable (results , func (i , j int ) bool {
2871+ if results [i ].Score == results [j ].Score {
2872+ return results [i ].AvUpdated > results [j ].AvUpdated
2873+ }
2874+ return results [i ].Score > results [j ].Score
2875+ })
2876+ }
2877+ if 12 < len (results ) {
2878+ return results [:12 ]
2879+ }
2880+ return results
2881+ }
2882+
27842883func SearchAttributeView (keyword string , excludeAvIDs []string , currentAvID , currentBlockID string ) []* AvSearchResult {
27852884 return SearchAttributeViewWithOptions (SearchAttributeViewOptions {
27862885 Keyword : keyword ,
@@ -2823,6 +2922,9 @@ func SearchAttributeViewWithOptions(options SearchAttributeViewOptions) (ret []*
28232922
28242923 var avSearchTmpResults []* AvSearchTempResult
28252924 var warmupAvIDs []string
2925+ warmupSignature := attributeViewSearchCacheWarmupHashOffset
2926+ warmupSignature ^= cache .GetAVCacheGeneration ()
2927+ warmupSignature *= attributeViewSearchCacheWarmupHashPrime
28262928 boxID := ""
28272929 if options .CurrentBlockID != "" {
28282930 if bt := treenode .GetBlockTree (options .CurrentBlockID ); nil != bt && IsEncryptedBox (bt .BoxID ) {
@@ -2831,6 +2933,9 @@ func SearchAttributeViewWithOptions(options SearchAttributeViewOptions) (ret []*
28312933 } else if options .CurrentAvID != "" {
28322934 _ , boxID = av .FindAttributeViewPath (options .CurrentAvID )
28332935 }
2936+ if keyword != "" {
2937+ stopAttributeViewSearchCacheWarmup (boxID )
2938+ }
28342939 avDir := filepath .Join (util .DataDir , "storage" , "av" )
28352940 if boxID != "" {
28362941 avDir = filepath .Join (util .DataDir , boxID , "storage" , "av" )
@@ -2866,16 +2971,17 @@ func SearchAttributeViewWithOptions(options SearchAttributeViewOptions) (ret []*
28662971 if nil == avBlockRels [id ] {
28672972 continue
28682973 }
2974+ readFileInfoStart := time .Now ()
2975+ info , _ := entry .Info ()
2976+ readFileInfoElapsed += time .Since (readFileInfoStart )
28692977 warmupAvIDs = append (warmupAvIDs , id )
2978+ warmupSignature = updateAttributeViewSearchCacheWarmupSignature (warmupSignature , id , info )
28702979
28712980 if gulu .Str .Contains (id , options .ExcludeAvIDs ) {
28722981 continue
28732982 }
28742983 eligibleFileCount ++
28752984
2876- readFileInfoStart := time .Now ()
2877- info , _ := entry .Info ()
2878- readFileInfoElapsed += time .Since (readFileInfoStart )
28792985 if info != nil {
28802986 eligibleFileSize += info .Size ()
28812987 }
@@ -2928,20 +3034,8 @@ func SearchAttributeViewWithOptions(options SearchAttributeViewOptions) (ret []*
29283034 scanElapsed = time .Since (scanStart )
29293035
29303036 sortStart := time .Now ()
2931- if "" == keyword {
2932- sort .Slice (avSearchTmpResults , func (i , j int ) bool { return avSearchTmpResults [i ].AvUpdated > avSearchTmpResults [j ].AvUpdated })
2933- } else {
2934- sort .SliceStable (avSearchTmpResults , func (i , j int ) bool {
2935- if avSearchTmpResults [i ].Score == avSearchTmpResults [j ].Score {
2936- return avSearchTmpResults [i ].AvUpdated > avSearchTmpResults [j ].AvUpdated
2937- }
2938- return avSearchTmpResults [i ].Score > avSearchTmpResults [j ].Score
2939- })
2940- }
29413037 matchedCount = len (avSearchTmpResults )
2942- if 12 <= len (avSearchTmpResults ) {
2943- avSearchTmpResults = avSearchTmpResults [:12 ]
2944- }
3038+ avSearchTmpResults = sortAndLimitAttributeViewSearchResults (avSearchTmpResults , keyword )
29453039 sortElapsed = time .Since (sortStart )
29463040
29473041 resolveStart := time .Now ()
@@ -3022,7 +3116,7 @@ func SearchAttributeViewWithOptions(options SearchAttributeViewOptions) (ret []*
30223116 }
30233117 resolveElapsed = time .Since (resolveStart )
30243118 if keyword == "" {
3025- warmAttributeViewSearchCache (boxID , warmupAvIDs )
3119+ warmAttributeViewSearchCache (boxID , warmupAvIDs , warmupSignature )
30263120 }
30273121 return
30283122}
0 commit comments