{"record":{"id":"3cceae77748d3f05","repo":"ankane/searchkick","slug":"class-name-is-not-a-searchkick-model","errorCode":null,"errorMessage":"#{class_name} is not a searchkick model","messagePattern":"#(.+?) is not a searchkick model","errorType":"exception","errorClass":"Searchkick::Error","httpStatus":null,"severity":"error","filePath":"lib/searchkick.rb","lineNumber":307,"sourceCode":"        raise Error, \"Need primary key to load records\" if !primary_key\n\n        relation.where(primary_key => ids)\n      elsif relation.respond_to?(:queryable)\n        relation.queryable.for_ids(ids)\n      end\n\n    raise Error, \"Not sure how to load records\" if !relation\n\n    relation\n  end\n\n  # public (for reindexing conversions)\n  def self.load_model(class_name, allow_child: false)\n    model = class_name.safe_constantize\n    raise Error, \"Could not find class: #{class_name}\" unless model\n    if allow_child\n      unless model.respond_to?(:searchkick_klass)\n        raise Error, \"#{class_name} is not a searchkick model\"\n      end\n    else\n      unless Searchkick.models.include?(model)\n        raise Error, \"#{class_name} is not a searchkick model\"\n      end\n    end\n    model\n  end\n\n  # private\n  def self.indexer\n    Thread.current[:searchkick_indexer] ||= Indexer.new\n  end\n\n  # private\n  def self.callbacks_value\n    Thread.current[:searchkick_callbacks_enabled]\n  end","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/ankane/searchkick/blob/93e901a75b11a25101668a616e006b158251b16e/lib/searchkick.rb#L289-L325","documentation":"For exact kNN on Elasticsearch, Searchkick maps the distance metric to a Painless script function (cosineSimilarity, l2norm, l1norm, dotProduct). Only 'cosine', 'euclidean', 'taxicab' and 'inner_product' are supported on this branch; any other value - including 'chebyshev', which only the OpenSearch branch supports - raises ArgumentError 'Unknown distance: ...'.","triggerScenarios":"Product.search('*', knn: {field: :embedding, vector: vec, distance: 'chebyshev', exact: true}) on Elasticsearch; values like 'dot_product', 'l2' or 'Cosine'.","commonSituations":"Sharing query code between OpenSearch and Elasticsearch deployments; porting metric names from Faiss or sentence-transformers examples; typos and casing mistakes.","solutions":["Use 'cosine', 'euclidean', 'taxicab' or 'inner_product'","For chebyshev on Elasticsearch, compute it via where: {_script: Searchkick::Script.new(...)} or switch the exact query to OpenSearch","Verify exact lowercase spelling of the metric string"],"exampleFix":"# before\nProduct.search('*', knn: {field: :v, vector: vec, distance: 'chebyshev', exact: true})\n# => ArgumentError: Unknown distance: chebyshev (Elasticsearch branch)\n\n# after\nProduct.search('*', knn: {field: :v, vector: vec, distance: 'cosine', exact: true})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"ES_EXACT_DISTANCES = %w[cosine euclidean taxicab inner_product].freeze\n\ndef valid_es_exact_distance?(value)\n  ES_EXACT_DISTANCES.include?(value)\nend\n\nraise ArgumentError, \"bad distance: #{d}\" unless valid_es_exact_distance?(d)\nProduct.search('*', knn: {field: :v, vector: vec, distance: d, exact: true})","tryCatchPattern":"begin\n  Product.search('*', knn: opts)\nrescue ArgumentError => e\n  raise unless e.message.start_with?('Unknown distance')\n  opts[:distance] = 'cosine'\n  Product.search('*', knn: opts)\nend","preventionTips":["Validate metric names against a per-server constant (Elasticsearch has no chebyshev)","Map external metric vocabularies to Searchkick names in one adapter","Test knn queries on both server flavors if you deploy both"],"tags":["searchkick","elasticsearch","knn","distance-metric","argumenterror"],"backgroundTag":"invalid-enum-value","analyzedSha":"93e901a75b11a25101668a616e006b158251b16e","analyzedAt":"2026-08-21T19:06:23.767Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}