Filename | /var/www/foswikidev/core/lib/Foswiki/Search/InfoCache.pm |
Statements | Executed 336138 statements in 1.03s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
50280 | 1 | 1 | 334ms | 334ms | __ANON__[:448] | Foswiki::Search::InfoCache::
8760 | 1 | 1 | 257ms | 618ms | addTopic | Foswiki::Search::InfoCache::
80 | 1 | 1 | 178ms | 178ms | sortTopics | Foswiki::Search::InfoCache::
40 | 1 | 1 | 79.1ms | 214ms | addTopics | Foswiki::Search::InfoCache::
80 | 2 | 2 | 3.21ms | 219ms | new | Foswiki::Search::InfoCache::
80 | 1 | 1 | 2.31ms | 181ms | sortResults | Foswiki::Search::InfoCache::
40 | 1 | 1 | 905µs | 1.71ms | getOptionFilter | Foswiki::Search::InfoCache::
40 | 1 | 1 | 803µs | 803µs | convertTopicPatternToRegex | Foswiki::Search::InfoCache::
40 | 1 | 1 | 758µs | 2.79s | getTopicListIterator | Foswiki::Search::InfoCache::
1 | 1 | 1 | 663µs | 750µs | BEGIN@33 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 13µs | 25µs | BEGIN@3 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 10µs | 34µs | BEGIN@30 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 8µs | 12µs | BEGIN@4 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 4µs | 4µs | BEGIN@9 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 4µs | 4µs | BEGIN@32 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 3µs | 3µs | BEGIN@31 | Foswiki::Search::InfoCache::
1 | 1 | 1 | 3µs | 3µs | BEGIN@6 | Foswiki::Search::InfoCache::
0 | 0 | 0 | 0s | 0s | __ANON__[:258] | Foswiki::Search::InfoCache::
0 | 0 | 0 | 0s | 0s | _compare | Foswiki::Search::InfoCache::
0 | 0 | 0 | 0s | 0s | filterByDate | Foswiki::Search::InfoCache::
0 | 0 | 0 | 0s | 0s | isImmutable | Foswiki::Search::InfoCache::
0 | 0 | 0 | 0s | 0s | numberOfTopics | Foswiki::Search::InfoCache::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | package Foswiki::Search::InfoCache; | ||||
3 | 2 | 26µs | 2 | 37µs | # spent 25µs (13+12) within Foswiki::Search::InfoCache::BEGIN@3 which was called:
# once (13µs+12µs) by Foswiki::Search::BEGIN@19 at line 3 # spent 25µs making 1 call to Foswiki::Search::InfoCache::BEGIN@3
# spent 12µs making 1 call to strict::import |
4 | 2 | 22µs | 2 | 16µs | # spent 12µs (8+4) within Foswiki::Search::InfoCache::BEGIN@4 which was called:
# once (8µs+4µs) by Foswiki::Search::BEGIN@19 at line 4 # spent 12µs making 1 call to Foswiki::Search::InfoCache::BEGIN@4
# spent 4µs making 1 call to warnings::import |
5 | |||||
6 | 2 | 59µs | 1 | 3µs | # spent 3µs within Foswiki::Search::InfoCache::BEGIN@6 which was called:
# once (3µs+0s) by Foswiki::Search::BEGIN@19 at line 6 # spent 3µs making 1 call to Foswiki::Search::InfoCache::BEGIN@6 |
7 | 1 | 10µs | our @ISA = ('Foswiki::ListIterator'); | ||
8 | |||||
9 | # spent 4µs within Foswiki::Search::InfoCache::BEGIN@9 which was called:
# once (4µs+0s) by Foswiki::Search::BEGIN@19 at line 14 | ||||
10 | 1 | 4µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
11 | require locale; | ||||
12 | import locale(); | ||||
13 | } | ||||
14 | 1 | 25µs | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::Search::InfoCache::BEGIN@9 |
15 | |||||
16 | =begin TML | ||||
17 | |||||
18 | ---+ package Foswiki::Search::InfoCache | ||||
19 | |||||
20 | Support package; cache of topic info. When information about search hits is | ||||
21 | compiled for output, this cache is used to avoid recovering the same info | ||||
22 | about the same topic more than once. | ||||
23 | |||||
24 | =cut | ||||
25 | |||||
26 | # TODO: this is going to transform from an ugly duckling into the | ||||
27 | # ResultSet Iterator | ||||
28 | # Sven has the feeling that we should make result sets immutable | ||||
29 | |||||
30 | 2 | 24µs | 2 | 59µs | # spent 34µs (10+24) within Foswiki::Search::InfoCache::BEGIN@30 which was called:
# once (10µs+24µs) by Foswiki::Search::BEGIN@19 at line 30 # spent 34µs making 1 call to Foswiki::Search::InfoCache::BEGIN@30
# spent 24µs making 1 call to Exporter::import |
31 | 2 | 26µs | 1 | 3µs | # spent 3µs within Foswiki::Search::InfoCache::BEGIN@31 which was called:
# once (3µs+0s) by Foswiki::Search::BEGIN@19 at line 31 # spent 3µs making 1 call to Foswiki::Search::InfoCache::BEGIN@31 |
32 | 2 | 23µs | 1 | 4µs | # spent 4µs within Foswiki::Search::InfoCache::BEGIN@32 which was called:
# once (4µs+0s) by Foswiki::Search::BEGIN@19 at line 32 # spent 4µs making 1 call to Foswiki::Search::InfoCache::BEGIN@32 |
33 | 2 | 1.87ms | 1 | 750µs | # spent 750µs (663+87) within Foswiki::Search::InfoCache::BEGIN@33 which was called:
# once (663µs+87µs) by Foswiki::Search::BEGIN@19 at line 33 # spent 750µs making 1 call to Foswiki::Search::InfoCache::BEGIN@33 |
34 | |||||
35 | #use Monitor (); | ||||
36 | #Monitor::MonitorMethod('Foswiki::Search::InfoCache', 'getTopicListIterator'); | ||||
37 | |||||
38 | =begin TML | ||||
39 | |||||
40 | ---++ ClassMethod new($session, $defaultWeb, \@topicList) | ||||
41 | Initialise a new list of topics, allowing their data to be lazy loaded | ||||
42 | if and when needed. | ||||
43 | |||||
44 | $defaultWeb is used to qualify topics that do not have a web | ||||
45 | specifier - should expect it to be the same as BASEWEB in most cases. | ||||
46 | |||||
47 | Because this Iterator can be created and filled dynamically, once the Iterator hasNext() and next() methods are called, it is immutable. | ||||
48 | |||||
49 | =cut | ||||
50 | |||||
51 | #TODO: duplicates??, what about topicExists? | ||||
52 | #TODO: remove the iterator code from this __container__ and make a $this->getIterator() which can then be used. | ||||
53 | #TODO: replace the Iterator->reset() function with a lightweight Iterator->copyConstructor? | ||||
54 | #TODO: or..... make reset() make the object mutable again, so we can change the elements in the list, but re-use the meta cache?? | ||||
55 | #CONSIDER: convert the internals to a hash[tomAddress] = {matches->[list of resultint text bits], othermeta...} - except this does not give us order :/ | ||||
56 | |||||
57 | # spent 219ms (3.21+216) within Foswiki::Search::InfoCache::new which was called 80 times, avg 2.73ms/call:
# 40 times (2.49ms+215ms) by Foswiki::Store::SearchAlgorithms::Forking::_webQuery at line 279 of /var/www/foswikidev/core/lib/Foswiki/Store/SearchAlgorithms/Forking.pm, avg 5.45ms/call
# 40 times (728µs+177µs) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 70 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 23µs/call | ||||
58 | 80 | 281µs | my ( $class, $session, $defaultWeb, $topicList ) = @_; | ||
59 | |||||
60 | 80 | 1.30ms | 80 | 1.26ms | my $this = $class->SUPER::new( [] ); # spent 1.26ms making 80 calls to Foswiki::ListIterator::new, avg 16µs/call |
61 | 80 | 180µs | $this->{_session} = $session; | ||
62 | 80 | 180µs | $this->{_defaultWeb} = $defaultWeb; | ||
63 | 80 | 218µs | $this->{count} = 0; | ||
64 | 80 | 462µs | 40 | 214ms | if ( defined($topicList) ) { # spent 214ms making 40 calls to Foswiki::Search::InfoCache::addTopics, avg 5.36ms/call |
65 | $this->addTopics( $defaultWeb, @$topicList ); | ||||
66 | } | ||||
67 | |||||
68 | 80 | 330µs | return $this; | ||
69 | } | ||||
70 | |||||
71 | sub isImmutable { | ||||
72 | my $this = shift; | ||||
73 | return ( $this->{index} != 0 ); | ||||
74 | } | ||||
75 | |||||
76 | # spent 214ms (79.1+135) within Foswiki::Search::InfoCache::addTopics which was called 40 times, avg 5.36ms/call:
# 40 times (79.1ms+135ms) by Foswiki::Search::InfoCache::new at line 64, avg 5.36ms/call | ||||
77 | 40 | 1.75ms | my ( $this, $defaultWeb, @list ) = @_; | ||
78 | ASSERT( !$this->isImmutable() ) | ||||
79 | if DEBUG; #cannot modify list once its being used as an iterator. | ||||
80 | 40 | 16µs | ASSERT( defined($defaultWeb) ) if DEBUG; | ||
81 | |||||
82 | 40 | 161µs | foreach my $t (@list) { | ||
83 | 8760 | 22.1ms | 8760 | 135ms | my ( $web, $topic ) = # spent 135ms making 8760 calls to Foswiki::Func::normalizeWebTopicName, avg 15µs/call |
84 | Foswiki::Func::normalizeWebTopicName( $defaultWeb, $t ); | ||||
85 | 8760 | 12.3ms | push( @{ $this->{list} }, "$web.$topic" ); | ||
86 | 8760 | 8.55ms | $this->{count}++; | ||
87 | } | ||||
88 | 40 | 1.00ms | undef $this->{sorted}; | ||
89 | } | ||||
90 | |||||
91 | #TODO: what if it isa Meta obj | ||||
92 | #TODO: or an infoCache obj.. | ||||
93 | # spent 618ms (257+362) within Foswiki::Search::InfoCache::addTopic which was called 8760 times, avg 71µs/call:
# 8760 times (257ms+362ms) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 215 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 71µs/call | ||||
94 | 8760 | 4.35ms | my ( $this, $meta ) = @_; | ||
95 | ASSERT( !$this->isImmutable() ) | ||||
96 | if DEBUG; #cannot modify list once its being used as an iterator. | ||||
97 | |||||
98 | 8760 | 9.46ms | 8760 | 13.2ms | my $web = $meta->web(); # spent 13.2ms making 8760 calls to Foswiki::Meta::web, avg 2µs/call |
99 | 8760 | 9.66ms | 8760 | 12.4ms | my $topic = $meta->topic(); # spent 12.4ms making 8760 calls to Foswiki::Meta::topic, avg 1µs/call |
100 | |||||
101 | 8760 | 15.2ms | 8760 | 97.6ms | my ( $w, $t ) = Foswiki::Func::normalizeWebTopicName( $web, $topic ); # spent 97.6ms making 8760 calls to Foswiki::Func::normalizeWebTopicName, avg 11µs/call |
102 | 8760 | 9.68ms | my $webtopic = "$w.$t"; | ||
103 | 8760 | 6.92ms | push( @{ $this->{list} }, $webtopic ); | ||
104 | 8760 | 2.37ms | $this->{count}++; | ||
105 | 8760 | 32.4ms | 26280 | 239ms | if ( defined($meta) ) { # spent 211ms making 8760 calls to Foswiki::MetaCache::addMeta, avg 24µs/call
# spent 14.7ms making 8760 calls to Foswiki::search, avg 2µs/call
# spent 13.1ms making 8760 calls to Foswiki::Search::metacache, avg 1µs/call |
106 | $this->{_session}->search->metacache->addMeta( $web, $topic, $meta ); | ||||
107 | } | ||||
108 | 8760 | 43.4ms | undef $this->{sorted}; | ||
109 | } | ||||
110 | |||||
111 | sub numberOfTopics { | ||||
112 | my $this = shift; | ||||
113 | |||||
114 | #can't use this, as it lies once its gone through the 'sortResults' hack | ||||
115 | #and lies more because the filterByDate is evaluated later. | ||||
116 | #return scalar(@{ $this->{list} }); | ||||
117 | # when fixed, the count update in filterByDate should be removed | ||||
118 | |||||
119 | return $this->{count}; | ||||
120 | } | ||||
121 | |||||
122 | =begin TML | ||||
123 | |||||
124 | ---++ ObjectMethod sortResults($params) | ||||
125 | |||||
126 | the implementation of %SORT{"" limit="" order="" reverse="" date=""}% | ||||
127 | |||||
128 | it should be possible for the search engine to pre-sort, making this a nop, or to | ||||
129 | delay evaluated, partially evaluated, or even delegated to the DB/SQL | ||||
130 | |||||
131 | can call repeatedly, the list will only be re-sorted if new elements are added. | ||||
132 | |||||
133 | =cut | ||||
134 | |||||
135 | # spent 181ms (2.31+178) within Foswiki::Search::InfoCache::sortResults which was called 80 times, avg 2.26ms/call:
# 80 times (2.31ms+178ms) by Foswiki::Search::ResultSet::sortResults at line 273 of /var/www/foswikidev/core/lib/Foswiki/Search/ResultSet.pm, avg 2.26ms/call | ||||
136 | 80 | 90µs | my ( $this, $params ) = @_; | ||
137 | |||||
138 | #TODO: for now assume we do not change the sort order later | ||||
139 | 80 | 69µs | return if ( defined( $this->{sorted} ) ); | ||
140 | 80 | 39µs | $this->{sorted} = 1; | ||
141 | |||||
142 | 80 | 43µs | my $session = $this->{_session}; | ||
143 | |||||
144 | 80 | 84µs | my $sortOrder = $params->{order} || ''; | ||
145 | 80 | 259µs | 80 | 228µs | my $revSort = Foswiki::isTrue( $params->{reverse} ); # spent 228µs making 80 calls to Foswiki::isTrue, avg 3µs/call |
146 | 80 | 127µs | my $limit = $params->{limit} || ''; | ||
147 | |||||
148 | #SMELL: duplicated code - removeme | ||||
149 | # Limit search results | ||||
150 | 80 | 147µs | if ( $limit =~ m/(^\d+$)/ ) { | ||
151 | |||||
152 | # only digits, all else is the same as | ||||
153 | # an empty string. "+10" won't work. | ||||
154 | $limit = $1; | ||||
155 | } | ||||
156 | else { | ||||
157 | |||||
158 | # change 'all' to 0, then to big number | ||||
159 | 80 | 59µs | $limit = 0; | ||
160 | } | ||||
161 | 80 | 48µs | $limit = 32000 unless ($limit); | ||
162 | |||||
163 | # TODO: this is really an ugly hack to get around the rather | ||||
164 | # horrible limit 'performance' hack | ||||
165 | 80 | 74µs | if ( defined( $params->{showpage} ) | ||
166 | and $params->{showpage} > 1 ) | ||||
167 | { | ||||
168 | $limit = ( 2 + $params->{showpage} ) * $params->{pagesize}; | ||||
169 | } | ||||
170 | |||||
171 | # sort the topic list by date, author or topic name, and cache the | ||||
172 | # info extracted to do the sorting | ||||
173 | 80 | 237µs | if ( $sortOrder eq 'modified' ) { | ||
174 | |||||
175 | # For performance: | ||||
176 | # * sort by approx time (to get a rough list) | ||||
177 | # * shorten list to the limit + some slack | ||||
178 | # * sort by rev date on shortened list to get the accurate list | ||||
179 | # SMELL: Cairo had efficient two stage handling of modified sort. | ||||
180 | # SMELL: In Dakar this seems to be pointless since latest rev | ||||
181 | # time is taken from topic instead of dir list. | ||||
182 | my $slack = 10; | ||||
183 | if ( $limit + 2 * $slack < scalar( @{ $this->{list} } ) ) { | ||||
184 | |||||
185 | # sort by approx latest rev time | ||||
186 | my @tmpList = | ||||
187 | map { $_->[1] } | ||||
188 | sort { $a->[0] <=> $b->[0] } | ||||
189 | map { | ||||
190 | my ( $web, $topic ) = | ||||
191 | Foswiki::Func::normalizeWebTopicName( $this->{_defaultWeb}, | ||||
192 | $_ ); | ||||
193 | [ $session->getApproxRevTime( $web, $topic ), $_ ] | ||||
194 | } @{ $this->{list} }; | ||||
195 | @tmpList = reverse(@tmpList) if ($revSort); | ||||
196 | |||||
197 | # then shorten list and build the hashes for date and author | ||||
198 | my $idx = $limit + $slack; | ||||
199 | @{ $this->{list} } = (); | ||||
200 | foreach (@tmpList) { | ||||
201 | push( @{ $this->{list} }, $_ ); | ||||
202 | $idx -= 1; | ||||
203 | last if $idx <= 0; | ||||
204 | } | ||||
205 | } | ||||
206 | |||||
207 | } | ||||
208 | elsif ( | ||||
209 | $sortOrder =~ m/^creat/ || # topic creation time | ||||
210 | $sortOrder eq 'editby' || # author | ||||
211 | $sortOrder =~ s/^formfield\(([^\)]+)\)$/$1/ # form field | ||||
212 | ) | ||||
213 | { | ||||
214 | } | ||||
215 | else { | ||||
216 | |||||
217 | #default to topic sorting | ||||
218 | 80 | 36µs | $sortOrder = 'topic'; | ||
219 | } | ||||
220 | 80 | 609µs | 80 | 178ms | sortTopics( $this->{list}, $sortOrder, !$revSort ); # spent 178ms making 80 calls to Foswiki::Search::InfoCache::sortTopics, avg 2.23ms/call |
221 | } | ||||
222 | |||||
223 | =begin TML | ||||
224 | |||||
225 | ---++ filterByDate( $date ) | ||||
226 | |||||
227 | Filter the list by date interval; see System.TimeSpecifications. | ||||
228 | |||||
229 | This function adds a filter evaluator to the infocache that is evaluated | ||||
230 | as you iterate through the collection | ||||
231 | |||||
232 | <verbatim> | ||||
233 | $infoCache->filterByDate( $date ); | ||||
234 | </verbatim> | ||||
235 | |||||
236 | =cut | ||||
237 | |||||
238 | sub filterByDate { | ||||
239 | my ( $this, $date ) = @_; | ||||
240 | ASSERT( !defined( $this->{filter} ) ) if DEBUG; | ||||
241 | |||||
242 | my $session = $Foswiki::Plugins::SESSION; | ||||
243 | |||||
244 | require Foswiki::Time; | ||||
245 | my @ends = Foswiki::Time::parseInterval($date); | ||||
246 | |||||
247 | $this->{filter} = sub { | ||||
248 | my $webtopic = shift; | ||||
249 | |||||
250 | # if date falls out of interval: exclude topic from result | ||||
251 | my ( $web, $topic ) = | ||||
252 | Foswiki::Func::normalizeWebTopicName( $this->{_defaultWeb}, | ||||
253 | $webtopic ); | ||||
254 | my $topicdate = $session->getApproxRevTime( $web, $topic ); | ||||
255 | |||||
256 | return !( $topicdate < $ends[0] || $topicdate > $ends[1] ); | ||||
257 | |||||
258 | }; | ||||
259 | |||||
260 | return; | ||||
261 | } | ||||
262 | |||||
263 | ######OLD methods | ||||
264 | |||||
265 | # Sort a topic list using cached info | ||||
266 | # spent 178ms within Foswiki::Search::InfoCache::sortTopics which was called 80 times, avg 2.23ms/call:
# 80 times (178ms+0s) by Foswiki::Search::InfoCache::sortResults at line 220, avg 2.23ms/call | ||||
267 | 80 | 139µs | my ( $listRef, $sortfield, $revSort ) = @_; | ||
268 | ASSERT($sortfield) if DEBUG; | ||||
269 | |||||
270 | # don't spend time doing stuff to an empty list (or a list of one!) | ||||
271 | 80 | 75µs | return if ( scalar(@$listRef) < 2 ); | ||
272 | |||||
273 | 80 | 56µs | if ( $sortfield eq 'topic' ) { | ||
274 | |||||
275 | # simple sort | ||||
276 | # note no extraction of topic info here, as not needed | ||||
277 | # for the sort. Instead it will be read lazily, later on. | ||||
278 | # TODO: need to remove the web portion | ||||
279 | # mmm, need to profile if there is even a point to this - | ||||
280 | # as all topics still need to be parsed to find permissions | ||||
281 | 80 | 4.96ms | if ($revSort) { | ||
282 | @{$listRef} = map { $_->[1] } | ||||
283 | 17520 | 116ms | sort { $a->[0] cmp $b->[0] } | ||
284 | 17520 | 16.5ms | map { $_ =~ m/^(.*?)([^.]+)$/; [ $2, $_ ] } #quickhack to remove web | ||
285 | 80 | 40.1ms | @{$listRef}; | ||
286 | } | ||||
287 | else { | ||||
288 | @{$listRef} = map { $_->[1] } | ||||
289 | sort { $b->[0] cmp $a->[0] } | ||||
290 | map { $_ =~ m/^(.*?)([^.]+)$/; [ $2, $_ ] } #quickhack to remove web | ||||
291 | @{$listRef}; | ||||
292 | } | ||||
293 | ASSERT( $listRef->[0] ) if DEBUG; | ||||
294 | 80 | 554µs | return; | ||
295 | } | ||||
296 | |||||
297 | my $metacache = $Foswiki::Plugins::SESSION->search->metacache; | ||||
298 | |||||
299 | # populate the cache for each topic | ||||
300 | foreach my $webtopic ( @{$listRef} ) { | ||||
301 | |||||
302 | my $info = $metacache->get($webtopic); | ||||
303 | |||||
304 | if ( $sortfield =~ m/^creat/ ) { | ||||
305 | |||||
306 | # The act of getting the info will cache it | ||||
307 | #$metacache->getRev1Info( $webtopic, $sortfield ); | ||||
308 | $info->{$sortfield} = $info->{tom}->getRev1Info($sortfield); | ||||
309 | } | ||||
310 | else { | ||||
311 | |||||
312 | # SMELL: SD duplicated from above - I'd rather do it only here, | ||||
313 | # but i'm not sure if i can. | ||||
314 | $sortfield =~ s/^formfield\((.*)\)$/$1/; # form field | ||||
315 | |||||
316 | if ( !defined( $info->{$sortfield} ) ) { | ||||
317 | |||||
318 | #under normal circumstances this code is not called, because the metacach has already filled it. | ||||
319 | if ( $sortfield eq 'modified' ) { | ||||
320 | my $ri = $info->{tom}->getRevisionInfo(); | ||||
321 | $info->{$sortfield} = $ri->{date}; | ||||
322 | } | ||||
323 | else { | ||||
324 | $info->{$sortfield} = | ||||
325 | Foswiki::Search::displayFormField( $info->{tom}, | ||||
326 | $sortfield ); | ||||
327 | } | ||||
328 | } | ||||
329 | } | ||||
330 | |||||
331 | # SMELL: CDot isn't clear why this is needed, but it is otherwise | ||||
332 | # we end up with the users all being identified as "undef" | ||||
333 | $info->{editby} = | ||||
334 | $info->{tom}->session->{users}->getWikiName( $info->{editby} ); | ||||
335 | } | ||||
336 | @{$listRef} = map { $_->[1] } | ||||
337 | sort { _compare( $b, $a, $revSort ) } | ||||
338 | map { [ $metacache->get($_)->{$sortfield}, $_ ] } @{$listRef}; | ||||
339 | } | ||||
340 | |||||
341 | # RE for a full-spec floating-point number | ||||
342 | 1 | 2µs | our $NUMBER = qr/^[-+]?[0-9]+(\.[0-9]*)?([Ee][-+]?[0-9]+)?$/s; | ||
343 | |||||
344 | sub _compare { | ||||
345 | my ( $a, $b, $reverse ) = @_; | ||||
346 | my ( $x, $y ); | ||||
347 | if ($reverse) { | ||||
348 | $x = $a->[0]; | ||||
349 | $y = $b->[0]; | ||||
350 | } | ||||
351 | else { | ||||
352 | $x = $b->[0]; | ||||
353 | $y = $a->[0]; | ||||
354 | } | ||||
355 | |||||
356 | ASSERT( defined($x) ) if DEBUG; | ||||
357 | ASSERT( defined($y) ) if DEBUG; | ||||
358 | |||||
359 | my $comparison; | ||||
360 | if ( $x =~ m/$NUMBER/ && $y =~ m/$NUMBER/ ) { | ||||
361 | |||||
362 | # when sorting numbers do it largest first; this is just because | ||||
363 | # this is what date comparisons need. | ||||
364 | $comparison = $y <=> $x; | ||||
365 | } | ||||
366 | else { | ||||
367 | |||||
368 | my $datex = undef; | ||||
369 | my $datey = undef; | ||||
370 | |||||
371 | # parseTime can error if you give it a date out of range so we skip | ||||
372 | # testing if pure number | ||||
373 | # We skip testing for dates the first character is not a digit | ||||
374 | # as all formats we recognise as dates are | ||||
375 | if ( $x =~ m/^\d/ | ||||
376 | && $x !~ /$NUMBER/ | ||||
377 | && $y =~ m/^\d/ | ||||
378 | && $y !~ /$NUMBER/ ) | ||||
379 | { | ||||
380 | $datex = Foswiki::Time::parseTime($x); | ||||
381 | $datey = Foswiki::Time::parseTime($y) if $datex; | ||||
382 | } | ||||
383 | |||||
384 | if ( $datex && $datey ) { | ||||
385 | $comparison = $datey <=> $datex; | ||||
386 | } | ||||
387 | else { | ||||
388 | $comparison = $y cmp $x; | ||||
389 | } | ||||
390 | } | ||||
391 | |||||
392 | # tie breaker if keys are equal | ||||
393 | # reverse order will not apply to the secondary search key | ||||
394 | if ( $comparison == 0 ) { | ||||
395 | $comparison = $b->[1] cmp $a->[1]; | ||||
396 | } | ||||
397 | |||||
398 | return $comparison; | ||||
399 | } | ||||
400 | |||||
401 | =begin TML | ||||
402 | |||||
403 | ---++ StaticMethod getOptionFilter(\%options) -> $code | ||||
404 | |||||
405 | Analyse the options given in \%options and return a function that | ||||
406 | filters based on those options. \%options may include: | ||||
407 | * =includeTopics= - a comma-separated wildcard list of topic names | ||||
408 | * =excludeTopics= - do | ||||
409 | * =casesensitive= - boolean | ||||
410 | |||||
411 | =cut | ||||
412 | |||||
413 | # spent 1.71ms (905µs+803µs) within Foswiki::Search::InfoCache::getOptionFilter which was called 40 times, avg 43µs/call:
# 40 times (905µs+803µs) by Foswiki::Search::InfoCache::getTopicListIterator at line 482, avg 43µs/call | ||||
414 | 40 | 35µs | my ($options) = @_; | ||
415 | |||||
416 | 40 | 56µs | my $casesensitive = | ||
417 | defined( $options->{casesensitive} ) ? $options->{casesensitive} : 1; | ||||
418 | |||||
419 | # E.g. "Web*, FooBar" ==> "^(Web.*|FooBar)$" | ||||
420 | 40 | 10µs | my $includeTopics; | ||
421 | 40 | 5µs | my $topicFilter; | ||
422 | 40 | 6µs | my $excludeTopics; | ||
423 | 40 | 24µs | $excludeTopics = convertTopicPatternToRegex( $options->{excludeTopics} ) | ||
424 | if ( $options->{excludeTopics} ); | ||||
425 | |||||
426 | 40 | 35µs | if ( $options->{includeTopics} ) { | ||
427 | |||||
428 | # E.g. "Bug*, *Patch" ==> "^(Bug.*|.*Patch)$" | ||||
429 | 40 | 100µs | 40 | 803µs | $includeTopics = # spent 803µs making 40 calls to Foswiki::Search::InfoCache::convertTopicPatternToRegex, avg 20µs/call |
430 | convertTopicPatternToRegex( $options->{includeTopics} ); | ||||
431 | |||||
432 | 40 | 31µs | if ($casesensitive) { | ||
433 | $topicFilter = qr/$includeTopics/; | ||||
434 | } | ||||
435 | else { | ||||
436 | 40 | 201µs | $topicFilter = qr/$includeTopics/i; | ||
437 | } | ||||
438 | } | ||||
439 | |||||
440 | # spent 334ms within Foswiki::Search::InfoCache::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Search/InfoCache.pm:448] which was called 50280 times, avg 7µs/call:
# 50280 times (334ms+0s) by Foswiki::Iterator::FilterIterator::hasNext at line 73 of /var/www/foswikidev/core/lib/Foswiki/Iterator/FilterIterator.pm, avg 7µs/call | ||||
441 | 50280 | 14.6ms | my $item = shift; | ||
442 | 50280 | 276ms | return 0 unless !$topicFilter || $item =~ m/$topicFilter/; | ||
443 | 46040 | 8.80ms | if ( defined $excludeTopics ) { | ||
444 | return 0 if $item =~ m/$excludeTopics/; | ||||
445 | return 0 if !$casesensitive && $item =~ m/$excludeTopics/i; | ||||
446 | } | ||||
447 | 46040 | 364ms | return 1; | ||
448 | } | ||||
449 | 40 | 338µs | } | ||
450 | |||||
451 | ######################################### | ||||
452 | # TODO: this is _now_ a default utility method that can be used by | ||||
453 | # search&query algo's to brute force file a list of topics to search. | ||||
454 | # if you can avoid it, you should - as it needs to do an opendir on the | ||||
455 | # web, and if you have alot of topics, life gets slow | ||||
456 | # get a list of topics to search in the web, filtered by the $topic | ||||
457 | # spec | ||||
458 | # spent 2.79s (758µs+2.79) within Foswiki::Search::InfoCache::getTopicListIterator which was called 40 times, avg 69.9ms/call:
# 40 times (758µs+2.79s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 141 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 69.9ms/call | ||||
459 | 40 | 36µs | my ( $webObject, $options ) = @_; | ||
460 | |||||
461 | 40 | 58µs | my $casesensitive = | ||
462 | defined( $options->{casesensitive} ) ? $options->{casesensitive} : 1; | ||||
463 | |||||
464 | # See if there's a list of topics to avoid having to do a web list | ||||
465 | 40 | 10µs | my $it; | ||
466 | 40 | 31µs | if ( $casesensitive | ||
467 | && $options->{includeTopics} | ||||
468 | && $options->{includeTopics} =~ m/^([[:alnum:]]+(,\s*|\|))+$/ ) | ||||
469 | { | ||||
470 | |||||
471 | # topic list without wildcards | ||||
472 | # convert pattern into a topic list | ||||
473 | my @list = grep { | ||||
474 | $Foswiki::Plugins::SESSION->topicExists( $webObject->web, $_ ) | ||||
475 | } split( /,\s*|\|/, $options->{includeTopics} ); | ||||
476 | $it = new Foswiki::ListIterator( \@list ); | ||||
477 | } | ||||
478 | else { | ||||
479 | 40 | 115µs | 40 | 2.79s | $it = $webObject->eachTopic(); # spent 2.79s making 40 calls to Foswiki::Meta::eachTopic, avg 69.8ms/call |
480 | } | ||||
481 | |||||
482 | 40 | 350µs | 80 | 1.93ms | return Foswiki::Iterator::FilterIterator->new( $it, # spent 1.71ms making 40 calls to Foswiki::Search::InfoCache::getOptionFilter, avg 43µs/call
# spent 226µs making 40 calls to Foswiki::Iterator::FilterIterator::new, avg 6µs/call |
483 | getOptionFilter($options) ); | ||||
484 | } | ||||
485 | |||||
486 | # spent 803µs within Foswiki::Search::InfoCache::convertTopicPatternToRegex which was called 40 times, avg 20µs/call:
# 40 times (803µs+0s) by Foswiki::Search::InfoCache::getOptionFilter at line 429, avg 20µs/call | ||||
487 | 40 | 55µs | my ($topic) = @_; | ||
488 | 40 | 15µs | return '' unless ($topic); | ||
489 | |||||
490 | # 'Web*, FooBar' ==> ( 'Web*', 'FooBar' ) ==> ( 'Web.*', "FooBar" ) | ||||
491 | 40 | 71µs | my @arr = | ||
492 | 120 | 502µs | map { $_ = quotemeta($_); s/(^|(?<!\\))\\\*/\.\*/g; $_ } | ||
493 | split( /(?:,\s*|\|)/, $topic ); | ||||
494 | |||||
495 | 40 | 15µs | return '' unless (@arr); | ||
496 | |||||
497 | 40 | 245µs | return '^(' . join( '|', @arr ) . ')$'; | ||
498 | } | ||||
499 | |||||
500 | 1 | 4µs | 1; | ||
501 | __END__ |