From d7febd194f081c26db639e634d807d72c7efc9d6 Mon Sep 17 00:00:00 2001 From: iamgeeeee Date: Mon, 23 Sep 2024 00:01:04 +1000 Subject: [PATCH 1/3] Fixup bug around search and search options --- README.md | 7 ++-- lib/flexi/json.rb | 6 ++-- lib/flexi/json/dataset.rb | 28 ++++++++++----- lib/flexi/json/searcher.rb | 4 +-- spec/flexi/json/dataset_spec.rb | 1 - spec/flexi/json_spec.rb | 64 +++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2881ddb..c2b4844 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,11 @@ flexi_json.find_duplicates("email,full_name") ## Advanced search ```ruby -options = {matched_all: true, exact_match: false} -flexi_json.search({first_name: "john", address: "sydney"}, options) +search_query = {first_name: "john", address: "sydney"} +flexi_json.search( + search_query, + options: {matched_all: true, exact_match: false} +) # => [#"John", :address=>"Sydney Australia"}, @name="John", @searchable_fields=["name", "address"]>] ``` diff --git a/lib/flexi/json.rb b/lib/flexi/json.rb index ea0d428..0ae3c4f 100644 --- a/lib/flexi/json.rb +++ b/lib/flexi/json.rb @@ -10,12 +10,10 @@ module Flexi::Json class << self attr_writer :configuration - # Access or initialize the configuration object def configuration @configuration ||= Flexi::Json::Configuration.instance end - # Configure block for setting custom configurations def configure yield(configuration) end @@ -26,8 +24,8 @@ def new(data) end end - def search(query = "", fields = nil) - @searcher.search(query, fields) + def search(query = "", fields = nil, options: nil) + @searcher.search(query, fields, options: options) end def find_duplicates(keys) diff --git a/lib/flexi/json/dataset.rb b/lib/flexi/json/dataset.rb index 8f3e761..b5566dc 100644 --- a/lib/flexi/json/dataset.rb +++ b/lib/flexi/json/dataset.rb @@ -12,20 +12,22 @@ def initialize(attributes = {}) end end - def matches?(query, fields = searchable_fields, options: Flexi::Json::Configuration.default_match_options) - validateable_fields = query.is_a?(Hash) ? query.keys.map(&:to_s) : fields - valid_fields = validateable_fields&.select { |field| searchable_fields.include?(field) } || searchable_fields + def matches?(query, fields = nil, options: nil) + options ||= Flexi::Json::Configuration.default_match_options + + valid_fields = validateable_fields(query, fields)&.select do |field| + searchable_fields.include?(field) + end || searchable_fields return false if valid_fields.empty? matching_method = options[:match_all] ? :all? : :any? valid_fields.public_send(matching_method) do |field| search_query = query.is_a?(Hash) ? query[field.to_sym] : query - if options[:exact_match] - attributes[field.to_sym].to_s.downcase == search_query.to_s.downcase - else - attributes[field.to_sym].to_s.downcase.include?(search_query.to_s.downcase) - end + attribute_value = attributes[field.to_sym].to_s.downcase + query_value = search_query.to_s.downcase + + options[:exact_match] ? attribute_value == query_value : attribute_value.include?(query_value) end end @@ -35,6 +37,16 @@ def searchable_fields private + def validateable_fields(query, fields) + if query.is_a?(Hash) + query.keys.map(&:to_s) + elsif fields.is_a?(String) + fields.delete(" ").split(",") + elsif fields.is_a?(Array) + [fields || searchable_fields].flatten + end + end + # Method to validate that a key is a valid method name and not dangerous def valid_key?(key) key.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) && !dangerous_method?(key) diff --git a/lib/flexi/json/searcher.rb b/lib/flexi/json/searcher.rb index cbe9d8f..8b5bd02 100644 --- a/lib/flexi/json/searcher.rb +++ b/lib/flexi/json/searcher.rb @@ -10,8 +10,8 @@ def initialize(data) @result = [] end - def search(query, fields = nil) - @result = @data.select { |data| data.matches?(query, fields) } + def search(query, fields = nil, options: nil) + @result = @data.select { |data| data.matches?(query, fields, options: options) } end def find_duplicates(keys) diff --git a/spec/flexi/json/dataset_spec.rb b/spec/flexi/json/dataset_spec.rb index 81c18af..7af3ba4 100644 --- a/spec/flexi/json/dataset_spec.rb +++ b/spec/flexi/json/dataset_spec.rb @@ -1,5 +1,4 @@ require "spec_helper" -require "debug" RSpec.describe Flexi::Json::Dataset do describe ".initialize" do diff --git a/spec/flexi/json_spec.rb b/spec/flexi/json_spec.rb index 3bb28b8..9c6f99d 100644 --- a/spec/flexi/json_spec.rb +++ b/spec/flexi/json_spec.rb @@ -34,4 +34,68 @@ end end end + + describe "#search" do + subject { described_class.new("./spec/data/dataset.json") } + + context "for query param" do + it "works for string type" do + expect(subject.search("john")).not_to eq [] + end + + it "works for array type" do + expect(subject.search(["john"])).to eq [] + end + end + + context "for fields param" do + it "works for string type" do + expect(subject.search("john", "full_name")).not_to eq [] + expect(subject.search("john", "full_name, email")).not_to eq [] + end + it "works for array type" do + expect(subject.search("john", ["full_name"])).not_to eq [] + expect(subject.search("john", ["full_name", "email"])).not_to eq [] + end + end + + context "for options params" do + it "works for match_all option" do + options = {match_all: true} + expect(subject.search("john", "full_name", options: options)).not_to eq [] + + query = {full_name: "john", email: "some_email@gmail.com"} + expect(subject.search(query, "full_name", options: options)).to eq [] + + query = {full_name: "john", email: "john.doe"} + expect(subject.search(query, options: options)).not_to eq [] + end + + it "works for exact_match option" do + options = {exact_match: true} + expect(subject.search("john", "full_name", options: options)).to eq [] + expect(subject.search("john doe", "full_name", options: options)).not_to eq [] + + query = {full_name: "john", email: "john.doe"} + expect(subject.search(query, "full_name", options: options)).to eq [] + end + + it "works for both options" do + options = {exact_match: true, match_all: true} + query = {full_name: "john", email: "some_email@gmail.com"} + expect(subject.search(query, options: options)).to eq [] + + query = {full_name: "john doe", email: "john.doe@gmail.com"} + expect(subject.search(query, options: options)).not_to eq [] + end + end + end + + describe "#find_duplicates" do + subject { described_class.new("./spec/data/dataset.json") } + it "works" do + expect(subject.find_duplicates("email").size).to eq 2 + expect(subject.find_duplicates("unknown_key").size).to eq 0 + end + end end From 08c500a371ba2a956bc3efc476ac2d50b8f35115 Mon Sep 17 00:00:00 2001 From: iamgeeeee Date: Mon, 23 Sep 2024 00:02:02 +1000 Subject: [PATCH 2/3] Bump version 0.4.2 --- Gemfile.lock | 2 +- lib/flexi/json/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0e246de..ec1e40c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - flexi-json (0.4.1) + flexi-json (0.4.2) GEM remote: https://rubygems.org/ diff --git a/lib/flexi/json/version.rb b/lib/flexi/json/version.rb index 973a02b..c2bf8fe 100644 --- a/lib/flexi/json/version.rb +++ b/lib/flexi/json/version.rb @@ -2,6 +2,6 @@ module Flexi module Json - VERSION = "0.4.1" + VERSION = "0.4.2" end end From 1e48f5e1106673a2ce325fbd966925e6753248a1 Mon Sep 17 00:00:00 2001 From: iamgeeeee Date: Mon, 23 Sep 2024 00:11:07 +1000 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69fd6ec..63a0d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version: 0.4.2 (23-09-2024) + +* [#25](https://github.com/GD-Personal/flexi-json/pull/25): A bug from [v0.4.0](https://github.com/GD-Personal/flexi-json/releases/tag/v0.4.0) was introduced where the advanced search function don't work properly when the options. + # Version: 0.4.1 (22-09-2024) * [0270c44](https://github.com/GD-Personal/flexi-json/commit/0270c44d7ffabd88ed0b69b09f1e98910257d99b): Update the readme and changelog