1010
1111#include < algorithm>
1212#include " knnmisc.h"
13+ #include " sortsetup.h"
14+ #include " sortcomp.h"
1315#include " knnlib.h"
1416#include " exprtraits.h"
1517#include " sphinxint.h"
@@ -841,24 +843,48 @@ RowIteratorsWithEstimates_t CreateKNNIterators ( knn::KNN_i * pKNN, const CSphQu
841843
842844// /////////////////////////////////////////////////////////////////////////////
843845
844- struct MatchSortRescore_fn : CSphMatchComparatorState
846+ struct MatchSortRescore_fn
845847{
846- const CSphAttrLocator & m_tLocator;
848+ const ISphMatchComparator * m_pComp = nullptr ;
849+ const CSphMatchComparatorState & m_tState;
847850
848- MatchSortRescore_fn ( const CSphAttrLocator & tLoc ) : m_tLocator(tLoc) {}
851+ MatchSortRescore_fn ( const ISphMatchComparator * pComp, const CSphMatchComparatorState & tState )
852+ : m_pComp ( pComp )
853+ , m_tState ( tState )
854+ {
855+ assert ( m_pComp );
856+ }
849857
850858 bool IsLess ( const CSphMatch * a, const CSphMatch * b ) const
851859 {
852860 assert ( a && b );
853- return a->GetAttrFloat (m_tLocator) < b->GetAttrFloat (m_tLocator);
861+ // CSphMatchComparatorState comparators report whether a match is worse.
862+ // sphSort() needs the opposite: whether a match must be emitted earlier.
863+ return m_pComp->VirtualIsLess ( *b, *a, m_tState );
854864 }
855865};
856866
867+ static ISphMatchComparator * CreateMatchComparator ( ESphSortFunc eFunc )
868+ {
869+ switch ( eFunc )
870+ {
871+ case FUNC_REL_DESC: return new MatchRelevanceLt_fn ();
872+ case FUNC_TIMESEGS: return new MatchTimeSegments_fn ();
873+ case FUNC_GENERIC1: return new MatchGeneric1_fn ();
874+ case FUNC_GENERIC2: return new MatchGeneric2_fn ();
875+ case FUNC_GENERIC3: return new MatchGeneric3_fn ();
876+ case FUNC_GENERIC4: return new MatchGeneric4_fn ();
877+ case FUNC_GENERIC5: return new MatchGeneric5_fn ();
878+ case FUNC_EXPR: return new MatchExpr_fn ();
879+ default : return nullptr ;
880+ }
881+ }
882+
857883
858884class RescoreSorter_c : public ISphMatchSorter
859885{
860886public:
861- RescoreSorter_c ( ISphMatchSorter * pSorter ) : m_pSorter ( pSorter ) {}
887+ RescoreSorter_c ( ISphMatchSorter * pSorter, CSphRefcountedPtr<ISphMatchComparator> pComp ) : m_pSorter ( pSorter ), m_pComp ( std::move ( pComp ) ) {}
862888
863889 bool Push ( const CSphMatch & tEntry ) final { return m_pSorter->Push (tEntry); }
864890 void Push ( const VecTraits_T<const CSphMatch> & dMatches ) override { for ( auto & i : dMatches ) m_pSorter->Push (i); }
@@ -897,6 +923,7 @@ class RescoreSorter_c : public ISphMatchSorter
897923
898924private:
899925 std::unique_ptr<ISphMatchSorter> m_pSorter;
926+ CSphRefcountedPtr<ISphMatchComparator> m_pComp;
900927};
901928
902929
@@ -921,14 +948,18 @@ int RescoreSorter_c::Flatten ( CSphMatch * pTo )
921948 auto * pKNNDistRescore = m_pSorter->GetSchema ()->GetAttr ( GetKnnDistRescoreAttrName () );
922949 assert (pKNNDistRescore);
923950
924- MatchSortRescore_fn tRescore ( pKNNDistRescore->m_tLocator );
925- sphSort ( dMatches.Begin (), dMatches.GetLength (), tRescore, MatchSortAccessor_t () );
926-
927- // copy rescored dist to old dist
951+ // Copy rescored dist to old dist first, then re-apply the original sorter.
952+ // The original sorter state starts with knn_dist() (unless the user explicitly
953+ // sorted by knn_dist()) and then contains user ORDER BY tie-breakers.
954+ // Sorting by rescored distance only, even stably, can keep stale approximate
955+ // distance order ahead of explicit tie-breakers for exact-distance ties.
928956 for ( auto & tMatch : dMatches )
929957 for ( const auto & tLocator : dOldKnnDistLoc )
930958 tMatch.SetAttrFloat ( tLocator, tMatch.GetAttrFloat ( pKNNDistRescore->m_tLocator ) );
931959
960+ MatchSortRescore_fn tRescore ( m_pComp, m_pSorter->GetState () );
961+ sphSort ( dMatches.Begin (), dMatches.GetLength (), tRescore, MatchSortAccessor_t () );
962+
932963 for ( auto & i : dMatches )
933964 Swap ( i, *pTo++ );
934965
@@ -938,7 +969,7 @@ int RescoreSorter_c::Flatten ( CSphMatch * pTo )
938969
939970ISphMatchSorter * RescoreSorter_c::Clone () const
940971{
941- auto pClone = new RescoreSorter_c ( m_pSorter->Clone () );
972+ auto pClone = new RescoreSorter_c ( m_pSorter->Clone (), m_pComp );
942973 CloneTo (pClone);
943974 return pClone;
944975}
@@ -952,12 +983,16 @@ void RescoreSorter_c::CloneTo ( ISphMatchSorter * pTrg ) const
952983}
953984
954985
955- ISphMatchSorter * CreateKNNRescoreSorter ( ISphMatchSorter * pSorter, const KnnSearchSettings_t & tSettings )
986+ ISphMatchSorter * CreateKNNRescoreSorter ( ISphMatchSorter * pSorter, const KnnSearchSettings_t & tSettings, ESphSortFunc eMatchFunc )
956987{
957988 if ( tSettings.m_sAttr .IsEmpty () || !tSettings.m_bRescore )
958989 return pSorter;
959990
960- return new RescoreSorter_c (pSorter);
991+ CSphRefcountedPtr<ISphMatchComparator> pComp ( CreateMatchComparator ( eMatchFunc ) );
992+ if ( !pComp )
993+ return nullptr ;
994+
995+ return new RescoreSorter_c ( pSorter, std::move ( pComp ) );
961996}
962997
963998bool ValidateEmbeddingsAPITimeout ( const CSphString & sValue , int & iTimeout, CSphString & sError )
0 commit comments