diff --git a/src/main/java/org/opensearch/knn/index/KNNQueryBuilder.java b/src/main/java/org/opensearch/knn/index/KNNQueryBuilder.java index e38e416af..de35f4c51 100644 --- a/src/main/java/org/opensearch/knn/index/KNNQueryBuilder.java +++ b/src/main/java/org/opensearch/knn/index/KNNQueryBuilder.java @@ -25,6 +25,8 @@ package org.opensearch.knn.index; +import org.opensearch.knn.indices.ModelDao; +import org.opensearch.knn.indices.ModelMetadata; import org.opensearch.knn.plugin.stats.KNNCounter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -49,6 +51,8 @@ */ public class KNNQueryBuilder extends AbstractQueryBuilder { private static Logger logger = LogManager.getLogger(KNNQueryBuilder.class); + private static ModelDao modelDao; + public static final ParseField VECTOR_FIELD = new ParseField("vector"); public static final ParseField K_FIELD = new ParseField("k"); public static int K_MAX = 10000; @@ -92,6 +96,10 @@ public KNNQueryBuilder(String fieldName, float[] vector, int k) { this.k = k; } + public static void initialize(ModelDao modelDao) { + KNNQueryBuilder.modelDao = modelDao; + } + private static float[] ObjectsToFloats(List objs) { float[] vec = new float[objs.size()]; for (int i = 0; i < objs.size(); i++) { @@ -212,6 +220,22 @@ protected Query doToQuery(QueryShardContext context) throws IOException { int dimension = ((KNNVectorFieldMapper.KNNVectorFieldType) mappedFieldType).getDimension(); + // If the dimension is not set, then the only valid route forward is if the field uses a model + if (dimension == -1) { + String modelId = ((KNNVectorFieldMapper.KNNVectorFieldType) mappedFieldType).getModelId(); + + if (modelId == null) { + throw new IllegalArgumentException("Field '" + this.fieldName + "' does not have dimension set."); + } + + ModelMetadata modelMetadata = modelDao.getMetadata(modelId); + + if (modelMetadata == null) { + throw new IllegalArgumentException("Model ID \"" + modelId + "\" does not exist."); + } + dimension = modelMetadata.getDimension(); + } + if (dimension != vector.length) { throw new IllegalArgumentException("Query vector has invalid dimension: " + vector.length + ". Dimension should be: " + dimension); diff --git a/src/main/java/org/opensearch/knn/index/KNNVectorFieldMapper.java b/src/main/java/org/opensearch/knn/index/KNNVectorFieldMapper.java index ba104c288..f78322d98 100644 --- a/src/main/java/org/opensearch/knn/index/KNNVectorFieldMapper.java +++ b/src/main/java/org/opensearch/knn/index/KNNVectorFieldMapper.java @@ -233,23 +233,21 @@ public KNNVectorFieldMapper build(BuilderContext context) { String modelIdAsString = this.modelId.get(); if (modelIdAsString != null) { - ModelMetadata modelMetadata = modelDao.getMetadata(modelIdAsString); - - if (modelMetadata == null) { - throw new IllegalArgumentException("Model \"" + modelId + "\" does not exist"); - } + // Because model information is stored in cluster metadata, we are unable to get it here. This is + // because to get the cluster metadata, you need access to the cluster state. Because this code is + // sometimes used to initialize the cluster state/update cluster state, we cannot get the state here + // safely. So, we are unable to validate the model. The model gets validated during ingestion. return new ModelFieldMapper( name, - new KNNVectorFieldType(buildFullName(context), meta.getValue(), modelMetadata.getDimension()), + new KNNVectorFieldType(buildFullName(context), meta.getValue(), -1, modelIdAsString), multiFieldsBuilder.build(this, context), copyTo.build(), ignoreMalformed(context), stored.get(), hasDocValues.get(), modelDao, - modelIdAsString, - modelMetadata); + modelIdAsString); } // Build legacy @@ -314,10 +312,16 @@ public Mapper.Builder parse(String name, Map node, ParserCont public static class KNNVectorFieldType extends MappedFieldType { int dimension; + String modelId; public KNNVectorFieldType(String name, Map meta, int dimension) { + this(name, meta, dimension, null); + } + + public KNNVectorFieldType(String name, Map meta, int dimension, String modelId) { super(name, false, false, true, TextSearchInfo.NONE, meta); this.dimension = dimension; + this.modelId = modelId; } @Override @@ -345,6 +349,10 @@ public int getDimension() { return dimension; } + public String getModelId() { + return modelId; + } + @Override public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier searchLookup) { failIfNoDocValues(); @@ -385,6 +393,11 @@ protected String contentType() { @Override protected void parseCreateField(ParseContext context) throws IOException { + parseCreateField(context, fieldType().getDimension()); + } + + protected void parseCreateField(ParseContext context, int dimension) throws IOException { + if (!KNNSettings.isKNNPluginEnabled()) { throw new IllegalStateException("KNN plugin is disabled. To enable " + "update knn.plugin.enabled setting to true"); @@ -431,9 +444,9 @@ protected void parseCreateField(ParseContext context) throws IOException { context.parser().nextToken(); } - if (fieldType().dimension != vector.size()) { - String errorMessage = String.format("Vector dimension mismatch. Expected: %d, Given: %d", - fieldType().dimension, vector.size()); + if (dimension != vector.size()) { + String errorMessage = String.format("Vector dimension mismatch. Expected: %d, Given: %d", dimension, + vector.size()); throw new IllegalArgumentException(errorMessage); } @@ -606,21 +619,32 @@ protected static class ModelFieldMapper extends KNNVectorFieldMapper { private ModelFieldMapper(String simpleName, KNNVectorFieldType mappedFieldType, MultiFields multiFields, CopyTo copyTo, Explicit ignoreMalformed, boolean stored, - boolean hasDocValues, ModelDao modelDao, String modelId, ModelMetadata modelMetadata) { + boolean hasDocValues, ModelDao modelDao, String modelId) { super(simpleName, mappedFieldType, multiFields, copyTo, ignoreMalformed, stored, hasDocValues); this.modelId = modelId; this.modelDao = modelDao; this.fieldType = new FieldType(KNNVectorFieldMapper.Defaults.FIELD_TYPE); - this.fieldType.putAttribute(MODEL_ID, modelId); + this.fieldType.freeze(); + } - this.fieldType.putAttribute(DIMENSION, String.valueOf(modelMetadata.getDimension())); - this.fieldType.putAttribute(SPACE_TYPE, modelMetadata.getSpaceType().getValue()); - this.fieldType.putAttribute(KNN_ENGINE, modelMetadata.getKnnEngine().getName()); + @Override + protected void parseCreateField(ParseContext context) throws IOException { + // For the model field mapper, we cannot validate the model during index creation due to + // an issue with reading cluster state during mapper creation. So, we need to validate the + // model when ingestion starts. + ModelMetadata modelMetadata = this.modelDao.getMetadata(modelId); + + if (modelMetadata == null) { + throw new IllegalStateException("Model \"" + modelId + "\" from " + + context.mapperService().index().getName() + "'s mapping does not exist. Because the " + + "\"" + MODEL_ID + "\" parameter is not updateable, this index will need to " + + "be recreated with a valid model."); + } - this.fieldType.freeze(); + parseCreateField(context, modelMetadata.getDimension()); } } } diff --git a/src/main/java/org/opensearch/knn/index/KNNWeight.java b/src/main/java/org/opensearch/knn/index/KNNWeight.java index daa827532..0623f5f2f 100644 --- a/src/main/java/org/opensearch/knn/index/KNNWeight.java +++ b/src/main/java/org/opensearch/knn/index/KNNWeight.java @@ -47,6 +47,8 @@ import org.apache.lucene.store.FilterDirectory; import org.apache.lucene.util.DocIdSetBuilder; import org.opensearch.common.io.PathUtils; +import org.opensearch.knn.indices.ModelDao; +import org.opensearch.knn.indices.ModelMetadata; import org.opensearch.knn.plugin.stats.KNNCounter; import java.io.IOException; @@ -60,6 +62,7 @@ import java.util.stream.Collectors; import static org.opensearch.knn.common.KNNConstants.KNN_ENGINE; +import static org.opensearch.knn.common.KNNConstants.MODEL_ID; import static org.opensearch.knn.common.KNNConstants.SPACE_TYPE; import static org.opensearch.knn.plugin.stats.KNNCounter.GRAPH_QUERY_ERRORS; @@ -68,6 +71,8 @@ */ public class KNNWeight extends Weight { private static Logger logger = LogManager.getLogger(KNNWeight.class); + private static ModelDao modelDao; + private final KNNQuery knnQuery; private final float boost; @@ -80,6 +85,10 @@ public KNNWeight(KNNQuery query, float boost) { this.nativeMemoryCacheManager = NativeMemoryCacheManager.getInstance(); } + public static void initialize(ModelDao modelDao) { + KNNWeight.modelDao = modelDao; + } + @Override public Explanation explain(LeafReaderContext context, int doc) { return Explanation.match(1.0f, "No Explanation"); @@ -102,8 +111,24 @@ public Scorer scorer(LeafReaderContext context) throws IOException { return null; } - KNNEngine knnEngine = KNNEngine.getEngine(fieldInfo.getAttribute(KNN_ENGINE)); - SpaceType spaceType = SpaceType.getSpace(fieldInfo.getAttribute(SPACE_TYPE)); + KNNEngine knnEngine; + SpaceType spaceType; + + // Check if a modelId exists. If so, the space type and engine will need to be picked up from the model's + // metadata. + String modelId = fieldInfo.getAttribute(MODEL_ID); + if (modelId != null) { + ModelMetadata modelMetadata = modelDao.getMetadata(modelId); + if (modelMetadata == null) { + throw new RuntimeException("Model \"" + modelId + "\" does not exist."); + } + + knnEngine = modelMetadata.getKnnEngine(); + spaceType = modelMetadata.getSpaceType(); + } else { + knnEngine = KNNEngine.getEngine(fieldInfo.getAttribute(KNN_ENGINE)); + spaceType = SpaceType.getSpace(fieldInfo.getAttribute(SPACE_TYPE)); + } /* * In case of compound file, extension would be + c otherwise diff --git a/src/main/java/org/opensearch/knn/index/codec/KNN80Codec/KNN80DocValuesConsumer.java b/src/main/java/org/opensearch/knn/index/codec/KNN80Codec/KNN80DocValuesConsumer.java index 55cec34db..d3d58382e 100644 --- a/src/main/java/org/opensearch/knn/index/codec/KNN80Codec/KNN80DocValuesConsumer.java +++ b/src/main/java/org/opensearch/knn/index/codec/KNN80Codec/KNN80DocValuesConsumer.java @@ -62,7 +62,6 @@ import java.util.HashMap; import java.util.Map; -import static org.opensearch.knn.common.KNNConstants.DIMENSION; import static org.opensearch.knn.common.KNNConstants.MODEL_ID; import static org.opensearch.knn.index.codec.KNNCodecUtil.buildEngineFileName; @@ -91,9 +90,6 @@ public void addBinaryField(FieldInfo field, DocValuesProducer valuesProducer) th public void addKNNBinaryField(FieldInfo field, DocValuesProducer valuesProducer) throws IOException { KNNCounter.GRAPH_INDEX_REQUESTS.increment(); if (field.attributes().containsKey(KNNVectorFieldMapper.KNN_FIELD)) { - // Get engine to be used for indexing - String engineName = field.attributes().getOrDefault(KNNConstants.KNN_ENGINE, KNNEngine.DEFAULT.getName()); - KNNEngine knnEngine = KNNEngine.getEngine(engineName); // Get values to be indexed BinaryDocValues values = valuesProducer.getBinary(field); @@ -104,44 +100,42 @@ public void addKNNBinaryField(FieldInfo field, DocValuesProducer valuesProducer) } // Create library index either from model or from scratch - String engineFileName = buildEngineFileName(state.segmentInfo.name, knnEngine.getLatestBuildVersion(), - field.name, knnEngine.getExtension()); - String indexPath = Paths.get(((FSDirectory) (FilterDirectory.unwrap(state.directory))).getDirectory().toString(), - engineFileName).toString(); - String tmpEngineFileName = engineFileName + TEMP_SUFFIX; - String tempIndexPath = indexPath + TEMP_SUFFIX; + String engineFileName; + String indexPath; + String tmpEngineFileName; + if (field.attributes().containsKey(MODEL_ID)) { + String modelId = field.attributes().get(MODEL_ID); Model model = ModelCache.getInstance().get(modelId); - if (model.getModelBlob() == null) { - throw new RuntimeException("There is no model with id \"" + modelId + "\""); - } + KNNEngine knnEngine = model.getModelMetadata().getKnnEngine(); - if (model.getModelMetadata().getKnnEngine() != knnEngine) { - throw new RuntimeException("Model Engine \"" + model.getModelMetadata().getKnnEngine().getName() - + "\" cannot be different than index engine \"" + knnEngine.getName() + "\""); - } - - String spaceName = field.getAttribute(KNNConstants.SPACE_TYPE); - if (spaceName == null) { - throw new RuntimeException("Space Type cannot be null"); - } + engineFileName = buildEngineFileName(state.segmentInfo.name, knnEngine.getLatestBuildVersion(), + field.name, knnEngine.getExtension()); + indexPath = Paths.get(((FSDirectory) (FilterDirectory.unwrap(state.directory))).getDirectory().toString(), + engineFileName).toString(); + tmpEngineFileName = engineFileName + TEMP_SUFFIX; + String tempIndexPath = indexPath + TEMP_SUFFIX; - SpaceType spaceType = SpaceType.getSpace(spaceName); - if (model.getModelMetadata().getSpaceType() != spaceType) { - throw new RuntimeException("Model Space Type \"" + model.getModelMetadata().getSpaceType().getValue() - + "\" cannot be different than index Space Type \"" + spaceType.getValue() + "\""); - } - - int dimension = Integer.parseInt(field.attributes().getOrDefault(DIMENSION, "-1")); - if (model.getModelMetadata().getDimension() != dimension) { - throw new RuntimeException("Model dimension \"" + model.getModelMetadata().getDimension() - + "\" cannot be different than index dimension \"" + dimension + "\""); + if (model.getModelBlob() == null) { + throw new RuntimeException("There is no trained model with id \"" + modelId + "\""); } createKNNIndexFromTemplate(model.getModelBlob(), pair, knnEngine, tempIndexPath); } else { + + // Get engine to be used for indexing + String engineName = field.attributes().getOrDefault(KNNConstants.KNN_ENGINE, KNNEngine.DEFAULT.getName()); + KNNEngine knnEngine = KNNEngine.getEngine(engineName); + + engineFileName = buildEngineFileName(state.segmentInfo.name, knnEngine.getLatestBuildVersion(), + field.name, knnEngine.getExtension()); + indexPath = Paths.get(((FSDirectory) (FilterDirectory.unwrap(state.directory))).getDirectory().toString(), + engineFileName).toString(); + tmpEngineFileName = engineFileName + TEMP_SUFFIX; + String tempIndexPath = indexPath + TEMP_SUFFIX; + createKNNIndexFromScratch(field, pair, knnEngine, tempIndexPath); } diff --git a/src/main/java/org/opensearch/knn/plugin/KNNPlugin.java b/src/main/java/org/opensearch/knn/plugin/KNNPlugin.java index c6bd08ec8..0c52553b6 100644 --- a/src/main/java/org/opensearch/knn/plugin/KNNPlugin.java +++ b/src/main/java/org/opensearch/knn/plugin/KNNPlugin.java @@ -30,6 +30,7 @@ import org.opensearch.knn.index.KNNSettings; import org.opensearch.knn.index.KNNVectorFieldMapper; +import org.opensearch.knn.index.KNNWeight; import org.opensearch.knn.index.memory.NativeMemoryLoadStrategy; import org.opensearch.knn.indices.ModelCache; import org.opensearch.knn.indices.ModelDao; @@ -173,6 +174,8 @@ public Collection createComponents(Client client, ClusterService cluster ModelCache.initialize(ModelDao.OpenSearchKNNModelDao.getInstance(), clusterService); TrainingJobRunner.initialize(threadPool, ModelDao.OpenSearchKNNModelDao.getInstance()); KNNCircuitBreaker.getInstance().initialize(threadPool, clusterService, client); + KNNQueryBuilder.initialize(ModelDao.OpenSearchKNNModelDao.getInstance()); + KNNWeight.initialize(ModelDao.OpenSearchKNNModelDao.getInstance()); knnStats = new KNNStats(KNNStatsConfig.KNN_STATS); return ImmutableList.of(knnStats); } diff --git a/src/test/java/org/opensearch/knn/index/KNNCreateIndexFromModelTests.java b/src/test/java/org/opensearch/knn/index/KNNCreateIndexFromModelTests.java new file mode 100644 index 000000000..ae1356386 --- /dev/null +++ b/src/test/java/org/opensearch/knn/index/KNNCreateIndexFromModelTests.java @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.knn.index; + +import com.google.common.collect.ImmutableMap; +import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.knn.KNNSingleNodeTestCase; +import org.opensearch.knn.index.util.KNNEngine; +import org.opensearch.knn.indices.Model; +import org.opensearch.knn.indices.ModelDao; +import org.opensearch.knn.indices.ModelMetadata; +import org.opensearch.knn.indices.ModelState; + +import java.io.IOException; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.knn.common.KNNConstants.INDEX_DESCRIPTION_PARAMETER; +import static org.opensearch.knn.common.KNNConstants.SPACE_TYPE; + +public class KNNCreateIndexFromModelTests extends KNNSingleNodeTestCase { + + public void testCreateIndexFromModel() throws IOException, InterruptedException { + // This test confirms that we can create an index from cluster metadata + + // Create a model offline + String modelId = "test-model"; + KNNEngine knnEngine = KNNEngine.FAISS; + SpaceType spaceType = SpaceType.L2; + int dimension = 3; + + // "Train" a faiss flat index - this really just creates an empty index that does brute force k-NN + long vectorsPointer = JNIService.transferVectors(0, new float[0][0]); + byte [] modelBlob = JNIService.trainIndex(ImmutableMap.of( + INDEX_DESCRIPTION_PARAMETER, "Flat", + SPACE_TYPE, spaceType.getValue()), dimension, vectorsPointer, + KNNEngine.FAISS.getName()); + + // Setup model + ModelMetadata modelMetadata = new ModelMetadata(knnEngine, spaceType, dimension, ModelState.CREATED, + ZonedDateTime.now(ZoneOffset.UTC).toString(), "", ""); + + Model model = new Model(modelMetadata, modelBlob); + + // Check that an index can be created from the model. + ModelDao modelDao = ModelDao.OpenSearchKNNModelDao.getInstance(); + + final CountDownLatch inProgressLatch = new CountDownLatch(1); + + String indexName = "test-index"; + String fieldName = "test-field"; + + modelDao.put(modelId, model, ActionListener.wrap(indexResponse -> { + CreateIndexRequestBuilder createIndexRequestBuilder = client().admin().indices().prepareCreate(indexName) + .setSettings(Settings.builder() + .put("number_of_shards", 1) + .put("number_of_replicas", 0) + .put("index.knn", true) + .build() + ).addMapping( + "_doc", ImmutableMap.of( + "properties", ImmutableMap.of( + fieldName, ImmutableMap.of( + "type", "knn_vector", + "model_id", modelId + ) + ) + ) + ); + + client().admin().indices().create(createIndexRequestBuilder.request(), + ActionListener.wrap( + createIndexResponse -> { + assertTrue(createIndexResponse.isAcknowledged()); + inProgressLatch.countDown(); + }, e -> fail("Unable to create index: " + e.getMessage()) + ) + ); + + }, e ->fail("Unable to put model: " + e.getMessage()))); + + assertTrue(inProgressLatch.await(20, TimeUnit.SECONDS)); + } +} diff --git a/src/test/java/org/opensearch/knn/index/KNNQueryBuilderTests.java b/src/test/java/org/opensearch/knn/index/KNNQueryBuilderTests.java index c2411e1fc..c18265700 100644 --- a/src/test/java/org/opensearch/knn/index/KNNQueryBuilderTests.java +++ b/src/test/java/org/opensearch/knn/index/KNNQueryBuilderTests.java @@ -32,6 +32,8 @@ import org.opensearch.index.Index; import org.opensearch.index.mapper.NumberFieldMapper; import org.opensearch.index.query.QueryShardContext; +import org.opensearch.knn.indices.ModelDao; +import org.opensearch.knn.indices.ModelMetadata; import java.io.IOException; @@ -110,6 +112,32 @@ public void testDoToQuery_Normal() throws Exception { assertEquals(knnQueryBuilder.vector(), query.getQueryVector()); } + public void testDoToQuery_FromModel() throws Exception { + float[] queryVector = {1.0f, 2.0f, 3.0f, 4.0f}; + KNNQueryBuilder knnQueryBuilder = new KNNQueryBuilder("myvector", queryVector, 1); + Index dummyIndex = new Index("dummy", "dummy"); + QueryShardContext mockQueryShardContext = mock(QueryShardContext.class); + KNNVectorFieldMapper.KNNVectorFieldType mockKNNVectorField = mock(KNNVectorFieldMapper.KNNVectorFieldType.class); + when(mockQueryShardContext.index()).thenReturn(dummyIndex); + + // Dimension is -1. In this case, model metadata will need to provide dimension + when(mockKNNVectorField.getDimension()).thenReturn(-1); + String modelId = "test-model-id"; + when(mockKNNVectorField.getModelId()).thenReturn(modelId); + + // Mock the modelDao to return mocked modelMetadata + ModelMetadata modelMetadata = mock(ModelMetadata.class); + when(modelMetadata.getDimension()).thenReturn(4); + ModelDao modelDao = mock(ModelDao.class); + when(modelDao.getMetadata(modelId)).thenReturn(modelMetadata); + KNNQueryBuilder.initialize(modelDao); + + when(mockQueryShardContext.fieldMapper(anyString())).thenReturn(mockKNNVectorField); + KNNQuery query = (KNNQuery)knnQueryBuilder.doToQuery(mockQueryShardContext); + assertEquals(knnQueryBuilder.getK(), query.getK()); + assertEquals(knnQueryBuilder.fieldName(), query.getField()); + assertEquals(knnQueryBuilder.vector(), query.getQueryVector()); + } public void testDoToQuery_InvalidDimensions() { float[] queryVector = {1.0f, 2.0f, 3.0f, 4.0f}; diff --git a/src/test/java/org/opensearch/knn/index/codec/KNNCodecTestCase.java b/src/test/java/org/opensearch/knn/index/codec/KNNCodecTestCase.java index 75e514e33..5e64768cc 100644 --- a/src/test/java/org/opensearch/knn/index/codec/KNNCodecTestCase.java +++ b/src/test/java/org/opensearch/knn/index/codec/KNNCodecTestCase.java @@ -35,6 +35,7 @@ import org.opensearch.knn.index.KNNQuery; import org.opensearch.knn.index.KNNSettings; import org.opensearch.knn.index.KNNVectorFieldMapper; +import org.opensearch.knn.index.KNNWeight; import org.opensearch.knn.index.SpaceType; import org.opensearch.knn.index.VectorField; import org.opensearch.knn.index.codec.KNN87Codec.KNN87Codec; @@ -230,6 +231,7 @@ public void testBuildFromModelTemplate(Codec codec) throws IOException, Executio Model mockModel = new Model(modelMetadata1, modelBlob); when(modelDao.get(modelId)).thenReturn(mockModel); + when(modelDao.getMetadata(modelId)).thenReturn(modelMetadata1); Settings settings = settings(CURRENT).put(MODEL_CACHE_SIZE_IN_BYTES_SETTING.getKey(), 10).build(); ClusterSettings clusterSettings = new ClusterSettings(settings, @@ -251,9 +253,6 @@ public void testBuildFromModelTemplate(Codec codec) throws IOException, Executio FieldType fieldType = new FieldType(KNNVectorFieldMapper.Defaults.FIELD_TYPE); fieldType.putAttribute(KNNConstants.MODEL_ID, modelId); - fieldType.putAttribute(KNNConstants.KNN_ENGINE, KNNEngine.FAISS.getName()); - fieldType.putAttribute(KNNConstants.SPACE_TYPE, spaceType.getValue()); - fieldType.putAttribute(KNNConstants.DIMENSION, String.valueOf(dimension)); fieldType.freeze(); // Add the documents to the index @@ -277,6 +276,7 @@ public void testBuildFromModelTemplate(Codec codec) throws IOException, Executio writer.close(); // Make sure that search returns the correct results + KNNWeight.initialize(modelDao); NativeMemoryLoadStrategy.IndexLoadStrategy.initialize(createDisabledResourceWatcherService()); float [] query = {10.0f, 10.0f, 10.0f}; IndexSearcher searcher = new IndexSearcher(reader);