Skip to content

Commit 5d98552

Browse files
authored
Update default engine to FAISS (opensearch-project#2235)
Since faiss supports more features than nmslib, and, we had seen data points that there are more number of vector search users are interesed in faiss, we will be updating default engine to be faiss. This will benefit users who preffered to use defaults while working with vector search. Signed-off-by: Vijayan Balasubramanian <[email protected]>
1 parent 09c3754 commit 5d98552

19 files changed

+163
-113
lines changed

qa/restart-upgrade/src/test/java/org/opensearch/knn/bwc/IndexingIT.java

+17
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ public void testKNNIndexDefaultLegacyFieldMapping() throws Exception {
6161
}
6262
}
6363

64+
// ensure that index is created using legacy mapping in old cluster, and, then docs are added, and, new docs are added
65+
// in new cluster, search should return docs when it was indexed during old cluster and new cluster.
66+
public void testKNNIndexDefaultEngine() throws Exception {
67+
waitForClusterHealthGreen(NODES_BWC_CLUSTER);
68+
if (isRunningAgainstOldCluster()) {
69+
createKnnIndex(testIndex, getKNNDefaultIndexSettings(), createKnnIndexMapping(TEST_FIELD, DIMENSIONS));
70+
addKNNDocs(testIndex, TEST_FIELD, DIMENSIONS, DOC_ID, 5);
71+
// Flush to ensure that index is not re-indexed when node comes back up
72+
flush(testIndex, true);
73+
} else {
74+
validateKNNSearch(testIndex, TEST_FIELD, DIMENSIONS, 5, 5);
75+
addKNNDocs(testIndex, TEST_FIELD, DIMENSIONS, 5, 5);
76+
validateKNNSearch(testIndex, TEST_FIELD, DIMENSIONS, 10, 10);
77+
deleteKNNIndex(testIndex);
78+
}
79+
}
80+
6481
// Ensure that when segments created with old mapping are forcemerged in new cluster, they
6582
// succeed
6683
public void testKNNIndexDefaultLegacyFieldMappingForceMerge() throws Exception {

release-notes/opensearch-knn.release-notes-2.18.0.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Compatible with OpenSearch 2.18.0
1515
* Add CompressionLevel Calculation for PQ [#2200](https://github.com/opensearch-project/k-NN/pull/2200)
1616
* Remove FSDirectory dependency from native engine constructing side and deprecated FileWatcher [#2182](https://github.com/opensearch-project/k-NN/pull/2182)
1717
* Update approximate_threshold to 15K documents [#2229](https://github.com/opensearch-project/k-NN/pull/2229)
18+
* Update default engine to FAISS [#2221](https://github.com/opensearch-project/k-NN/pull/2221)
1819
### Bug Fixes
1920
* Add DocValuesProducers for releasing memory when close index [#1946](https://github.com/opensearch-project/k-NN/pull/1946)
2021
* KNN80DocValues should only be considered for BinaryDocValues fields [#2147](https://github.com/opensearch-project/k-NN/pull/2147)

src/main/java/org/opensearch/knn/index/engine/EngineResolver.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public KNNEngine resolveEngine(
4949

5050
// For 1x, we need to default to faiss if mode is provided and use nmslib otherwise
5151
if (CompressionLevel.isConfigured(compressionLevel) == false || compressionLevel == CompressionLevel.x1) {
52-
return mode == Mode.ON_DISK ? KNNEngine.FAISS : KNNEngine.DEFAULT;
52+
return mode == Mode.ON_DISK ? KNNEngine.FAISS : KNNEngine.NMSLIB;
5353
}
5454

5555
// Lucene is only engine that supports 4x - so we have to default to it here.

src/main/java/org/opensearch/knn/index/engine/KNNEngine.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public enum KNNEngine implements KNNLibrary {
2929
FAISS(FAISS_NAME, Faiss.INSTANCE),
3030
LUCENE(LUCENE_NAME, Lucene.INSTANCE);
3131

32-
public static final KNNEngine DEFAULT = NMSLIB;
32+
public static final KNNEngine DEFAULT = FAISS;
3333

3434
private static final Set<KNNEngine> CUSTOM_SEGMENT_FILE_ENGINES = ImmutableSet.of(KNNEngine.NMSLIB, KNNEngine.FAISS);
3535
private static final Set<KNNEngine> ENGINES_SUPPORTING_FILTERS = ImmutableSet.of(KNNEngine.LUCENE, KNNEngine.FAISS);

src/main/java/org/opensearch/knn/index/mapper/KNNVectorFieldMapper.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,7 @@ private void resolveKNNMethodComponents(
500500
.build()
501501
);
502502

503-
// If the original parameters are from legacy
504-
if (builder.originalParameters.isLegacyMapping()) {
503+
if (useKNNMethodContextFromLegacy(builder, parserContext)) {
505504
// Then create KNNMethodContext to be used from the legacy index settings
506505
builder.originalParameters.setResolvedKnnMethodContext(
507506
createKNNMethodContextFromLegacy(parserContext.getSettings(), parserContext.indexVersionCreated(), resolvedSpaceType)
@@ -550,6 +549,12 @@ private void setEngine(final KNNMethodContext knnMethodContext, KNNEngine knnEng
550549
}
551550
}
552551

552+
static boolean useKNNMethodContextFromLegacy(Builder builder, Mapper.TypeParser.ParserContext parserContext) {
553+
// If the original parameters are from legacy, and it is created on or before 2_17_2 since default is changed to
554+
// FAISS starting 2_18, which doesn't support accepting algo params from index settings
555+
return parserContext.indexVersionCreated().onOrBefore(Version.V_2_17_2) && builder.originalParameters.isLegacyMapping();
556+
}
557+
553558
// We store the version of the index with the mapper as different version of Opensearch has different default
554559
// values of KNN engine Algorithms hyperparameters.
555560
protected Version indexCreatedVersion;

src/main/java/org/opensearch/knn/index/query/KNNWeight.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ private Map<Integer, Float> doANNSearch(
251251
spaceType = modelMetadata.getSpaceType();
252252
vectorDataType = modelMetadata.getVectorDataType();
253253
} else {
254-
String engineName = fieldInfo.attributes().getOrDefault(KNN_ENGINE, KNNEngine.NMSLIB.getName());
254+
String engineName = fieldInfo.attributes().getOrDefault(KNN_ENGINE, KNNEngine.DEFAULT.getName());
255255
knnEngine = KNNEngine.getEngine(engineName);
256256
String spaceTypeName = fieldInfo.attributes().getOrDefault(SPACE_TYPE, SpaceType.L2.getValue());
257257
spaceType = SpaceType.getSpace(spaceTypeName);

src/test/java/org/opensearch/knn/KNNSingleNodeTestCase.java

+18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.opensearch.cluster.service.ClusterService;
1717
import org.opensearch.common.xcontent.XContentHelper;
1818
import org.opensearch.knn.index.KNNSettings;
19+
import org.opensearch.knn.index.engine.KNNEngine;
1920
import org.opensearch.knn.index.query.KNNQueryBuilder;
2021
import org.opensearch.knn.index.memory.NativeMemoryCacheManager;
2122
import org.opensearch.knn.index.memory.NativeMemoryLoadStrategy;
@@ -50,6 +51,7 @@
5051
import static org.mockito.Mockito.when;
5152
import static org.opensearch.knn.common.KNNConstants.DIMENSION;
5253
import static org.opensearch.knn.common.KNNConstants.KNN_ENGINE;
54+
import static org.opensearch.knn.common.KNNConstants.METHOD_HNSW;
5355
import static org.opensearch.knn.common.KNNConstants.METHOD_PARAMETER_SPACE_TYPE;
5456
import static org.opensearch.knn.common.KNNConstants.MODEL_BLOB_PARAMETER;
5557
import static org.opensearch.knn.common.KNNConstants.MODEL_DESCRIPTION;
@@ -109,6 +111,22 @@ protected void createKnnIndexMapping(String indexName, String fieldName, Integer
109111
/**
110112
* Create simple k-NN mapping with engine
111113
*/
114+
protected void createKnnIndexMapping(String indexName, String fieldName, Integer dimensions, KNNEngine engine) throws IOException {
115+
PutMappingRequest request = new PutMappingRequest(indexName);
116+
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("properties");
117+
xContentBuilder.startObject(fieldName);
118+
xContentBuilder.field("type", "knn_vector").field("dimension", dimensions.toString());
119+
xContentBuilder.startObject("method");
120+
xContentBuilder.field("name", METHOD_HNSW);
121+
xContentBuilder.field(KNN_ENGINE, engine.getName());
122+
xContentBuilder.endObject();
123+
xContentBuilder.endObject();
124+
xContentBuilder.endObject();
125+
xContentBuilder.endObject();
126+
request.source(xContentBuilder);
127+
OpenSearchAssertions.assertAcked(client().admin().indices().putMapping(request).actionGet());
128+
}
129+
112130
protected void updateIndexSetting(String indexName, Settings setting) {
113131
UpdateSettingsRequest request = new UpdateSettingsRequest(setting, indexName);
114132
OpenSearchAssertions.assertAcked(client().admin().indices().updateSettings(request).actionGet());

src/test/java/org/opensearch/knn/index/KNNIndexShardTests.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.opensearch.index.IndexService;
2020
import org.opensearch.index.engine.Engine;
2121
import org.opensearch.index.shard.IndexShard;
22+
import org.opensearch.knn.index.engine.KNNEngine;
2223
import org.opensearch.knn.index.memory.NativeMemoryCacheManager;
2324

2425
import java.io.IOException;
@@ -108,7 +109,7 @@ public void testWarmup_shardNotPresentInCache() throws InterruptedException, Exe
108109

109110
public void testGetAllEngineFileContexts() throws IOException, ExecutionException, InterruptedException {
110111
IndexService indexService = createKNNIndex(testIndexName);
111-
createKnnIndexMapping(testIndexName, testFieldName, dimensions);
112+
createKnnIndexMapping(testIndexName, testFieldName, dimensions, KNNEngine.NMSLIB);
112113
updateIndexSetting(testIndexName, Settings.builder().put(KNNSettings.INDEX_KNN_ADVANCED_APPROXIMATE_THRESHOLD, 0).build());
113114

114115
IndexShard indexShard = indexService.iterator().next();

src/test/java/org/opensearch/knn/index/NmslibIT.java

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.TreeMap;
3737

3838
import static org.hamcrest.Matchers.containsString;
39+
import static org.opensearch.knn.common.KNNConstants.KNN_ENGINE;
3940

4041
public class NmslibIT extends KNNRestTestCase {
4142

@@ -281,6 +282,7 @@ public void testCreateIndexWithValidAlgoParams_mapping() {
281282
.field("dimension", 2)
282283
.startObject(KNNConstants.KNN_METHOD)
283284
.field(KNNConstants.METHOD_PARAMETER_SPACE_TYPE, spaceType)
285+
.field(KNN_ENGINE, KNNEngine.NMSLIB.getName())
284286
.field(KNNConstants.NAME, KNNConstants.METHOD_HNSW)
285287
.startObject(KNNConstants.PARAMETERS)
286288
.field(KNNConstants.METHOD_PARAMETER_EF_CONSTRUCTION, efConstruction)
@@ -336,6 +338,7 @@ public void testCreateIndexWithValidAlgoParams_mappingAndSettings() {
336338
.field("dimension", 2)
337339
.startObject(KNNConstants.KNN_METHOD)
338340
.field(KNNConstants.METHOD_PARAMETER_SPACE_TYPE, spaceType1)
341+
.field(KNN_ENGINE, KNNEngine.NMSLIB.getName())
339342
.field(KNNConstants.NAME, KNNConstants.METHOD_HNSW)
340343
.startObject(KNNConstants.PARAMETERS)
341344
.field(KNNConstants.METHOD_PARAMETER_EF_CONSTRUCTION, efConstruction1)
@@ -363,6 +366,7 @@ public void testCreateIndexWithValidAlgoParams_mappingAndSettings() {
363366
.field("dimension", 2)
364367
.startObject(KNNConstants.KNN_METHOD)
365368
.field(KNNConstants.METHOD_PARAMETER_SPACE_TYPE, spaceType1)
369+
.field(KNN_ENGINE, KNNEngine.NMSLIB.getName())
366370
.field(KNNConstants.NAME, KNNConstants.METHOD_HNSW)
367371
.startObject(KNNConstants.PARAMETERS)
368372
.field(KNNConstants.METHOD_PARAMETER_EF_CONSTRUCTION, efConstruction1)
@@ -375,6 +379,7 @@ public void testCreateIndexWithValidAlgoParams_mappingAndSettings() {
375379
.field("dimension", 2)
376380
.startObject(KNNConstants.KNN_METHOD)
377381
.field(KNNConstants.METHOD_PARAMETER_SPACE_TYPE, spaceType2)
382+
.field(KNN_ENGINE, KNNEngine.NMSLIB.getName())
378383
.field(KNNConstants.NAME, KNNConstants.METHOD_HNSW)
379384
.startObject(KNNConstants.PARAMETERS)
380385
.field(KNNConstants.METHOD_PARAMETER_EF_CONSTRUCTION, efConstruction2)

src/test/java/org/opensearch/knn/index/VectorDataTypeIT.java

-23
Original file line numberDiff line numberDiff line change
@@ -263,29 +263,6 @@ public void testByteVectorDataTypeWithNmslibEngine() {
263263
assertTrue(ex.getMessage().contains("is not supported for vector data type"));
264264
}
265265

266-
@SneakyThrows
267-
public void testByteVectorDataTypeWithLegacyFieldMapperKnnIndexSetting() {
268-
// Create an index with byte vector data_type and index.knn as true without setting KnnMethodContext,
269-
// which should throw an exception because the LegacyFieldMapper will use NMSLIB engine and byte data_type
270-
// is not supported for NMSLIB engine.
271-
XContentBuilder builder = XContentFactory.jsonBuilder()
272-
.startObject()
273-
.startObject(PROPERTIES_FIELD)
274-
.startObject(FIELD_NAME)
275-
.field(TYPE_FIELD_NAME, KNN_VECTOR_TYPE)
276-
.field(DIMENSION, 2)
277-
.field(VECTOR_DATA_TYPE_FIELD, VectorDataType.BYTE.getValue())
278-
.endObject()
279-
.endObject()
280-
.endObject();
281-
282-
String mapping = builder.toString();
283-
284-
ResponseException ex = expectThrows(ResponseException.class, () -> createKnnIndex(INDEX_NAME, mapping));
285-
assertTrue(ex.getMessage(), ex.getMessage().contains("is not supported for vector data type"));
286-
287-
}
288-
289266
public void testDocValuesWithByteVectorDataTypeLuceneEngine() throws Exception {
290267
createKnnIndexMappingWithLuceneEngine(2, SpaceType.L2, VectorDataType.BYTE.getValue());
291268
ingestL2ByteTestData();

src/test/java/org/opensearch/knn/index/codec/KNN80Codec/KNN80DocValuesConsumerTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ public void testAddKNNBinaryField_fromScratch_nmslibLegacy() throws IOException
279279
.addAttribute(KNNConstants.HNSW_ALGO_EF_CONSTRUCTION, "512")
280280
.addAttribute(KNNConstants.HNSW_ALGO_M, "16")
281281
.addAttribute(KNNConstants.SPACE_TYPE, spaceType.getValue())
282+
.addAttribute(KNNConstants.KNN_ENGINE, knnEngine.getName())
282283
.build() };
283284

284285
FieldInfos fieldInfos = new FieldInfos(fieldInfoArray);

src/test/java/org/opensearch/knn/index/codec/KNNCodecTestCase.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public class KNNCodecTestCase extends KNNTestCase {
103103
.vectorDataType(VectorDataType.DEFAULT)
104104
.build();
105105
KNNMethodContext knnMethodContext = new KNNMethodContext(
106-
KNNEngine.DEFAULT,
106+
KNNEngine.NMSLIB,
107107
SpaceType.DEFAULT,
108108
new MethodComponentContext(METHOD_HNSW, ImmutableMap.of(METHOD_PARAMETER_M, 16, METHOD_PARAMETER_EF_CONSTRUCTION, 512))
109109
);

src/test/java/org/opensearch/knn/index/engine/EngineResolverTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ public void testResolveEngine_whenModeAndCompressionAreFalse_thenDefault() {
4141
);
4242
}
4343

44-
public void testResolveEngine_whenModeSpecifiedAndCompressionIsNotSpecified_thenDefault() {
44+
public void testResolveEngine_whenModeSpecifiedAndCompressionIsNotSpecified_thenNMSLIB() {
4545
assertEquals(KNNEngine.DEFAULT, ENGINE_RESOLVER.resolveEngine(KNNMethodConfigContext.builder().build(), null, false));
4646
assertEquals(
47-
KNNEngine.DEFAULT,
47+
KNNEngine.NMSLIB,
4848
ENGINE_RESOLVER.resolveEngine(
4949
KNNMethodConfigContext.builder().mode(Mode.IN_MEMORY).build(),
5050
new KNNMethodContext(KNNEngine.DEFAULT, SpaceType.UNDEFINED, MethodComponentContext.EMPTY, false),
@@ -63,7 +63,7 @@ public void testResolveEngine_whenCompressionIs1x_thenEngineBasedOnMode() {
6363
)
6464
);
6565
assertEquals(
66-
KNNEngine.DEFAULT,
66+
KNNEngine.NMSLIB,
6767
ENGINE_RESOLVER.resolveEngine(KNNMethodConfigContext.builder().compressionLevel(CompressionLevel.x1).build(), null, false)
6868
);
6969
}

src/test/java/org/opensearch/knn/index/engine/KNNEngineTests.java

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public void testDelegateLibraryFunctions() {
2525
assertEquals(Lucene.INSTANCE.getVersion(), KNNEngine.LUCENE.getVersion());
2626
}
2727

28+
public void testGetDefaultEngine_thenReturnFAISS() {
29+
assertEquals(KNNEngine.FAISS, KNNEngine.DEFAULT);
30+
}
31+
2832
/**
2933
* Test name getter
3034
*/

0 commit comments

Comments
 (0)