Class: ProductFilter
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ProductFilter
- Includes:
- Models::Auditable
- Defined in:
- app/models/product_filter.rb
Overview
== Schema Information
Table name: product_filters
Database name: primary
id :integer not null, primary key
apply_discount :boolean
cached_item_ids :integer is an Array
exclude_item_ids :integer default([]), not null, is an Array
exclude_product_category_ids :integer default([]), not null, is an Array
exclude_product_line_ids :integer default([]), not null, is an Array
item_ids :integer default([]), is an Array
max_lin_ft :integer
max_msrp_amount :decimal(8, 2)
max_qty :integer
max_sq_ft :integer
min_lin_ft :integer
min_msrp_amount :decimal(8, 2)
min_qty :integer default(1), not null
min_sq_ft :integer
product_category_ids :integer default([]), is an Array
product_line_ids :integer default([]), is an Array
resource_type :string
created_at :datetime
updated_at :datetime
assortment_instruction_id :integer
creator_id :integer
resource_id :integer
updater_id :integer
Indexes
idx_assortment_instruction_id (assortment_instruction_id)
idx_resource_id_resource_type (resource_id,resource_type)
Foreign Keys
fk_rails_... (assortment_instruction_id => assortment_instructions.id)
Defined Under Namespace
Classes: LineExtractor, LineQualifier
Constant Summary
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary collapse
-
#criteria_met ⇒ Object
Returns the value of attribute criteria_met.
- #max_lin_ft ⇒ Object readonly
- #max_msrp_amount ⇒ Object readonly
- #max_qty ⇒ Object readonly
- #max_sq_ft ⇒ Object readonly
- #min_lin_ft ⇒ Object readonly
- #min_msrp_amount ⇒ Object readonly
- #min_qty ⇒ Object readonly
- #min_sq_ft ⇒ Object readonly
Belongs to collapse
Methods included from Models::Auditable
Class Method Summary collapse
-
.presence_checking ⇒ ActiveRecord::Relation<ProductFilter>
A relation of ProductFilters that are presence checking.
Instance Method Summary collapse
-
#any_items_match?(candidate_ids) ⇒ Boolean
Check if any of the given candidate item IDs match this filter's criteria.
-
#applicable_item_ids_set ⇒ Set<Integer>
Returns a Set of applicable item IDs for fast O(1) lookup during batch operations.
-
#applicable_items_scope ⇒ ActiveRecord::Relation
Returns an ActiveRecord relation of applicable items using real-time ltree queries.
- #deep_dup ⇒ Object
- #effective_item_skus ⇒ Object
- #effective_items ⇒ Object
- #exclude_items ⇒ Object
- #exclude_product_categories ⇒ Object
- #exclude_product_lines ⇒ Object
- #has_product_conditions? ⇒ Boolean
- #items ⇒ Object
-
#matching_item_ids(candidate_ids) ⇒ Set<Integer>
Returns which of the given candidate item IDs match this filter's criteria.
-
#meet_conditions?(item, applicable_ids: nil) ⇒ Boolean
Check if an item matches the product filter criteria.
- #product_categories ⇒ Object
- #product_lines ⇒ Object
-
#reset_applicable_item_ids_set! ⇒ Object
Clear the memoized applicable_item_ids_set (call when filter criteria change).
- #to_s ⇒ Object
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#criteria_met ⇒ Object
Returns the value of attribute criteria_met.
44 45 46 |
# File 'app/models/product_filter.rb', line 44 def criteria_met @criteria_met end |
#max_lin_ft ⇒ Object (readonly)
59 |
# File 'app/models/product_filter.rb', line 59 validates :max_lin_ft, numericality: { greater_than_or_equal_to: :min_lin_ft, if: -> { max_lin_ft.present? } } |
#max_msrp_amount ⇒ Object (readonly)
61 |
# File 'app/models/product_filter.rb', line 61 validates :max_msrp_amount, numericality: { greater_than_or_equal_to: :min_msrp_amount, if: -> { max_msrp_amount.present? } } |
#max_qty ⇒ Object (readonly)
55 |
# File 'app/models/product_filter.rb', line 55 validates :max_qty, numericality: { greater_than_or_equal_to: :min_qty, if: -> { max_qty.present? } } |
#max_sq_ft ⇒ Object (readonly)
57 |
# File 'app/models/product_filter.rb', line 57 validates :max_sq_ft, numericality: { greater_than_or_equal_to: :min_sq_ft, if: -> { max_sq_ft.present? } } |
#min_lin_ft ⇒ Object (readonly)
58 |
# File 'app/models/product_filter.rb', line 58 validates :min_lin_ft, numericality: { greater_than_or_equal_to: 1, if: -> { min_lin_ft.present? } } |
#min_msrp_amount ⇒ Object (readonly)
60 |
# File 'app/models/product_filter.rb', line 60 validates :min_msrp_amount, numericality: { greater_than_or_equal_to: 1, if: -> { min_msrp_amount.present? } } |
#min_qty ⇒ Object (readonly)
53 |
# File 'app/models/product_filter.rb', line 53 validates :min_qty, presence: true |
#min_sq_ft ⇒ Object (readonly)
56 |
# File 'app/models/product_filter.rb', line 56 validates :min_sq_ft, numericality: { greater_than_or_equal_to: 1, if: -> { min_sq_ft.present? } } |
Class Method Details
.presence_checking ⇒ ActiveRecord::Relation<ProductFilter>
A relation of ProductFilters that are presence checking. Active Record Scope
64 |
# File 'app/models/product_filter.rb', line 64 scope :presence_checking, -> { where('product_filters.min_qty > 0 or product_filters.min_sq_ft > 0 or product_filters.min_lin_ft > 0 or product_filters.min_msrp_amount > 0') } |
Instance Method Details
#any_items_match?(candidate_ids) ⇒ Boolean
Check if any of the given candidate item IDs match this filter's criteria.
More efficient than matching_item_ids when you only need a boolean result.
158 159 160 161 162 |
# File 'app/models/product_filter.rb', line 158 def any_items_match?(candidate_ids) return false if candidate_ids.blank? applicable_items_scope.where(id: candidate_ids).exists? end |
#applicable_item_ids_set ⇒ Set<Integer>
Returns a Set of applicable item IDs for fast O(1) lookup during batch operations.
NOTE: Prefer matching_item_ids(candidate_ids) when checking a small set of items
(e.g., order line items) as it's much more efficient.
169 170 171 |
# File 'app/models/product_filter.rb', line 169 def applicable_item_ids_set @applicable_item_ids_set ||= applicable_items_scope.ids.to_set end |
#applicable_items_scope ⇒ ActiveRecord::Relation
Returns an ActiveRecord relation of applicable items using real-time ltree queries.
This is the preferred way to get applicable items as it doesn't require cache maintenance.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'app/models/product_filter.rb', line 102 def applicable_items_scope base_scope = Item.non_publications.active has_pl_or_pc = product_line_ids.present? || product_category_ids.present? # Build scope based on what filters are present scope = if has_pl_or_pc # Product line and/or category filters present if product_line_ids.present? && product_category_ids.present? base_scope.by_product_line_id(product_line_ids).by_product_category_id(product_category_ids) elsif product_line_ids.present? base_scope.by_product_line_id(product_line_ids) else base_scope.by_product_category_id(product_category_ids) end elsif item_ids.present? # Only specific item_ids, no product line/category filters base_scope.where(id: item_ids) else # No inclusion criteria - return none (exclusions only make sense with inclusions) Item.none end # Union with specifically listed items (when we have PL/PC filters AND item_ids) scope = scope.or(base_scope.where(id: item_ids)) if has_pl_or_pc && item_ids.present? # Apply exclusions scope = scope.where.not(id: base_scope.by_product_line_id(exclude_product_line_ids).select(:id)) if exclude_product_line_ids.present? scope = scope.where.not(id: base_scope.by_product_category_id(exclude_product_category_ids).select(:id)) if exclude_product_category_ids.present? scope = scope.where.not(id: exclude_item_ids) if exclude_item_ids.present? scope end |
#assortment_instruction ⇒ AssortmentInstruction
47 |
# File 'app/models/product_filter.rb', line 47 belongs_to :assortment_instruction, inverse_of: :product_filters, optional: true |
#deep_dup ⇒ Object
66 67 68 |
# File 'app/models/product_filter.rb', line 66 def deep_dup deep_clone end |
#effective_item_skus ⇒ Object
78 79 80 |
# File 'app/models/product_filter.rb', line 78 def effective_item_skus effective_items.pluck(:sku) end |
#effective_items ⇒ Object
74 75 76 |
# File 'app/models/product_filter.rb', line 74 def effective_items applicable_items_scope.order(:sku) end |
#exclude_items ⇒ Object
190 191 192 |
# File 'app/models/product_filter.rb', line 190 def exclude_items Item.where(id: exclude_item_ids) end |
#exclude_product_categories ⇒ Object
198 199 200 |
# File 'app/models/product_filter.rb', line 198 def exclude_product_categories ProductCategory.where(id: exclude_product_category_ids) end |
#exclude_product_lines ⇒ Object
194 195 196 |
# File 'app/models/product_filter.rb', line 194 def exclude_product_lines ProductLine.where(id: exclude_product_line_ids) end |
#has_product_conditions? ⇒ Boolean
82 83 84 |
# File 'app/models/product_filter.rb', line 82 def has_product_conditions? [*item_ids, *product_line_ids, *product_category_ids, *exclude_item_ids, *exclude_product_line_ids, *exclude_product_category_ids].compact.present? end |
#items ⇒ Object
178 179 180 |
# File 'app/models/product_filter.rb', line 178 def items Item.where(id: item_ids) end |
#matching_item_ids(candidate_ids) ⇒ Set<Integer>
Returns which of the given candidate item IDs match this filter's criteria.
This is much more efficient than fetching ALL applicable items when you only
need to check a small set of items (e.g., items from an order).
Results are memoized per candidate set so the same filter evaluated against
the same line items (common during discount calculation across many coupons)
hits the DB only once instead of once per coupon.
145 146 147 148 149 150 151 |
# File 'app/models/product_filter.rb', line 145 def matching_item_ids(candidate_ids) return Set.new if candidate_ids.blank? cache_key = candidate_ids.sort @matching_item_ids_cache ||= {} @matching_item_ids_cache[cache_key] ||= applicable_items_scope.where(id: candidate_ids).ids.to_set end |
#meet_conditions?(item, applicable_ids: nil) ⇒ Boolean
Check if an item matches the product filter criteria.
Uses real-time ltree queries - no cache maintenance needed.
93 94 95 96 |
# File 'app/models/product_filter.rb', line 93 def meet_conditions?(item, applicable_ids: nil) ids = applicable_ids || applicable_item_ids_set ids.include?(item.id) end |
#product_categories ⇒ Object
186 187 188 |
# File 'app/models/product_filter.rb', line 186 def product_categories ProductCategory.where(id: product_category_ids) end |
#product_lines ⇒ Object
182 183 184 |
# File 'app/models/product_filter.rb', line 182 def product_lines ProductLine.where(id: product_line_ids) end |
#reset_applicable_item_ids_set! ⇒ Object
Clear the memoized applicable_item_ids_set (call when filter criteria change)
174 175 176 |
# File 'app/models/product_filter.rb', line 174 def reset_applicable_item_ids_set! @applicable_item_ids_set = nil end |
#resource ⇒ Resource
46 |
# File 'app/models/product_filter.rb', line 46 belongs_to :resource, polymorphic: true, optional: true |
#to_s ⇒ Object
70 71 72 |
# File 'app/models/product_filter.rb', line 70 def to_s "[ProductFilter:#{id}]" end |