← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 19:05:14 2015
Reported on Fri Jul 31 19:08:11 2015

Filename/var/www/foswiki11/lib/Foswiki/Query/HoistREs.pm
StatementsExecuted 1538 statements in 4.37ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
4011912µs1.69msFoswiki::Query::HoistREs::::_hoistEQFoswiki::Query::HoistREs::_hoistEQ
4111709µs3.02msFoswiki::Query::HoistREs::::collatedHoistFoswiki::Query::HoistREs::collatedHoist
4011532µs532µsFoswiki::Query::HoistREs::::_hoistDOTFoswiki::Query::HoistREs::_hoistDOT
4111328µs2.31msFoswiki::Query::HoistREs::::hoistFoswiki::Query::HoistREs::hoist
4011288µs1.98msFoswiki::Query::HoistREs::::_hoistORFoswiki::Query::HoistREs::_hoistOR
8021250µs250µsFoswiki::Query::HoistREs::::_hoistConstantFoswiki::Query::HoistREs::_hoistConstant
11115µs27µsFoswiki::Query::HoistREs::::BEGIN@17Foswiki::Query::HoistREs::BEGIN@17
1119µs14µsFoswiki::Query::HoistREs::::BEGIN@18Foswiki::Query::HoistREs::BEGIN@18
1119µs37µsFoswiki::Query::HoistREs::::BEGIN@33Foswiki::Query::HoistREs::BEGIN@33
1114µs4µsFoswiki::Query::HoistREs::::BEGIN@20Foswiki::Query::HoistREs::BEGIN@20
1113µs3µsFoswiki::Query::HoistREs::::BEGIN@21Foswiki::Query::HoistREs::BEGIN@21
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::Query::HoistREs
6
7Static functions to extract regular expressions from queries. The REs can
8be used in caching stores that use the Foswiki standard inline meta-data
9representation to pre-filter topic lists for more efficient query matching.
10
11See =Store/QueryAlgorithms/BruteForce.pm= for an example of usage.
12
13=cut
14
15package Foswiki::Query::HoistREs;
16
17226µs240µs
# spent 27µs (15+12) within Foswiki::Query::HoistREs::BEGIN@17 which was called: # once (15µs+12µs) by Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 at line 17
use strict;
# spent 27µs making 1 call to Foswiki::Query::HoistREs::BEGIN@17 # spent 12µs making 1 call to strict::import
18226µs219µs
# spent 14µs (9+5) within Foswiki::Query::HoistREs::BEGIN@18 which was called: # once (9µs+5µs) by Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 at line 18
use warnings;
# spent 14µs making 1 call to Foswiki::Query::HoistREs::BEGIN@18 # spent 5µs making 1 call to warnings::import
19
20218µs14µs
# spent 4µs within Foswiki::Query::HoistREs::BEGIN@20 which was called: # once (4µs+0s) by Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 at line 20
use Foswiki::Infix::Node ();
# spent 4µs making 1 call to Foswiki::Query::HoistREs::BEGIN@20
21231µs13µs
# spent 3µs within Foswiki::Query::HoistREs::BEGIN@21 which was called: # once (3µs+0s) by Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 at line 21
use Foswiki::Query::Node ();
# spent 3µs making 1 call to Foswiki::Query::HoistREs::BEGIN@21
22
23# Try to optimise a query by hoisting regular expression searches
24# out of the query
25#
26# patterns we need to look for:
27#
28# top level is defined by a sequence of AND and OR conjunctions
29# second level, = and ~ and =~
30# second level LHS is a field access
31# second level RHS is a static string or number
32
3321.30ms266µs
# spent 37µs (9+29) within Foswiki::Query::HoistREs::BEGIN@33 which was called: # once (9µs+29µs) by Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 at line 33
use constant MONITOR_HOIST => 0;
# spent 37µs making 1 call to Foswiki::Query::HoistREs::BEGIN@33 # spent 29µs making 1 call to constant::import
34
35=begin TML
36
37---++ ObjectMethod collatedHoist($query) -> $hasRef
38
39retuns a hashRef where the keys are the node's (web|name|text)
40for which we have hoisted regex's
41and who's values are a list of regex's
42
43and also keys of "(web|name|text)_source" where the list contains
44the non-regex version (ie what the user entered)
45
46=cut
47
48
# spent 3.02ms (709µs+2.31) within Foswiki::Query::HoistREs::collatedHoist which was called 41 times, avg 74µs/call: # 41 times (709µs+2.31ms) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 140 of /var/www/foswiki11/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 74µs/call
sub collatedHoist {
494117µs my $node = shift;
50
514114µs my %collation;
52
5341152µs412.31ms my @ops = hoist($node);
# spent 2.31ms making 41 calls to Foswiki::Query::HoistREs::hoist, avg 56µs/call
544168µs foreach my $op (@ops) {
5540105µs push( @{ $collation{ $op->{node} } }, $op->{regex} );
5640113µs push( @{ $collation{ $op->{node} . '_source' } }, $op->{source} );
57 }
58
59 #use Data::Dumper;
60 #print STDERR "--- hoisted: ".Dumper(%collation)."\n" if MONITOR_HOIST;
61
6241175µs return \%collation;
63}
64
65=begin TML
66
67---++ ObjectMethod hoist($query) -> @hashRefs
68
69Extract useful filter REs from the given query. The list returned is a list
70of filter expressions that can be used with a cache search to refine the
71list of topics. The full query should still be applied to topics that remain
72after the filter match has been applied; this is purely an optimisation.
73
74each hash in the array contains the node the regex is for, and a regex string
75
76{
77 node => 'web|name|text',
78 regex => 'Web.*'
79 source => 'Web*'
80}
81
82=cut
83
84
# spent 2.31ms (328µs+1.98) within Foswiki::Query::HoistREs::hoist which was called 41 times, avg 56µs/call: # 41 times (328µs+1.98ms) by Foswiki::Query::HoistREs::collatedHoist at line 53, avg 56µs/call
sub hoist {
854116µs my $node = shift;
86
874148µs return () unless ref( $node->{op} );
88
894036µs if ( $node->{op}->{name} eq '(' ) {
90 return hoist( $node->{params}[0] );
91 }
92
93 print STDERR "hoist ", $node->stringify(), "\n" if MONITOR_HOIST;
944043µs if ( $node->{op}->{name} eq 'and' ) {
95 my @lhs = hoist( $node->{params}[0] );
96 my $rhs = _hoistOR( $node->{params}[1] );
97 if ( scalar(@lhs) && $rhs ) {
98 return ( @lhs, $rhs );
99 }
100 elsif ( scalar(@lhs) ) {
101 return @lhs;
102 }
103 elsif ($rhs) {
104 return ($rhs);
105 }
106 }
107 else {
1084094µs401.98ms my $or = _hoistOR($node);
# spent 1.98ms making 40 calls to Foswiki::Query::HoistREs::_hoistOR, avg 50µs/call
10940105µs return ($or) if $or;
110 }
111
112 print STDERR "\tFAILED\n" if MONITOR_HOIST;
113 return ();
114}
115
116# depth 1; we can handle a sequence of ORs
117
# spent 1.98ms (288µs+1.69) within Foswiki::Query::HoistREs::_hoistOR which was called 40 times, avg 50µs/call: # 40 times (288µs+1.69ms) by Foswiki::Query::HoistREs::hoist at line 108, avg 50µs/call
sub _hoistOR {
1184010µs my $node = shift;
119
1204033µs return unless ref( $node->{op} );
121
1224026µs if ( $node->{op}->{name} eq '(' ) {
123 return _hoistOR( $node->{params}[0] );
124 }
125
1264035µs if ( $node->{op}->{name} eq 'or' ) {
127 print STDERR "hoistOR ", $node->stringify(), "\n" if MONITOR_HOIST;
128 my $lhs = _hoistOR( $node->{params}[0] );
129 my $rhs = _hoistEQ( $node->{params}[1] );
130 if ( $lhs && $rhs ) {
131 if ( $lhs->{node} eq $rhs->{node} ) {
132 return {
133 node => $lhs->{node},
134 regex => $lhs->{regex} . '|' . $rhs->{regex},
135 source => $lhs->{source} . ',' . $rhs->{source}
136 };
137 }
138 return ( $lhs, $rhs );
139 }
140 }
141 else {
14240183µs401.69ms return _hoistEQ($node);
# spent 1.69ms making 40 calls to Foswiki::Query::HoistREs::_hoistEQ, avg 42µs/call
143 }
144
145 print STDERR "\tFAILED\n" if MONITOR_HOIST;
146 return;
147}
148
149# depth 2: can handle = and ~ expressions
150
# spent 1.69ms (912µs+781µs) within Foswiki::Query::HoistREs::_hoistEQ which was called 40 times, avg 42µs/call: # 40 times (912µs+781µs) by Foswiki::Query::HoistREs::_hoistOR at line 142, avg 42µs/call
sub _hoistEQ {
1514010µs my $node = shift;
152
1534036µs return unless ref( $node->{op} );
154
1554028µs if ( $node->{op}->{name} eq '(' ) {
156 return _hoistEQ( $node->{params}[0] );
157 }
158
159 print STDERR "hoistEQ ", $node->stringify(), "\n" if MONITOR_HOIST;
160
161 # \000RHS\001 is a placholder for the RHS term
1624036µs if ( $node->{op}->{name} eq '=' ) {
16340146µs40532µs my $lhs = _hoistDOT( $node->{params}[0] );
# spent 532µs making 40 calls to Foswiki::Query::HoistREs::_hoistDOT, avg 13µs/call
16440121µs40179µs my $rhs = _hoistConstant( $node->{params}[1] );
# spent 179µs making 40 calls to Foswiki::Query::HoistREs::_hoistConstant, avg 4µs/call
1654019µs if ( $lhs && defined $rhs ) {
1664036µs $rhs = quotemeta($rhs);
16740180µs $lhs->{regex} =~ s/\000RHS\001/$rhs/g;
16840112µs4070µs $lhs->{source} = _hoistConstant( $node->{params}[1] );
# spent 70µs making 40 calls to Foswiki::Query::HoistREs::_hoistConstant, avg 2µs/call
16940110µs return $lhs;
170 }
171
172 # = is symmetric, so try the other order
173 $lhs = _hoistDOT( $node->{params}[1] );
174 $rhs = _hoistConstant( $node->{params}[0] );
175 if ( $lhs && defined $rhs ) {
176 $rhs = quotemeta($rhs);
177 $lhs->{regex} =~ s/\000RHS\001/$rhs/g;
178 $lhs->{source} = _hoistConstant( $node->{params}[0] );
179 return $lhs;
180 }
181 }
182 elsif ( $node->{op}->{name} eq '~' ) {
183 my $lhs = _hoistDOT( $node->{params}[0] );
184 my $rhs = _hoistConstant( $node->{params}[1] );
185 if ( $lhs && defined $rhs ) {
186 $rhs = quotemeta($rhs);
187 $rhs =~ s/\\\?/./g;
188 $rhs =~ s/\\\*/.*/g;
189 $lhs->{regex} =~ s/\000RHS\001/$rhs/g;
190 $lhs->{source} = _hoistConstant( $node->{params}[1] );
191 return $lhs;
192 }
193 }
194 elsif ( $node->{op}->{name} eq '=~' ) {
195 my $lhs = _hoistDOT( $node->{params}[0] );
196 my $rhs = _hoistConstant( $node->{params}[1] );
197 if ( $lhs && defined $rhs ) {
198
199#need to detect if its a field, or in a text, and if its a field, remove the ^$ chars...
200#or if there are no ^$, add .*'s if they are not present
201 if ( $lhs->{regex} ne "\000RHS\001" ) {
202 if ( ( not( $rhs =~ /^\^/ ) )
203 and ( not( $rhs =~ /^\.\*/ ) ) )
204 {
205 $rhs = '.*' . $rhs;
206 }
207
208 if ( ( not( $rhs =~ /\$$/ ) )
209 and ( not( $rhs =~ /\.\*$/ ) ) )
210 {
211 $rhs = $rhs . '.*';
212 }
213
214 #if we're embedding the regex into another, then remove the ^'s
215 $rhs =~ s/^\^//;
216 $rhs =~ s/\$$//;
217 }
218 $lhs->{regex} =~ s/\000RHS\001/$rhs/g;
219 $lhs->{source} = _hoistConstant( $node->{params}[1] );
220 return $lhs;
221 }
222 }
223
224 print STDERR "\tFAILED\n" if MONITOR_HOIST;
225 return;
226}
227
228# Expecting a (root level) field access expression. This must be of the form
229# <name>
230# or
231# <rootfield>.<name>
232# <rootfield> may be aliased
233
# spent 532µs within Foswiki::Query::HoistREs::_hoistDOT which was called 40 times, avg 13µs/call: # 40 times (532µs+0s) by Foswiki::Query::HoistREs::_hoistEQ at line 163, avg 13µs/call
sub _hoistDOT {
2344019µs my $node = shift;
235
2364039µs if ( ref( $node->{op} ) && $node->{op}->{name} eq '(' ) {
237 return _hoistDOT( $node->{params}[0] );
238 }
239
240 print STDERR "hoistDOT ", $node->stringify(), "\n" if MONITOR_HOIST;
24140101µs if ( ref( $node->{op} ) && $node->{op}->{name} eq '.' ) {
242 my $lhs = $node->{params}[0];
243 my $rhs = $node->{params}[1];
244 if ( !ref( $lhs->{op} )
245 && !ref( $rhs->{op} )
246 && $lhs->{op} eq $Foswiki::Infix::Node::NAME
247 && $rhs->{op} eq $Foswiki::Infix::Node::NAME )
248 {
249 $lhs = $lhs->{params}[0];
250 $rhs = $rhs->{params}[0];
251 if ( $Foswiki::Query::Node::aliases{$lhs} ) {
252 $lhs = $Foswiki::Query::Node::aliases{$lhs};
253 }
254 if ( $lhs =~ /^META:/ ) {
255
256 # \000RHS\001 is a placholder for the RHS term
257 return {
258 node => 'text',
259 regex => '^%'
260 . $lhs
261 . '{.*\\b'
262 . $rhs
263 . "=\\\"\000RHS\001\\\""
264 };
265 }
266
267 # Otherwise assume the term before the dot is the form name
268 if ( $rhs eq 'text' ) {
269
270 # Special case for the text body
271 return { node => 'text', regex => "\000RHS\001" };
272 }
273 else {
274 return {
275 node => 'text',
276 regex =>
277"^%META:FIELD{name=\\\"$rhs\\\".*\\bvalue=\\\"\000RHS\001\\\""
278 };
279 }
280
281 }
282 }
283 elsif ( !ref( $node->{op} ) && $node->{op} eq $Foswiki::Infix::Node::NAME )
284 {
2854062µs if ( $node->{params}[0] eq 'name' ) {
286
287 # Special case for the topic name
288 return { node => 'name', regex => "\000RHS\001" };
289 return;
290 }
291 elsif ( $node->{params}[0] eq 'web' ) {
292
293 # Special case for the web name
294 return { node => 'web', regex => "\000RHS\001" };
295 return;
296 }
297 elsif ( $node->{params}[0] eq 'text' ) {
298
299 # Special case for the text body
300 return { node => 'text', regex => "\000RHS\001" };
301 }
302 else {
303 return {
30440324µs node => 'text',
305 regex =>
306"^%META:FIELD{name=\\\"$node->{params}[0]\\\".*\\bvalue=\\\"\0RHS\1\\\""
307 };
308 }
309 }
310
311 print STDERR "\tFAILED\n" if MONITOR_HOIST;
312 return;
313}
314
315# Expecting a constant
316
# spent 250µs within Foswiki::Query::HoistREs::_hoistConstant which was called 80 times, avg 3µs/call: # 40 times (179µs+0s) by Foswiki::Query::HoistREs::_hoistEQ at line 164, avg 4µs/call # 40 times (70µs+0s) by Foswiki::Query::HoistREs::_hoistEQ at line 168, avg 2µs/call
sub _hoistConstant {
3178023µs my $node = shift;
318
319 print STDERR "hoistCONST ", $node->stringify(), "\n" if MONITOR_HOIST;
32080293µs if (
321 !ref( $node->{op} )
322 && ( $node->{op} eq $Foswiki::Infix::Node::STRING
323 || $node->{op} eq $Foswiki::Infix::Node::NUMBER )
324 )
325 {
326 return $node->{params}[0];
327 }
328 return;
329}
330
33112µs1;
332__END__