Skip to content

Commit

Permalink
Add option to sort by identifier match order. Fix #4065.
Browse files Browse the repository at this point in the history
  • Loading branch information
mjy committed Sep 27, 2024
1 parent 31157e0 commit 3f6a928
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 26 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv

### Added

- Sort by identifier match option [#4065]
- `/collection_objects/123/dwc_compact` - DwC fields for those populated [#3994]
- Pagination to `/api/v1/otus/:otu_id/inventory/dwc_gallery`

### Fixed

- Display of missing DwC fields [#4051]
- `verbatim_field_number` updates ignored [#4066]
- DwC importer `verbatim_field_number` collision with Identifier validation
- Shortcuts: Keys pressed are not removed after user switches windows/tab

[#4065]: https://github.com/SpeciesFileGroup/taxonworks/issues/4065
[#4051]: https://github.com/SpeciesFileGroup/taxonworks/issues/4051
[#4066]: https://github.com/SpeciesFileGroup/taxonworks/issues/4066
[#3994]: https://github.com/SpeciesFileGroup/taxonworks/issues/3994

Expand All @@ -32,7 +36,7 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv
### Changed

- Revert strict `verbatim_field_number` validation [#4061]
- Renamed CollectingEvent `verbatim_trip_code` to `verbatim_field_number` [#4058]
- Renamed CollectingEvent `verbatim_trip_identifier` to `verbatim_field_number` [#4058]

### Fixed

Expand Down Expand Up @@ -89,7 +93,7 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv
### Changed

- DwC export will now use a valid taxon name if linked first to an invalid, and it is available
- EventID and verbatim_trip_code are disentangled in DwC Importer, they do not map to one-another now [#3800] [#2852]
- EventID and verbatim_trip_identifier are disentangled in DwC Importer, they do not map to one-another now [#3800] [#2852]
- TripCode is now FieldNumber (all data migrated)
- DwcOccurrence rebuilds triggered for changes to TaxonNameRelationship [#4019], TypeMaterial, TaxonDetermination, Identifier::Global
- Wikidata IDs are now also loaded into recordedByID and identifiedByID [#3989]
Expand Down
18 changes: 10 additions & 8 deletions lib/queries/collection_object/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,11 @@ def determiner_facet

b = b.as('det_z1_')

::CollectionObject.joins(Arel::Nodes::InnerJoin.new(b,
Arel::Nodes::On.new( b['taxon_determination_object_id'].eq(tt['id']).and( b['taxon_determination_object_type'].eq('CollectionObject') )
)))
::CollectionObject.joins(Arel::Nodes::InnerJoin.new(
b,
Arel::Nodes::On.new(
b['taxon_determination_object_id'].eq(tt['id']).and( b['taxon_determination_object_type'].eq('CollectionObject'))
)))
end

def determiners_facet
Expand Down Expand Up @@ -903,8 +905,8 @@ def otu_query_facet
def dwc_occurrence_query_facet
return nil if dwc_occurrence_query.nil?

s = ::CollectionObject
.with(query_dwc_co: dwc_occurrence_query.all.select(:dwc_occurrence_object_id, :dwc_occurrence_object_type, :id))
s = ::CollectionObject
.with(query_dwc_co: dwc_occurrence_query.all.select(:dwc_occurrence_object_id, :dwc_occurrence_object_type, :id))
.joins(:dwc_occurrence)
.joins('JOIN query_dwc_co as query_dwc_co1 on query_dwc_co1.id = dwc_occurrences.id')
.to_sql
Expand Down Expand Up @@ -1049,6 +1051,6 @@ def merge_clauses
with_buffered_other_labels_facet,
]
end
end
end
end
end
end
end
4 changes: 2 additions & 2 deletions lib/queries/concerns/identifiers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ def match_identifiers_facet
when 'internal'
a = referenced_klass.where(id: ids)
when 'identifier'
a = referenced_klass.joins(:identifiers).where(identifiers: {cached: ids})
a = referenced_klass.joins(:identifiers).where(identifiers: {cached: ids}).distinct
when 'dwc_occurrence_id'
a = referenced_klass.joins(:identifiers).where(identifiers: {cached: ids, type: 'Identifier::Global::Uuid::TaxonworksDwcOccurrence' })
a = referenced_klass.joins(:identifiers).where(identifiers: {cached: ids, type: 'Identifier::Global::Uuid::TaxonworksDwcOccurrence' }).distinct
else
return nil
end
Expand Down
88 changes: 74 additions & 14 deletions lib/queries/query/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,18 @@ def self.inverted_subqueries
# ( A ( B ) C )
attr_accessor :venn_mode

def venn_mode
v = @venn_mode.to_s.downcase.to_sym
v = :ab if v.blank?
if [:a, :ab, :b].include?(v)
v
else
nil
end
end

def process_url_into_params(url)
Rack::Utils.parse_nested_query(url)
end
# @return symbol
# must match a existing parameter name (used to check if values provided)
#
# @param order_by [String, Symbol]
# the kind of sort to apply.
#
# Supported values:
# * `match_identifiers` - sort by the order of the identifiers in provided
#
# !! Does not sub-sort
#
attr_accessor :order_by

# @return Hash
# the parsed/permitted params
Expand All @@ -250,6 +249,7 @@ def initialize(query_params)

# Reference to query_params, i.e. always permitted
@api = boolean_param(query_params, :api)

@recent = boolean_param(query_params, :recent)
@recent_target = query_params[:recent_target]

Expand All @@ -268,6 +268,8 @@ def initialize(query_params)
@per = query_params[:per]
@page = query_params[:page]

@order_by = query_params[:order_by]

# After this point, if you started with ActionController::Parameters,
# then all values have been explicitly permitted.
if query_params.kind_of?(Hash)
Expand All @@ -285,6 +287,11 @@ def initialize(query_params)
set_user_dates(params)
end

def order_by
return nil if @order_by.blank?
@order_by.to_sym
end

def object_global_id
[@object_global_id].flatten.compact
end
Expand All @@ -300,6 +307,20 @@ def recent_target
r
end

def venn_mode
v = @venn_mode.to_s.downcase.to_sym
v = :ab if v.blank?
if [:a, :ab, :b].include?(v)
v
else
nil
end
end

def process_url_into_params(url)
Rack::Utils.parse_nested_query(url)
end

# @params [Parameters]
# @return [Filter, nil]
# the class of filter that is referenced at the base of this parameter set
Expand Down Expand Up @@ -707,16 +728,55 @@ def all(nil_empty = false)
q = apply_venn(q)
end

# TODO: collides with recent, and needs isolation/generic application
# probably through native .order() use.
# Order in general likely belongs outside the scope of filters, but
# see this param use, where we depend on the incoming values
#
# See spec/lib/queries/otu/filter_spec.rb for tests

if order_by
case order_by
when :match_identifiers
if !match_identifiers.blank?

case match_identifiers_type
when 'internal'
o = "array_position(ARRAY[#{identifiers_to_match.join(',')}], #{table.name}.id)"
q = q.order(Arel.sql(o))
else
# TODO: optimize, this was done hastily
o = "array_position(ARRAY[#{identifiers_to_match.collect{|i| "'#{i}'"}.join(',')}], sid.cached) s"

i = ::Identifier
.from('identifiers sid')
.where(sid: {cached: identifiers_to_match}) # This is required to de-duplicate for some reason ?!
.select("sid.*, #{o}")

q = q.with(sid: i)
.joins("JOIN sid on sid.identifier_object_id = #{table.name}.id AND sid.identifier_object_type = '#{referenced_klass.base_class.name}'")
.select("#{table.name}.*, sid.s")
.order('sid.s')
end
end
end
end

if recent
q = referenced_klass.from(q.all, table.name).order(recent_target => :desc)
end

if paginate
q = q.order(:id).page(page).per(per)
if order_by
q = q.page(page).per(per)
else
q = q.order(:id).page(page).per(per)
end
end

# TODO: canonically address whether or not to use `.distinct` at this point, we should be able to, however
# some incoming queries may have joins/group/etc. alone?! I.e. why can't we?

q
end

Expand Down
10 changes: 10 additions & 0 deletions spec/lib/queries/otu/filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
let!(:o2) { Otu.create!(taxon_name: s2) }
let!(:o3) { Otu.create!(name: 'none') }

# See lib/queries/query/filter.rb for application
specify 'order_by match_identifiers' do
ids = [ o3.id, o1.id, o2.id]

q.match_identifiers = ids.join(',')
q.order_by = 'match_identifiers'
q.match_identifiers_type = 'internal'
expect(q.all.pluck(:id)).to eq(ids)
end

specify 'with synonymy' do
q.otu_id = o1.id
q.coordinatify = true
Expand Down

0 comments on commit 3f6a928

Please sign in to comment.