From: Lebbeous Fogle-Weekley Date: Sat, 15 Dec 2012 00:13:54 +0000 (-0500) Subject: ML mostly done pre-testing, see 'TODO sprintf' though X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=df5270f2c394521ebec28bd5be41effe1572bc6c;p=evergreen%2Fequinox.git ML mostly done pre-testing, see 'TODO sprintf' though may need to optimize approx Signed-off-by: Lebbeous Fogle-Weekley --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm index 50844fea8e..49f0d64c0b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm @@ -17,6 +17,32 @@ use OpenILS::Application::Circ::CircCommon; use OpenILS::Application::AppUtils; my $U = "OpenILS::Application::AppUtils"; +# used in build_hold_sort_clause() +my %HOLD_SORT_ORDER_BY = ( + pprox => 'p.prox', + hprox => 'actor.org_unit_proximity(%d, h.request_lib)', + aprox => 'COALESCE(hm.proximity, p.prox)', + approx => 'action.hold_copy_calculated_proximity(h.id, %d, %d)', + priority => 'pgt.hold_priority', + cut => 'CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END', + depth => 'h.selection_depth', + rtime => 'h.request_time', + htime => q! + CASE WHEN + copy_has_not_been_home.result + THEN actor.org_unit_proximity(%d, h.request_lib) + ELSE 999 + END + !, + shtime => q! + CASE WHEN + copy_has_not_been_home_even_to_idle.result + THEN actor.org_unit_proximity(%d, h.request_lib) + ELSE 999 + END + !, +); + sub isTrue { my $v = shift; @@ -294,8 +320,8 @@ sub get_hold_sort_order { my $row = $dbh->selectrow_hashref( q! SELECT - cbho.pprox, cbho.hprox, cbho.aprox, cbho.priority, - cbho.cut, cbho.depth, cbho.htime, cbho.rtime + cbho.pprox, cbho.hprox, cbho.aprox, cbho.approx, cbho.priority, + cbho.cut, cbho.depth, cbho.htime, cbho.shtime, cbho.rtime FROM config.best_hold_order cbho WHERE id = ( SELECT oils_json_to_text(value)::INT @@ -309,26 +335,121 @@ sub get_hold_sort_order { # Return only the keys of our hash, sorted by value, # keys for null values omitted. - return grep { defined $row->{$_} } ( - sort {$row->{$a} cmp $row->{$b}} keys %$row - ); + return [ + grep { defined $row->{$_} } ( + sort {$row->{$a} cmp $row->{$b}} keys %$row + ) + ]; } +# Returns an ORDER BY clause *and* a string with a CTE expression to precede +# the nearest-hold SQL query sub build_hold_sort_clause { - my (@columns) = @_; - - my %HOLD_SORT_EXPRESSIONS = ( - pprox => 'p.prox', - hprox => '', - aprox => '', - priority => 'pgt.hold_priority', - cut => '', - depth => 'h.selection_depth', - rtime => 'h.request_time', - htime => '' + my ($columns, $cp, $here) = @_; + + my %sprintf_args = ( + hprox => [$cp->circ_lib], + approx => [$cp->id, $here->id], + htime => [$cp->circ_lib], + shtime => [$cp->circ_lib] ); + my @clauses; + my $ctes_needed = 0; + foreach my $col (@$columns) { + if ($col eq 'htime' and not $ctes_needed) { + $ctes_needed = 1; + } elsif ($col eq 'shtime') { + $ctes_needed = 2; + } + + my @args; + @args = @{$sprintf_args{$col}} if exists $sprintf_args{$col}; + + push @clauses, sprintf($HOLD_SORT_ORDER_BY{$col}, @args); + + last if $col eq 'rtime'; # rtime is effectively unique, no need for + # more order-by clauses after that. + } + my ($ctes, $joins); + if ($ctes_needed >= 1) { + # For our first auxiliary query, the question we seek to answer is, "has + # our copy been circulating away from home too long?" Two parts to + # answer this question. + # + # 1: Have their been no checkouts at the copy's circ_lib since the + # beginning of our go-home interval? + # 2: Was the last transit to affect our copy before the beginning + # of our go-home interval an outbound transit? i.e. away from circ-lib + + $ctes .= q! +, copy_has_not_been_home AS ( + SELECT ( + -- part 1 + SELECT circ.id FROM action.circulation circ + JOIN go_home_interval ON (true) + WHERE + circ.target_copy = %d AND -- $cp->id + circ.circ_lib = %d AND -- $cp->circ_lib + circ.xact_start >= NOW() - go_home_interval.value + ) IS NULL AND ( + -- part 2 + SELECT atc.dest <> $cp->circ_lib FROM action.transit_copy atc + JOIN go_home_interval ON (true) + WHERE + atc.id = ( + SELECT MAX(id) FROM action.transit_copy atc_inner + WHERE + atc_inner.target_copy = $cp AND + atc_inner.source_send_time < NOW() - go_home_interval.value + ) + ) AS result +) !; # TODO sprintf + $joins .= " JOIN copy_has_not_been_home ON (true) "; + } + if ($ctes_needed == 2) { + # In this auxiliary query, we ask the question, "has our copy come home + # by any means that we can determine, even if it didn't circulate once + # it came home, in the time defined by the go-home-interval?" + # answer this question. Two parts to this too (besides including the + # previous auxiliary query). + # + # 1: there have been no homebound transits for this copy since the + # beginning of the go-home interval. + # 2: there have been no checkins at home since the beginning of + # the go-home interval for this copy + + $ctes .= q! +, copy_has_not_been_home_even_to_idle AS ( + SELECT + copy_has_not_been_home.response AND ( + -- part 1 + SELECT atc.id FROM action.transit_copy atc + JOIN go_home_interval ON (true) + WHERE + atc.target_copy = %d AND -- $cp + atc.dest = %d AND -- $cp->circ_lib + atc.dest_recv_time >= NOW() - go_home_interval.value + ) IS NULL AND ( + -- part 2 + SELECT circ.id FROM action.circulation circ + JOIN go_home_interval ON (true) + WHERE + circ.target_copy = $cp AND + circ.checkin_lib = $cp->circ_lib AND + circ.checkin_time >= NOW() - go_home_interval.value + ) IS NULL + AS result +) !; # TODO sprintf + $joins .= " JOIN copy_has_not_been_home_even_to_idle ON (true) "; + } + + return ( + join(", ", @clauses), + $ctes, + $joins + ); } sub nearest_hold { @@ -340,18 +461,23 @@ sub nearest_hold { my $age = shift() || '0 seconds'; my $fifo = shift(); - if (isTrue($fifo)) { - $log->info("deprecated 'fifo' param true, but ignored"); - } + $log->info("deprecated 'fifo' param true, but ignored") if isTrue $fifo; -# my $holdsort = isTrue($fifo) ? -# "pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.request_time, h.selection_depth DESC, COALESCE(hm.proximity, h.prox) " : -# "COALESCE(hm.proximity, h.prox), pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.selection_depth DESC, h.request_time "; - my $holdsort = build_hold_sort_clause(get_hold_sort_order($here)); + my ($holdsort, $addl_cte, $addl_join) = build_hold_sort_clause( + get_hold_sort_order($here), $cp, $here + ); local $OpenILS::Application::Storage::WRITE = 1; my $ids = action::hold_request->db_Main->selectcol_arrayref(<<" SQL", {}, $here, $cp, $age); + WITH go_home_interval AS ( + SELECT OILS_JSON_TO_TEXT( + (SELECT value FROM actor.org_unit_ancestor_setting( + 'circ.hold_go_home_interval', ? + ) + ))::INTERVAL AS value + ) + $addl_cte SELECT h.id FROM action.hold_request h JOIN actor.org_unit_proximity p ON (p.from_org = ? AND p.to_org = h.pickup_lib) @@ -362,6 +488,7 @@ sub nearest_hold { ON ( au.id = ausp.usr AND ( ausp.stop_date IS NULL OR ausp.stop_date > NOW() ) ) LEFT JOIN config.standing_penalty csp ON ( csp.id = ausp.standing_penalty AND csp.block_list LIKE '%CAPTURE%' ) + $addl_join WHERE hm.target_copy = ? AND (AGE(NOW(),h.request_time) >= CAST(? AS INTERVAL) OR p.prox = 0) AND h.capture_time IS NULL