{"record":{"id":"9645e3095bffcc87","repo":"spring-projects/spring-ai","slug":"similarity-threshold-filtering-requires-all-vector","errorCode":null,"errorMessage":"Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store. Also only COSINE and DOT distance types are supported.","messagePattern":"Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store\\. Also only COSINE and DOT distance types are supported\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java","lineNumber":418,"sourceCode":"\t\t\t\t\t\tselect id, content, metadata, embedding, %sVECTOR_DISTANCE(embedding, ?, %s)%s as distance\n\t\t\t\t\t\tfrom %s\n\t\t\t\t\t\t%sorder by distance\n\t\t\t\t\t\tfetch EXACT first %d rows only\"\"\",\n\t\t\t\t\t\tthis.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? \"(1+\" : \"\",\n\t\t\t\t\t\tthis.distanceType.name(),\n\t\t\t\t\t\tthis.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? \")/2\" : \"\",\n\t\t\t\t\t\tthis.tableName, jsonPathFilter, request.getTopK());\n\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"SQL query: \" + sql);\n\t\t\t\t}\n\n\t\t\t\treturn this.jdbcTemplate.query(sql, new DocumentRowMapper(), embeddingVector);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (!this.forcedNormalization || (this.distanceType != OracleVectorStoreDistanceType.COSINE\n\t\t\t\t\t\t&& this.distanceType != OracleVectorStore.OracleVectorStoreDistanceType.DOT)) {\n\t\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\t\"Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store. Also only COSINE and DOT distance types are supported.\");\n\t\t\t\t}\n\n\t\t\t\tfinal double distance = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT\n\t\t\t\t\t\t? (1d - request.getSimilarityThreshold()) * 2d - 1d : 1d - request.getSimilarityThreshold();\n\n\t\t\t\tif (StringUtils.hasText(nativeFilterExpression)) {\n\t\t\t\t\tjsonPathFilter = \" and \" + nativeFilterExpression;\n\t\t\t\t}\n\n\t\t\t\tfinal String sql = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT\n\t\t\t\t\t\t? (this.searchAccuracy == DEFAULT_SEARCH_ACCURACY\n\t\t\t\t\t\t\t\t? String.format(\n\t\t\t\t\t\t\t\t\t\t\"\"\"\n\t\t\t\t\t\t\t\t\t\t\t\tselect id, content, metadata, embedding, (1+VECTOR_DISTANCE(embedding, ?, DOT))/2 as distance\n\t\t\t\t\t\t\t\t\t\t\t\tfrom %s\n\t\t\t\t\t\t\t\t\t\t\t\twhere VECTOR_DISTANCE(embedding, ?, DOT) <= ?%s\n\t\t\t\t\t\t\t\t\t\t\t\torder by distance","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java#L400-L436","documentation":"OracleVectorStore.doSimilaritySearch throws this RuntimeException when a similarity threshold is used but the store cannot guarantee meaningful threshold math: either forcedNormalization was not enabled or the configured distance type is not COSINE or DOT. Threshold filtering requires normalized vectors so distance can be derived from similarity.","triggerScenarios":"Calling similaritySearch(SearchRequest) with a similarityThreshold > 0 on an OracleVectorStore built without forcedNormalization=true, or with OracleVectorStoreDistanceType.EUCLIDEAN / other non-COSINE/DOT distance.","commonSituations":"Default builder config (no forcedNormalization) combined with a nonzero threshold; switching distance type to EUCLIDEAN for legacy data while keeping threshold logic; documents ingested without L2 normalization.","solutions":["Set forcedNormalization(true) in the OracleVectorStore builder and re-ingest vectors so they are normalized.","Switch the distance type to COSINE or DOT via OracleVectorStoreDistanceType.","Set the search threshold to 0 if you cannot normalize vectors, and filter results in application code instead.","Verify existing rows are normalized (L2 norm = 1) before enabling threshold filtering."],"exampleFix":"// before\nOracleVectorStore.builder(jdbcTemplate, embeddingModel)\n    .distanceType(OracleVectorStoreDistanceType.EUCLIDEAN)\n    .build();\nrequest.withSimilarityThreshold(0.7); // throws\n// after\nOracleVectorStore.builder(jdbcTemplate, embeddingModel)\n    .forcedNormalization(true)\n    .distanceType(OracleVectorStoreDistanceType.COSINE)\n    .build();","handlingStrategy":"validation","validationCode":"boolean thresholdUsable = store.isForcedNormalization()\n    && (store.getDistanceType() == OracleVectorStoreDistanceType.COSINE\n     || store.getDistanceType() == OracleVectorStoreDistanceType.DOT);\nif (!thresholdUsable) request = request.withSimilarityThreshold(0);","typeGuard":null,"tryCatchPattern":"try {\n    vectorStore.similaritySearch(request.withSimilarityThreshold(0.75));\n} catch (RuntimeException e) {\n    if (e.getMessage().contains(\"Similarity threshold filtering requires\")) {\n        results = vectorStore.similaritySearch(request.withSimilarityThreshold(0)); // post-filter in app\n    }\n}","preventionTips":["Enable forcedNormalization(true) in the builder when using thresholds","Use COSINE or DOT distance types whenever thresholds matter","Re-ingest after changing normalization settings so old vectors match","Verify stored vectors have L2 norm 1 before threshold filtering"],"tags":["oracle","vector-store","similarity-threshold","normalization"],"backgroundTag":"conflicting-config-options","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}