diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index 637646e03..75673e98e 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -555,8 +555,14 @@ def build_query(query, filters, should, must_not, custom_filters, multiply_filte bool[:must_not] = must_not if must_not.any? # exclude bool[:should] = should if should.any? # conversions - # nested query and does not contain match all - if nested && !bool.dig(:must, :match_all) + # nested query + if nested + # If query is nested ensure dis_max queries + # is available. This allows processing of JSON + # field as nested without adding to mappings + unless bool.dig(:must, :dis_max, :queries) + bool[:must] = { dis_max: { queries: [] } } + end bool.dig(:must, :dis_max, :queries) << nested_query(nested, filters) end @@ -857,6 +863,11 @@ def set_order(payload) def where_filters(where) filters = [] (where || {}).each do |field, value| + + # Ensure nested JSON field keys are symbols + # and not strings for processing below + value.try(:deep_symbolize_keys!) + field = :_id if field.to_s == "id" if field == :or @@ -869,8 +880,11 @@ def where_filters(where) filters << {bool: {must_not: where_filters(value)}} elsif field == :_and filters << {bool: {must: value.map { |or_statement| {bool: {filter: where_filters(or_statement)}} }}} - elsif field == :nested + elsif field.to_sym == :nested Array.wrap(value).each { |v| filters << nested_filters(v) } + elsif value.try(:keys).try(:include?, :nested) + # Handle nested field containing JSON data + Array.wrap(value).each { |v| filters << nested_filters(value[:nested]) } else # expand ranges if value.is_a?(Range) diff --git a/lib/searchkick/version.rb b/lib/searchkick/version.rb index 58a62d9cc..5dfa04002 100644 --- a/lib/searchkick/version.rb +++ b/lib/searchkick/version.rb @@ -1,3 +1,3 @@ module Searchkick - VERSION = "3.1.1" + VERSION = "3.1.1-everfi.1" end diff --git a/test/sql_test.rb b/test/sql_test.rb index 7b782a050..2f0aaf59a 100644 --- a/test/sql_test.rb +++ b/test/sql_test.rb @@ -409,6 +409,53 @@ def test_where_nested end + def test_json_field + store [ + {name: 'Amazon', nested_json: {foo: 'bar', nested_field: {name: 'test1'}}}, + {name: 'Costco', nested_json: {foo: 'boo', nested_field: {name: 'test2'}}}, + {name: 'Walmart', nested_json: {foo: 'boo', nested_field: {name: 'test3'}}} + ], Store + + # Flattened dot notation + result = Store.search "*", { where: { + 'nested_json.nested_field.name': 'test1' + } + } + + assert_equal result.results.first.name, 'Amazon' + + # Directly access nested JSON field + result = Store.search "*", { where: { + name: 'Amazon', + nested: { + path: 'nested_field', + where: { + name: 'test1' + } + } + } + } + + assert_equal result.results.first.name, 'Amazon' + + # Access JSON from field then nested mapping + result = Store.search "*", { where: { + name: 'Amazon', + nested_json: { + foo: 'bar', + nested: { + path: 'nested_field', + where: { + name: 'test1' + } + } + } + } + } + + assert_equal result.results.first.name, 'Amazon' + end + def test_nested_json store [ {name: 'Jim', reviews: [Review.create(name: 'Review A')]}, diff --git a/test/test_helper.rb b/test/test_helper.rb index 071002e22..9f9b2f9c5 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -383,6 +383,7 @@ class Tenant < ActiveRecord::Base ActiveRecord::Migration.create_table :stores do |t| t.string :name + t.json :nested_json end ActiveRecord::Migration.create_table :employees do |t| @@ -542,7 +543,12 @@ class Store mappings: { store: { properties: { - name: {type: "keyword"}, + nested_field: { + type: 'nested', + properties: { + name: {type: 'text'} + } + }, employees: { type: 'nested', properties: { @@ -595,6 +601,7 @@ def search_data } } } + data[:nested_field] = nested_json&.dig('nested_field') serializable_hash.except("id", "_id").merge( data )