@@ -387,7 +387,7 @@ private DenseVectorIndexOptions defaultIndexOptions(boolean defaultInt8Hnsw, boo
387387 return new BBQHnswIndexOptions (
388388 Lucene99HnswVectorsFormat .DEFAULT_MAX_CONN ,
389389 Lucene99HnswVectorsFormat .DEFAULT_BEAM_WIDTH ,
390- new RescoreVector ( DEFAULT_OVERSAMPLE )
390+ null
391391 );
392392 } else if (defaultInt8Hnsw ) {
393393 return new Int8HnswIndexOptions (
@@ -1632,9 +1632,6 @@ public DenseVectorIndexOptions parseIndexOptions(String fieldName, Map<String, ?
16321632 RescoreVector rescoreVector = null ;
16331633 if (hasRescoreIndexVersion (indexVersion )) {
16341634 rescoreVector = RescoreVector .fromIndexOptions (indexOptionsMap , indexVersion );
1635- if (rescoreVector == null && defaultOversampleForBBQ (indexVersion )) {
1636- rescoreVector = new RescoreVector (DEFAULT_OVERSAMPLE );
1637- }
16381635 }
16391636 MappingParser .checkNoRemainingFields (fieldName , indexOptionsMap );
16401637 return new BBQHnswIndexOptions (m , efConstruction , rescoreVector );
@@ -1656,9 +1653,6 @@ public DenseVectorIndexOptions parseIndexOptions(String fieldName, Map<String, ?
16561653 RescoreVector rescoreVector = null ;
16571654 if (hasRescoreIndexVersion (indexVersion )) {
16581655 rescoreVector = RescoreVector .fromIndexOptions (indexOptionsMap , indexVersion );
1659- if (rescoreVector == null && defaultOversampleForBBQ (indexVersion )) {
1660- rescoreVector = new RescoreVector (DEFAULT_OVERSAMPLE );
1661- }
16621656 }
16631657 MappingParser .checkNoRemainingFields (fieldName , indexOptionsMap );
16641658 return new BBQFlatIndexOptions (rescoreVector );
@@ -1693,9 +1687,6 @@ public DenseVectorIndexOptions parseIndexOptions(String fieldName, Map<String, ?
16931687 }
16941688 }
16951689 RescoreVector rescoreVector = RescoreVector .fromIndexOptions (indexOptionsMap , indexVersion );
1696- if (rescoreVector == null ) {
1697- rescoreVector = new RescoreVector (DEFAULT_OVERSAMPLE );
1698- }
16991690 Object nProbeNode = indexOptionsMap .remove ("default_n_probe" );
17001691 int nProbe = -1 ;
17011692 if (nProbeNode != null ) {
@@ -2183,7 +2174,8 @@ public BBQHnswIndexOptions(int m, int efConstruction, RescoreVector rescoreVecto
21832174 @ Override
21842175 KnnVectorsFormat getVectorsFormat (ElementType elementType ) {
21852176 assert elementType == ElementType .FLOAT ;
2186- return new ES818HnswBinaryQuantizedVectorsFormat (m , efConstruction );
2177+ boolean directIO = rescoreVector .useDirectIO != null && rescoreVector .useDirectIO ;
2178+ return new ES818HnswBinaryQuantizedVectorsFormat (m , efConstruction , directIO );
21872179 }
21882180
21892181 @ Override
@@ -2342,36 +2334,44 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
23422334 }
23432335 }
23442336
2345- public record RescoreVector (float oversample ) implements ToXContentObject {
2337+ public record RescoreVector (Float oversample , Boolean useDirectIO ) implements ToXContentObject {
23462338 static final String NAME = "rescore_vector" ;
23472339 static final String OVERSAMPLE = "oversample" ;
2340+ static final String DIRECT_IO = "direct_io" ;
23482341
23492342 static RescoreVector fromIndexOptions (Map <String , ?> indexOptionsMap , IndexVersion indexVersion ) {
23502343 Object rescoreVectorNode = indexOptionsMap .remove (NAME );
23512344 if (rescoreVectorNode == null ) {
23522345 return null ;
23532346 }
23542347 Map <String , Object > mappedNode = XContentMapValues .nodeMapValue (rescoreVectorNode , NAME );
2348+
2349+ Float oversampleValue = null ;
23552350 Object oversampleNode = mappedNode .get (OVERSAMPLE );
2356- if (oversampleNode == null ) {
2357- throw new IllegalArgumentException ("Invalid rescore_vector value. Missing required field " + OVERSAMPLE );
2358- }
2359- float oversampleValue = (float ) XContentMapValues .nodeDoubleValue (oversampleNode );
2360- if (oversampleValue == 0 && allowsZeroRescore (indexVersion ) == false ) {
2361- throw new IllegalArgumentException ("oversample must be greater than 1" );
2362- }
2363- if (oversampleValue < 1 && oversampleValue != 0 ) {
2364- throw new IllegalArgumentException ("oversample must be greater than 1 or exactly 0" );
2365- } else if (oversampleValue > 10 ) {
2366- throw new IllegalArgumentException ("oversample must be less than or equal to 10" );
2351+ if (oversampleNode != null ) {
2352+ oversampleValue = (float ) XContentMapValues .nodeDoubleValue (oversampleNode );
2353+ if (oversampleValue == 0 && allowsZeroRescore (indexVersion ) == false ) {
2354+ throw new IllegalArgumentException ("oversample must be greater than 1" );
2355+ }
2356+ if (oversampleValue < 1 && oversampleValue != 0 ) {
2357+ throw new IllegalArgumentException ("oversample must be greater than 1 or exactly 0" );
2358+ } else if (oversampleValue > 10 ) {
2359+ throw new IllegalArgumentException ("oversample must be less than or equal to 10" );
2360+ }
23672361 }
2368- return new RescoreVector (oversampleValue );
2362+
2363+ Boolean directIO = (Boolean ) mappedNode .get (DIRECT_IO );
2364+
2365+ return new RescoreVector (oversampleValue , directIO );
23692366 }
23702367
23712368 @ Override
23722369 public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
23732370 builder .startObject (NAME );
23742371 builder .field (OVERSAMPLE , oversample );
2372+ if (useDirectIO ) {
2373+ builder .field (DIRECT_IO , useDirectIO );
2374+ }
23752375 builder .endObject ();
23762376 return builder ;
23772377 }
@@ -2710,6 +2710,10 @@ && isNotUnitVector(squaredMagnitude)) {
27102710 && quantizedIndexOptions .rescoreVector != null ) {
27112711 oversample = quantizedIndexOptions .rescoreVector .oversample ;
27122712 }
2713+ if (oversample == null ) {
2714+ oversample = DEFAULT_OVERSAMPLE ;
2715+ }
2716+
27132717 boolean rescore = needsRescore (oversample );
27142718 if (rescore ) {
27152719 // Will get k * oversample for rescoring, and get the top k
0 commit comments