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