Find broader taxon if we don't have a specific one

For example, when importing `beef` products associate the type `meat-product`
instead because we don't have a specific beef category.
This commit is contained in:
Maikel Linke
2025-01-29 16:15:30 +11:00
parent 26d09acafe
commit c9f319aa96
2 changed files with 91 additions and 4 deletions

View File

@@ -1,11 +1,31 @@
# frozen_string_literal: true
class ProductTypeImporter < DfcBuilder
# Try to find the taxon closest matching the given product type.
# If we don't find any matching taxon, we return a random one.
def self.taxon(product_type)
dfc_id = product_type&.semanticId
priority_list = [product_type, *list_broaders(product_type)].compact
# Every product needs a primary taxon to be valid. So if we don't have
# one or can't find it we just take a random one.
Spree::Taxon.find_by(dfc_id:) || Spree::Taxon.first
# Optimistic querying.
# We could query all broader taxons in one but then we need to still sort
# them locally and use more memory. That would be a pessimistic query.
# Consider caching the result instead.
taxons = priority_list.lazy.map do |type|
Spree::Taxon.find_by(dfc_id: type.semanticId)
end.compact
taxons.first || Spree::Taxon.first
end
def self.list_broaders(type)
return [] if type.nil?
broaders = type.broaders.map do |id|
DataFoodConsortium::Connector::SKOSParser.concepts[id]
end
broaders + broaders.flat_map do |broader|
list_broaders(broader)
end
end
end

View File

@@ -0,0 +1,67 @@
# frozen_string_literal: true
require_relative "../spec_helper"
RSpec.describe ProductTypeImporter do
let(:drink) {
DfcLoader.connector.PRODUCT_TYPES.DRINK
}
let(:soft_drink) {
DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK
}
let(:lemonade) {
DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE
}
describe ".taxon" do
it "finds a linked taxon" do
create(:taxon, dfc_id: soft_drink.semanticId)
lemonade_taxon = create(:taxon, dfc_id: lemonade.semanticId)
expect(described_class.taxon(lemonade)).to eq lemonade_taxon
end
it "falls back to a broader taxon" do
drink_taxon = create(:taxon, dfc_id: drink.semanticId)
expect(described_class.taxon(lemonade)).to eq drink_taxon
end
it "returns random taxon when none can be found" do
only_taxon = create(:taxon)
expect(described_class.taxon(lemonade)).to eq only_taxon
end
it "queries the database only until it found a taxon" do
soft_drink_taxon = create(:taxon, dfc_id: soft_drink.semanticId)
expect {
expect(described_class.taxon(lemonade)).to eq soft_drink_taxon
}.to query_database [
"Spree::Taxon Load", # query for lemonade, not found
"Spree::Taxon Load", # query for soft drink, found
# no query for drink
]
end
end
describe ".list_broaders" do
it "returns an empty array if no type is given" do
list = described_class.list_broaders(nil)
expect(list).to eq []
end
it "can return an empty list for top concepts" do
list = described_class.list_broaders(drink)
expect(list).to eq []
end
it "lists the broader concepts of a type" do
list = described_class.list_broaders(soft_drink)
expect(list).to eq [drink]
end
it "lists all the broader concepts to the top concepts" do
list = described_class.list_broaders(lemonade)
expect(list).to eq [soft_drink, drink]
end
end
end