{"record":{"id":"3aca82c80c7a267e","repo":"ankane/searchkick","slug":"requires-non-oss-version-of-elasticsearch","errorCode":null,"errorMessage":"Requires non-OSS version of Elasticsearch","messagePattern":"Requires non-OSS version of Elasticsearch","errorType":"exception","errorClass":"Searchkick::Error","httpStatus":null,"severity":"error","filePath":"lib/searchkick/index.rb","lineNumber":198,"sourceCode":"      RecordData.new(self, record).document_type\n    end\n\n    def similar_record(record, **options)\n      options[:per_page] ||= 10\n      options[:similar] = [RecordData.new(self, record).record_data]\n      options[:models] ||= [record.class] unless options.key?(:model)\n\n      Searchkick.search(\"*\", **options)\n    end\n\n    def reload_synonyms\n      if Searchkick.opensearch?\n        client.transport.perform_request \"POST\", \"_plugins/_refresh_search_analyzers/#{CGI.escape(name)}\"\n      else\n        begin\n          client.transport.perform_request(\"GET\", \"#{CGI.escape(name)}/_reload_search_analyzers\")\n        rescue => e\n          raise Error, \"Requires non-OSS version of Elasticsearch\" if Searchkick.not_allowed_error?(e)\n          raise e\n        end\n      end\n    end\n\n    # queue\n\n    def reindex_queue\n      ReindexQueue.new(name)\n    end\n\n    # reindex\n\n    # note: this is designed to be used internally\n    # so it does not check object matches index class\n    def reindex(object, method_name: nil, ignore_missing: nil, full: false, **options)\n      if @options[:job_options]\n        options[:job_options] = (@options[:job_options] || {}).merge(options[:job_options] || {})","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/ankane/searchkick/blob/93e901a75b11a25101668a616e006b158251b16e/lib/searchkick/index.rb#L180-L216","documentation":"The exists where-operator accepts only true or false, mapping to an exists filter or a must_not-exists filter respectively. Any other value raises ArgumentError because Elasticsearch has no meaningful query for exists: nil, exists: 'true' or exists: 1.","triggerScenarios":"Product.search('*', where: {description: {exists: nil}}); where: {email: {exists: 'true'}} or {exists: 1} when filters are built from unconverted request params.","commonSituations":"Building filters from query strings where booleans arrive as 'true'/'false' strings or nil; JSON payloads using 0/1 flags; assuming nil works like false (use exists: false for missing fields).","solutions":["Pass real booleans: where: {description: {exists: true}}","Coerce params first: ActiveModel::Type::Boolean.new.cast(params[:has_desc]) or params == 'true'","To match missing values use exists: false rather than exists: nil"],"exampleFix":"# before\nProduct.search('*', where: {description: {exists: params[:has_desc]}}) # params value can be nil/'true'\n# => ArgumentError: Passing a value other than true or false to exists is not supported\n\n# after\nProduct.search('*', where: {description: {exists: ActiveModel::Type::Boolean.new.cast(params[:has_desc])}})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"BOOL = ->(v) { v.nil? ? nil : ActiveModel::Type::Boolean.new.cast(v) }\n\ndef exists_filter(v)\n  bool = BOOL.call(v)\n  raise ArgumentError, 'exists must be true/false' unless bool == true || bool == false\n  {exists: bool}\nend\n\nProduct.search('*', where: {description: exists_filter(params[:has_desc])})","tryCatchPattern":null,"preventionTips":["Coerce all incoming filter params through ActiveModel::Type::Boolean before building where hashes","Reject or default nil rather than passing it through","Keep a sanitizer between request params and the where option"],"tags":["searchkick","where","exists-filter","argumenterror","boolean-coercion"],"backgroundTag":"invalid-argument-value","analyzedSha":"93e901a75b11a25101668a616e006b158251b16e","analyzedAt":"2026-08-21T19:06:23.767Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}