{"record":{"id":"ce3b1c5acd393808","repo":"ankane/searchkick","slug":"knn-requires-opensearch-2-4","errorCode":null,"errorMessage":"knn requires OpenSearch 2.4+","messagePattern":"knn requires OpenSearch 2\\.4\\+","errorType":"exception","errorClass":"Searchkick::Error","httpStatus":null,"severity":"error","filePath":"lib/searchkick/index_options.rb","lineNumber":175,"sourceCode":"\n      if Searchkick.env == \"test\"\n        settings[:number_of_shards] = 1\n        settings[:number_of_replicas] = 0\n      end\n\n      if options[:similarity]\n        settings[:similarity] = {default: {type: options[:similarity]}}\n      end\n\n      settings[:index] = {\n        max_ngram_diff: 49,\n        max_shingle_diff: 4\n      }\n\n      if options[:knn]\n        unless Searchkick.knn_support?\n          if Searchkick.opensearch?\n            raise Error, \"knn requires OpenSearch 2.4+\"\n          else\n            raise Error, \"knn requires Elasticsearch 8.6+\"\n          end\n        end\n\n        if Searchkick.opensearch? && options[:knn].any? { |_, v| !v[:distance].nil? }\n          # only enable if doing approximate search\n          settings[:index][:knn] = true\n        end\n      end\n\n      add_synonyms(settings)\n      add_search_synonyms(settings)\n\n      if options[:special_characters] == false\n        settings[:analysis][:analyzer].each_value do |analyzer_settings|\n          analyzer_settings[:filter].reject! { |f| f == \"asciifolding\" }\n        end","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/ankane/searchkick/blob/93e901a75b11a25101668a616e006b158251b16e/lib/searchkick/index_options.rb#L157-L193","documentation":"reindex(resume: true) skips already-indexed records by comparing against the index's total docs using relation.primary_key and an arel greater-than condition - Active Record only API. Mongoid relations do not respond to primary_key, so resume_relation raises Error 'Resume not supported for Mongoid'.","triggerScenarios":"Calling Product.reindex(resume: true) on a Mongoid model - commonly when resuming an interrupted reindex job or when scripts always pass resume: true after a failure.","commonSituations":"Recovering a large interrupted reindex on a Mongoid app; copy-pasting resume flows from ActiveRecord-based projects; automation that passes resume: true unconditionally.","solutions":["Run the full reindex without resume: Product.reindex","For long-running Mongoid reindexes, use mode: :async or :queue with batches and restart from scratch on failure","Narrow the work with a where(...) scope instead of resuming"],"exampleFix":"# before\nProduct.reindex(resume: true) # Mongoid model\n# => Searchkick::Error: Resume not supported for Mongoid\n\n# after\nProduct.reindex # full reindex, no resume\n# or scope the work\nProduct.where(created_at: 2.days.ago..).reindex","handlingStrategy":"validation","validationCode":"def reindex_with_resume?(relation)\n  relation.respond_to?(:primary_key) # Active Record only\nend\n\nProduct.reindex(resume: reindex_with_resume?(Product.all) && user_wants_resume)","typeGuard":null,"tryCatchPattern":"begin\n  Product.reindex(resume: true)\nrescue Searchkick::Error => e\n  raise unless e.message == 'Resume not supported for Mongoid'\n  Product.reindex # full reindex fallback\nend","preventionTips":["Gate resume: true behind a respond_to?(:primary_key) check for shared codebases","For Mongoid, run reindexes in batches scoped by time/id instead of resuming","Document ORM differences where teams mix ActiveRecord and Mongoid"],"tags":["searchkick","mongoid","reindex","resume","orm"],"backgroundTag":"unsupported-platform-feature","analyzedSha":"93e901a75b11a25101668a616e006b158251b16e","analyzedAt":"2026-08-21T19:06:23.767Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}