Filename | /var/www/foswiki11/lib/Foswiki/Search.pm |
Statements | Executed 420290 statements in 391ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
41 | 1 | 1 | 372ms | 1.07s | formatResults | Foswiki::Search::
26367 | 5 | 4 | 48.1ms | 48.1ms | metacache | Foswiki::Search::
8800 | 2 | 1 | 13.9ms | 13.9ms | _collate_to_list | Foswiki::Search::
41 | 2 | 2 | 8.12ms | 49.8s | searchWeb | Foswiki::Search::
85 | 1 | 1 | 7.71ms | 9.65ms | formatResult | Foswiki::Search::
1 | 1 | 1 | 2.55ms | 3.09ms | BEGIN@19 | Foswiki::Search::
41 | 1 | 1 | 2.33ms | 70.5ms | loadTemplates | Foswiki::Search::
170 | 3 | 1 | 1.24ms | 1.65ms | __ANON__[:1336] | Foswiki::Search::
41 | 1 | 1 | 1.23ms | 13.5ms | parseSearch | Foswiki::Search::
1 | 1 | 1 | 886µs | 1.06ms | BEGIN@24 | Foswiki::Search::
1 | 1 | 1 | 740µs | 818µs | BEGIN@20 | Foswiki::Search::
82 | 2 | 1 | 526µs | 526µs | setup_callback | Foswiki::Search::
1 | 1 | 1 | 343µs | 456µs | BEGIN@23 | Foswiki::Search::
41 | 1 | 1 | 293µs | 293µs | formatCommon | Foswiki::Search::
41 | 1 | 1 | 203µs | 10.6ms | __ANON__[:133] | Foswiki::Search::
85 | 1 | 1 | 143µs | 143µs | __ANON__[:1014] | Foswiki::Search::
85 | 1 | 1 | 93µs | 93µs | __ANON__[:1015] | Foswiki::Search::
85 | 1 | 1 | 92µs | 92µs | __ANON__[:1018] | Foswiki::Search::
85 | 1 | 1 | 88µs | 88µs | __ANON__[:1016] | Foswiki::Search::
85 | 1 | 1 | 88µs | 88µs | __ANON__[:1017] | Foswiki::Search::
85 | 1 | 1 | 88µs | 88µs | __ANON__[:1019] | Foswiki::Search::
1 | 1 | 1 | 82µs | 2.87ms | finish | Foswiki::Search::
85 | 1 | 1 | 79µs | 79µs | __ANON__[:1020] | Foswiki::Search::
85 | 1 | 1 | 71µs | 71µs | __ANON__[:1021] | Foswiki::Search::
1 | 1 | 1 | 14µs | 26µs | BEGIN@12 | Foswiki::Search::
1 | 1 | 1 | 12µs | 17µs | BEGIN@13 | Foswiki::Search::
1 | 1 | 1 | 11µs | 16µs | BEGIN@26 | Foswiki::Search::
1 | 1 | 1 | 10µs | 10µs | new | Foswiki::Search::
1 | 1 | 1 | 10µs | 25µs | BEGIN@14 | Foswiki::Search::
1 | 1 | 1 | 8µs | 117µs | BEGIN@15 | Foswiki::Search::
1 | 1 | 1 | 5µs | 5µs | BEGIN@21 | Foswiki::Search::
1 | 1 | 1 | 4µs | 4µs | BEGIN@17 | Foswiki::Search::
1 | 1 | 1 | 4µs | 4µs | BEGIN@18 | Foswiki::Search::
1 | 1 | 1 | 3µs | 3µs | BEGIN@22 | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:1003] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:1007] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:1011] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:138] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:174] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:177] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:692] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:693] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:694] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:695] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:696] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:697] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:698] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:699] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:705] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:709] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:718] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:984] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:985] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:986] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:987] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | __ANON__[:999] | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | _countPattern | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | _extractPattern | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | _isSetTrue | Foswiki::Search::
0 | 0 | 0 | 0s | 0s | displayFormField | Foswiki::Search::
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; | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki::Search | ||||
7 | |||||
8 | This module implements all the search functionality. | ||||
9 | |||||
10 | =cut | ||||
11 | |||||
12 | 2 | 27µs | 2 | 38µs | # spent 26µs (14+12) within Foswiki::Search::BEGIN@12 which was called:
# once (14µs+12µs) by Foswiki::search at line 12 # spent 26µs making 1 call to Foswiki::Search::BEGIN@12
# spent 12µs making 1 call to strict::import |
13 | 2 | 26µs | 2 | 22µs | # spent 17µs (12+5) within Foswiki::Search::BEGIN@13 which was called:
# once (12µs+5µs) by Foswiki::search at line 13 # spent 17µs making 1 call to Foswiki::Search::BEGIN@13
# spent 5µs making 1 call to warnings::import |
14 | 2 | 26µs | 2 | 41µs | # spent 25µs (10+15) within Foswiki::Search::BEGIN@14 which was called:
# once (10µs+15µs) by Foswiki::search at line 14 # spent 25µs making 1 call to Foswiki::Search::BEGIN@14
# spent 16µs making 1 call to Assert::import |
15 | 2 | 28µs | 2 | 227µs | # spent 117µs (8+109) within Foswiki::Search::BEGIN@15 which was called:
# once (8µs+109µs) by Foswiki::search at line 15 # spent 117µs making 1 call to Foswiki::Search::BEGIN@15
# spent 109µs making 1 call to Error::import |
16 | |||||
17 | 2 | 18µs | 1 | 4µs | # spent 4µs within Foswiki::Search::BEGIN@17 which was called:
# once (4µs+0s) by Foswiki::search at line 17 # spent 4µs making 1 call to Foswiki::Search::BEGIN@17 |
18 | 2 | 17µs | 1 | 4µs | # spent 4µs within Foswiki::Search::BEGIN@18 which was called:
# once (4µs+0s) by Foswiki::search at line 18 # spent 4µs making 1 call to Foswiki::Search::BEGIN@18 |
19 | 2 | 98µs | 1 | 3.09ms | # spent 3.09ms (2.55+543µs) within Foswiki::Search::BEGIN@19 which was called:
# once (2.55ms+543µs) by Foswiki::search at line 19 # spent 3.09ms making 1 call to Foswiki::Search::BEGIN@19 |
20 | 2 | 104µs | 1 | 818µs | # spent 818µs (740+78) within Foswiki::Search::BEGIN@20 which was called:
# once (740µs+78µs) by Foswiki::search at line 20 # spent 818µs making 1 call to Foswiki::Search::BEGIN@20 |
21 | 2 | 18µs | 1 | 5µs | # spent 5µs within Foswiki::Search::BEGIN@21 which was called:
# once (5µs+0s) by Foswiki::search at line 21 # spent 5µs making 1 call to Foswiki::Search::BEGIN@21 |
22 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Search::BEGIN@22 which was called:
# once (3µs+0s) by Foswiki::search at line 22 # spent 3µs making 1 call to Foswiki::Search::BEGIN@22 |
23 | 2 | 89µs | 1 | 456µs | # spent 456µs (343+113) within Foswiki::Search::BEGIN@23 which was called:
# once (343µs+113µs) by Foswiki::search at line 23 # spent 456µs making 1 call to Foswiki::Search::BEGIN@23 |
24 | 2 | 116µs | 1 | 1.06ms | # spent 1.06ms (886µs+171µs) within Foswiki::Search::BEGIN@24 which was called:
# once (886µs+171µs) by Foswiki::search at line 24 # spent 1.06ms making 1 call to Foswiki::Search::BEGIN@24 |
25 | |||||
26 | # spent 16µs (11+5) within Foswiki::Search::BEGIN@26 which was called:
# once (11µs+5µs) by Foswiki::search at line 35 | ||||
27 | |||||
28 | # 'Use locale' for internationalisation of Perl sorting and searching - | ||||
29 | # main locale settings are done in Foswiki::setupLocale | ||||
30 | # Do a dynamic 'use locale' for this module | ||||
31 | 1 | 3µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
32 | 1 | 500ns | require locale; | ||
33 | 1 | 3µs | 1 | 5µs | import locale(); # spent 5µs making 1 call to locale::import |
34 | } | ||||
35 | 1 | 4.92ms | 1 | 16µs | } # spent 16µs making 1 call to Foswiki::Search::BEGIN@26 |
36 | |||||
37 | =begin TML | ||||
38 | |||||
39 | ---++ ClassMethod new ($session) | ||||
40 | |||||
41 | Constructor for the singleton Search engine object. | ||||
42 | |||||
43 | =cut | ||||
44 | |||||
45 | # spent 10µs within Foswiki::Search::new which was called:
# once (10µs+0s) by Foswiki::search at line 2099 of /var/www/foswiki11/lib/Foswiki.pm | ||||
46 | 1 | 1µs | my ( $class, $session ) = @_; | ||
47 | 1 | 7µs | my $this = bless( { session => $session }, $class ); | ||
48 | |||||
49 | 1 | 6µs | return $this; | ||
50 | } | ||||
51 | |||||
52 | =begin TML | ||||
53 | |||||
54 | ---++ ObjectMethod finish() | ||||
55 | Break circular references. | ||||
56 | |||||
57 | Note to developers; please undef *all* fields in the object explicitly, | ||||
58 | whether they are references or not. That way this method is "golden | ||||
59 | documentation" of the live fields in the object. | ||||
60 | |||||
61 | =cut | ||||
62 | |||||
63 | # spent 2.87ms (82µs+2.79) within Foswiki::Search::finish which was called:
# once (82µs+2.79ms) by Foswiki::finish at line 2163 of /var/www/foswiki11/lib/Foswiki.pm | ||||
64 | 1 | 700ns | my $this = shift; | ||
65 | 1 | 1µs | undef $this->{session}; | ||
66 | |||||
67 | # these may well be function objects, but if (a setting changes, it needs to be picked up again. | ||||
68 | 1 | 53µs | if ( defined( $this->{queryParser} ) ) { | ||
69 | 1 | 8µs | 1 | 2µs | $this->{queryParser}->finish(); # spent 2µs making 1 call to Foswiki::Infix::Parser::finish |
70 | 1 | 1µs | undef $this->{queryParser}; | ||
71 | } | ||||
72 | 1 | 600ns | if ( defined( $this->{searchParser} ) ) { | ||
73 | $this->{searchParser}->finish(); | ||||
74 | undef $this->{searchParser}; | ||||
75 | } | ||||
76 | 1 | 5µs | if ( defined( $this->{MetaCache} ) ) { | ||
77 | 1 | 5µs | 1 | 2.79ms | $this->{MetaCache}->finish(); # spent 2.79ms making 1 call to Foswiki::MetaCache::finish |
78 | 1 | 900ns | undef $this->{MetaCache}; | ||
79 | } | ||||
80 | } | ||||
81 | |||||
82 | =begin TML | ||||
83 | |||||
84 | ---++ ObjectMethod metacache | ||||
85 | returns the metacache. | ||||
86 | |||||
87 | =cut | ||||
88 | |||||
89 | # spent 48.1ms (48.1+33µs) within Foswiki::Search::metacache which was called 26367 times, avg 2µs/call:
# 8845 times (13.4ms+0s) by Foswiki::Search::formatResults at line 787, avg 2µs/call
# 8760 times (17.6ms+0s) by Foswiki::Search::InfoCache::addTopic at line 99 of /var/www/foswiki11/lib/Foswiki/Search/InfoCache.pm, avg 2µs/call
# 8760 times (17.1ms+33µs) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 230 of /var/www/foswiki11/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 2µs/call
# once (8µs+0s) by Foswiki::Users::TopicUserMapping::_getListOfGroups at line 1630 of /var/www/foswiki11/lib/Foswiki/Users/TopicUserMapping.pm
# once (4µs+0s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 114 of /var/www/foswiki11/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm | ||||
90 | 26367 | 8.88ms | my $this = shift; | ||
91 | |||||
92 | # these may well be function objects, but if (a setting changes, it needs to be picked up again. | ||||
93 | 26367 | 10.0ms | 1 | 33µs | if ( !defined( $this->{MetaCache} ) ) { # spent 33µs making 1 call to Foswiki::MetaCache::new |
94 | $this->{MetaCache} = new Foswiki::MetaCache( $this->{session} ); | ||||
95 | } | ||||
96 | 26367 | 98.4ms | return $this->{MetaCache}; | ||
97 | } | ||||
98 | |||||
99 | =begin TML | ||||
100 | |||||
101 | ---++ ObjectMethod parseSearch($searchString, $params) -> Foswiki::*::Node | ||||
102 | |||||
103 | parses the search string and builds the appropriate nodes (uses $param->{type} to work out which parser | ||||
104 | |||||
105 | TODO: make parser register themselves with their type, so that we could plug in anything. | ||||
106 | |||||
107 | =cut | ||||
108 | |||||
109 | # spent 13.5ms (1.23+12.3) within Foswiki::Search::parseSearch which was called 41 times, avg 329µs/call:
# 41 times (1.23ms+12.3ms) by Foswiki::Search::searchWeb at line 351, avg 329µs/call | ||||
110 | 41 | 19µs | my $this = shift; | ||
111 | 41 | 18µs | my $searchString = shift; | ||
112 | 41 | 12µs | my $params = shift; | ||
113 | |||||
114 | 41 | 8µs | my $query; | ||
115 | 41 | 7µs | my $theParser; | ||
116 | 41 | 36µs | if ( $params->{type} eq 'query' ) { | ||
117 | 41 | 25µs | unless ( defined( $this->{queryParser} ) ) { | ||
118 | 1 | 500ns | require Foswiki::Query::Parser; | ||
119 | 1 | 5µs | 1 | 1.01ms | $this->{queryParser} = new Foswiki::Query::Parser(); # spent 1.01ms making 1 call to Foswiki::Query::Parser::new |
120 | } | ||||
121 | 41 | 25µs | $theParser = $this->{queryParser}; | ||
122 | } | ||||
123 | else { | ||||
124 | unless ( defined( $this->{searchParser} ) ) { | ||||
125 | require Foswiki::Search::Parser; | ||||
126 | $this->{searchParser} = | ||||
127 | new Foswiki::Search::Parser( $this->{session} ); | ||||
128 | } | ||||
129 | $theParser = $this->{searchParser}; | ||||
130 | } | ||||
131 | # spent 10.6ms (203µs+10.4) within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:133] which was called 41 times, avg 258µs/call:
# 41 times (203µs+10.4ms) by Error::subs::try at line 419 of Error.pm, avg 258µs/call | ||||
132 | 41 | 296µs | 41 | 10.4ms | $query = $theParser->parse( $searchString, $params ); # spent 10.4ms making 41 calls to Foswiki::Infix::Parser::parse, avg 253µs/call |
133 | } | ||||
134 | catch Foswiki::Infix::Error with { | ||||
135 | |||||
136 | # Pass the error on to the caller | ||||
137 | throw Error::Simple( shift->stringify() ); | ||||
138 | 41 | 838µs | 123 | 218µs | }; # spent 161µs making 41 calls to Error::catch, avg 4µs/call
# spent 57µs making 41 calls to Error::subs::with, avg 1µs/call
# spent 11.0ms making 41 calls to Error::subs::try, avg 269µs/call, recursion: max depth 3, sum of overlapping time 11.0ms |
139 | 41 | 107µs | return $query; | ||
140 | } | ||||
141 | |||||
142 | sub _extractPattern { | ||||
143 | my ( $text, $pattern ) = @_; | ||||
144 | |||||
145 | # Pattern comes from topic, therefore tainted | ||||
146 | $pattern = | ||||
147 | Foswiki::Sandbox::untaint( $pattern, \&Foswiki::validatePattern ); | ||||
148 | |||||
149 | my $ok = 0; | ||||
150 | eval { | ||||
151 | |||||
152 | # The eval acts as a try block in case there is anything evil in | ||||
153 | # the pattern. | ||||
154 | $ok = 1 if ( $text =~ s/$pattern/$1/is ); | ||||
155 | }; | ||||
156 | $text = '' unless $ok; | ||||
157 | |||||
158 | return $text; | ||||
159 | } | ||||
160 | |||||
161 | # With the same argument as $pattern, returns a number which is the count of | ||||
162 | # occurences of the pattern argument. | ||||
163 | sub _countPattern { | ||||
164 | my ( $text, $pattern ) = @_; | ||||
165 | |||||
166 | $pattern = | ||||
167 | Foswiki::Sandbox::untaint( $pattern, \&Foswiki::validatePattern ); | ||||
168 | |||||
169 | my $count; | ||||
170 | try { | ||||
171 | |||||
172 | # see: perldoc -q count | ||||
173 | $count = () = $text =~ /$pattern/g; | ||||
174 | } | ||||
175 | catch Error::Simple with { | ||||
176 | $count = 0; | ||||
177 | }; | ||||
178 | |||||
179 | return $count; | ||||
180 | } | ||||
181 | |||||
182 | =begin TML | ||||
183 | |||||
184 | ---++ StaticMethod _isSetTrue( $value, $default ) -> $boolean | ||||
185 | |||||
186 | Returns 1 if =$value= is _actually set to_ true, and 0 otherwise. | ||||
187 | |||||
188 | If the value is undef, then =$default= is returned. If =$default= is | ||||
189 | not specified it is taken as 0. | ||||
190 | |||||
191 | =cut | ||||
192 | |||||
193 | sub _isSetTrue { | ||||
194 | my ( $value, $default ) = @_; | ||||
195 | |||||
196 | $default ||= 0; | ||||
197 | |||||
198 | return $default unless defined($value); | ||||
199 | |||||
200 | $value =~ s/on//gi; | ||||
201 | $value =~ s/yes//gi; | ||||
202 | $value =~ s/true//gi; | ||||
203 | return ($value) ? 0 : 1; | ||||
204 | } | ||||
205 | |||||
206 | =begin TML | ||||
207 | |||||
208 | ---++ ObjectMethod searchWeb (...) | ||||
209 | |||||
210 | Search one or more webs according to the parameters. | ||||
211 | |||||
212 | If =_callback= is set, that means the caller wants results as | ||||
213 | soon as they are ready. =_callback_ should be set to a reference | ||||
214 | to a function which takes =_cbdata= as the first parameter and | ||||
215 | remaining parameters the same as 'print'. | ||||
216 | |||||
217 | If =_callback= is set, the result is always undef. Otherwise the | ||||
218 | result is a string containing the rendered search results. | ||||
219 | |||||
220 | The function will throw Error::Simple if it encounters any problems with the | ||||
221 | syntax of the search string. | ||||
222 | |||||
223 | Note: If =format= is set, =template= will be ignored. | ||||
224 | |||||
225 | Note: For legacy, if =regex= is defined, it will force type='regex' | ||||
226 | |||||
227 | If =type="word"= it will be changed to =type="keyword"= with =wordboundaries=1=. This will be used for searching with scope="text" only, because scope="topic" will do a Perl search on topic names. | ||||
228 | |||||
229 | SMELL: If =template= is defined =bookview= will not work | ||||
230 | |||||
231 | SMELL: it seems that if you define =_callback= then you are | ||||
232 | responsible for converting the TML to HTML yourself! | ||||
233 | |||||
234 | FIXME: =callback= cannot work with format parameter (consider format='| $topic |' | ||||
235 | |||||
236 | =cut | ||||
237 | |||||
238 | # spent 49.8s (8.12ms+49.8) within Foswiki::Search::searchWeb which was called 41 times, avg 1.22s/call:
# 40 times (7.89ms+49.6s) by Foswiki::__ANON__[/var/www/foswiki11/lib/Foswiki/Macros/SEARCH.pm:33] at line 32 of /var/www/foswiki11/lib/Foswiki/Macros/SEARCH.pm, avg 1.24s/call
# once (235µs+218ms) by Foswiki::Users::TopicUserMapping::_getListOfGroups at line 1636 of /var/www/foswiki11/lib/Foswiki/Users/TopicUserMapping.pm | ||||
239 | 41 | 20µs | my $this = shift; | ||
240 | 41 | 29µs | my $session = $this->{session}; | ||
241 | 41 | 51µs | 41 | 36µs | ASSERT( defined $session->{webName} ) if DEBUG; # spent 36µs making 41 calls to Assert::ASSERTS_OFF, avg 888ns/call |
242 | 41 | 157µs | my %params = @_; | ||
243 | |||||
244 | 41 | 185µs | 41 | 708µs | my $baseWebObject = Foswiki::Meta->new( $session, $session->{webName} ); # spent 708µs making 41 calls to Foswiki::Meta::new, avg 17µs/call |
245 | |||||
246 | 41 | 90µs | 41 | 178µs | my ( $callback, $cbdata ) = setup_callback( \%params, $baseWebObject ); # spent 178µs making 41 calls to Foswiki::Search::setup_callback, avg 4µs/call |
247 | |||||
248 | 41 | 33µs | my $baseTopic = $params{basetopic} || $session->{topicName}; | ||
249 | 41 | 18µs | my $baseWeb = $params{baseweb} || $session->{webName}; | ||
250 | 41 | 129µs | 41 | 92µs | $params{casesensitive} = Foswiki::isTrue( $params{casesensitive} ); # spent 92µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
251 | 41 | 48µs | $params{excludeTopics} = $params{excludetopic} || ''; | ||
252 | 41 | 47µs | my $formatDefined = $params{formatdefined} = defined $params{format}; | ||
253 | 41 | 17µs | my $format = $params{format}; | ||
254 | |||||
255 | 41 | 116µs | 41 | 62µs | $params{multiple} = Foswiki::isTrue( $params{multiple} ); # spent 62µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
256 | |||||
257 | # Let the search know we're only concerned with one hit per file | ||||
258 | 41 | 95µs | 41 | 305µs | $params{files_without_match} = not Foswiki::isTrue( $params{multiple} ); # spent 305µs making 41 calls to Foswiki::isTrue, avg 7µs/call |
259 | |||||
260 | 41 | 87µs | 41 | 219µs | $params{nonoise} = Foswiki::isTrue( $params{nonoise} ); # spent 219µs making 41 calls to Foswiki::isTrue, avg 5µs/call |
261 | 41 | 117µs | 41 | 77µs | $params{noempty} = Foswiki::isTrue( $params{noempty}, $params{nonoise} ); # spent 77µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
262 | ### $params{zeroresults} = Foswiki::isTrue( ( $params{zeroresults} ), $params{nonoise} ); | ||||
263 | |||||
264 | #paging - this code should be hidden in the InfoCache iterator, but atm, that won't let me do multi-web | ||||
265 | #TODO: or... I may wrap an AggregateIterator in a PagingIterator which then is evaluated by a Formattingiterator. | ||||
266 | 41 | 59µs | my $pagesize = | ||
267 | $params{pagesize} | ||||
268 | || $Foswiki::cfg{Search}{DefaultPageSize} | ||||
269 | || 25; | ||||
270 | |||||
271 | 41 | 31µs | require Digest::MD5; | ||
272 | 41 | 15µs | my $string_id = $params{_RAW} || 'we had better not go there'; | ||
273 | 41 | 455µs | 41 | 283µs | my $paging_ID = 'SEARCH' . Digest::MD5::md5_hex($string_id); # spent 283µs making 41 calls to Digest::MD5::md5_hex, avg 7µs/call |
274 | 41 | 33µs | $params{pager_urlparam_id} = $paging_ID; | ||
275 | |||||
276 | # 1-based system; 0 is not a valid page number | ||||
277 | 41 | 242µs | 82 | 861µs | my $showpage = # spent 781µs making 41 calls to Foswiki::Request::param, avg 19µs/call
# spent 80µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
278 | $session->{request}->param($paging_ID) | ||||
279 | || $params{showpage} | ||||
280 | || ( Foswiki::isTrue( $params{pager} ) ? 1 : undef ); | ||||
281 | |||||
282 | 41 | 31µs | if ( defined( $params{pagesize} ) or defined($showpage) ) { | ||
283 | if ( !defined($showpage) ) { | ||||
284 | $showpage = 1; | ||||
285 | } | ||||
286 | $params{pager_skip_results_from} = $pagesize * ( $showpage - 1 ); | ||||
287 | $params{pager_show_results_to} = $pagesize; | ||||
288 | } | ||||
289 | |||||
290 | #TODO: refactorme | ||||
291 | 41 | 19µs | my $header = $params{header}; | ||
292 | 41 | 45µs | my $footer = $params{footer}; | ||
293 | 41 | 105µs | 41 | 69µs | my $noTotal = Foswiki::isTrue( $params{nototal}, $params{nonoise} ); # spent 69µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
294 | |||||
295 | 41 | 69µs | 41 | 238µs | my $noEmpty = Foswiki::isTrue( $params{noempty}, $params{nonoise} ); # spent 238µs making 41 calls to Foswiki::isTrue, avg 6µs/call |
296 | |||||
297 | # Note: a defined header/footer overrides noheader/nofooter | ||||
298 | # To maintain Cairo compatibility we ommit default header/footer if the | ||||
299 | 41 | 125µs | 41 | 68µs | my $noHeader = # spent 68µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
300 | !defined($header) | ||||
301 | && Foswiki::isTrue( $params{noheader}, $params{nonoise} ) | ||||
302 | || ( !$header && $formatDefined ); | ||||
303 | |||||
304 | 41 | 45µs | 1 | 2µs | my $noFooter = # spent 2µs making 1 call to Foswiki::isTrue |
305 | !defined($footer) | ||||
306 | && Foswiki::isTrue( $params{nofooter}, $params{nonoise} ) | ||||
307 | || ( !$footer && $formatDefined ); | ||||
308 | |||||
309 | 41 | 79µs | 41 | 61µs | my $noSummary = Foswiki::isTrue( $params{nosummary}, $params{nonoise} ); # spent 61µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
310 | 41 | 77µs | 41 | 54µs | my $zeroResults = # spent 54µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
311 | Foswiki::isTrue( $params{zeroresults}, $params{nonoise} || 1 ); | ||||
312 | |||||
313 | #END TODO | ||||
314 | |||||
315 | 41 | 72µs | 41 | 57µs | my $doBookView = Foswiki::isTrue( $params{bookview} ); # spent 57µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
316 | |||||
317 | 41 | 60µs | 41 | 53µs | my $revSort = Foswiki::isTrue( $params{reverse} ); # spent 53µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
318 | 41 | 41µs | $params{scope} = $params{scope} || ''; | ||
319 | 41 | 30µs | my $searchString = defined $params{search} ? $params{search} : ''; | ||
320 | |||||
321 | 41 | 40µs | $params{includeTopics} = $params{topic} || ''; | ||
322 | 41 | 17µs | $params{type} = $params{type} || ''; | ||
323 | |||||
324 | 41 | 40µs | $params{wordboundaries} = 0; | ||
325 | 41 | 26µs | if ( $params{type} eq 'word' ) { | ||
326 | |||||
327 | # 'word' is exactly the same as 'keyword', except we will be searching | ||||
328 | # with word boundaries | ||||
329 | $params{type} = 'keyword'; | ||||
330 | $params{wordboundaries} = 1; | ||||
331 | } | ||||
332 | |||||
333 | 41 | 60µs | my $webNames = $params{web} || ''; | ||
334 | 41 | 40µs | my $date = $params{date} || ''; | ||
335 | 41 | 42µs | my $recurse = $params{'recurse'} || ''; | ||
336 | |||||
337 | 41 | 70µs | $baseWeb =~ s/\./\//go; | ||
338 | |||||
339 | 41 | 20µs | $params{type} = 'regex' if ( $params{regex} ); | ||
340 | |||||
341 | #TODO: quick hackjob - see what the feature proposal gives before it becomes public | ||||
342 | 41 | 32µs | if ( defined( $params{groupby} ) and ( $params{groupby} eq 'none' ) ) { | ||
343 | |||||
344 | #_only_ allow groupby="none" - as its a secrect none public setting. | ||||
345 | } | ||||
346 | else { | ||||
347 | 41 | 46µs | $params{groupby} = 'web'; | ||
348 | } | ||||
349 | |||||
350 | ################### Perform The Search | ||||
351 | 41 | 145µs | 41 | 13.5ms | my $query = $this->parseSearch( $searchString, \%params ); # spent 13.5ms making 41 calls to Foswiki::Search::parseSearch, avg 329µs/call |
352 | |||||
353 | #setting the inputTopicSet to be undef allows the search/query algo to use | ||||
354 | #the topic="" and excludetopic="" params and web Obj to get a new list of topics. | ||||
355 | #this allows the algo's to customise and optimise the getting of this list themselves. | ||||
356 | 41 | 158µs | 41 | 48.7s | my $infoCache = Foswiki::Meta::query( $query, undef, \%params ); # spent 48.7s making 41 calls to Foswiki::Meta::query, avg 1.19s/call |
357 | |||||
358 | ################### Do the Rendering | ||||
359 | |||||
360 | # If the search did not return anything, return the rendered zeroresults | ||||
361 | # if it is defined as a string. | ||||
362 | # (http://foswiki.org/Development/AddDefaultTopicParameterToINCLUDE) | ||||
363 | 41 | 102µs | 41 | 879µs | if ( not $infoCache->hasNext() ) { # spent 879µs making 41 calls to Foswiki::Search::ResultSet::hasNext, avg 21µs/call |
364 | if ( not $zeroResults ) { | ||||
365 | return ''; | ||||
366 | } | ||||
367 | else { | ||||
368 | if ( not _isSetTrue( $params{zeroresults}, 1 ) ) { | ||||
369 | |||||
370 | #foswiki 1.1 Feature Proposal: SEARCH needs an alt parameter in case of zero results | ||||
371 | |||||
372 | #TODO: extract & merge with extraction of footer processing code below | ||||
373 | my $result = $params{zeroresults}; | ||||
374 | |||||
375 | $result =~ s/\$web/$baseWeb/gos; # expand name of web | ||||
376 | $result =~ s/([^\n])$/$1\n/os; # add new line at end | ||||
377 | |||||
378 | # output footer of $web | ||||
379 | $result =~ s/\$ntopics/0/gs; | ||||
380 | $result =~ s/\$nhits/0/gs; | ||||
381 | $result =~ s/\$index/0/gs; | ||||
382 | |||||
383 | #legacy SEARCH counter support | ||||
384 | $result =~ s/%NTOPICS%/0/go; | ||||
385 | |||||
386 | $result = Foswiki::expandStandardEscapes($result); | ||||
387 | $result =~ s/\n$//os; # remove trailing new line | ||||
388 | |||||
389 | return $result; | ||||
390 | } | ||||
391 | } | ||||
392 | } | ||||
393 | |||||
394 | 41 | 353µs | 41 | 70.5ms | my $tmplSearch = # spent 70.5ms making 41 calls to Foswiki::Search::loadTemplates, avg 1.72ms/call |
395 | $this->loadTemplates( \%params, $baseWebObject, $formatDefined, | ||||
396 | $doBookView, $noHeader, $noSummary, $noTotal, $noFooter ); | ||||
397 | |||||
398 | # Generate 'Search:' part showing actual search string used | ||||
399 | # Ommit any text before search results if either nosearch or nonoise is on | ||||
400 | 41 | 113µs | 41 | 399µs | my $nonoise = Foswiki::isTrue( $params{nonoise} ); # spent 399µs making 41 calls to Foswiki::isTrue, avg 10µs/call |
401 | 41 | 102µs | 41 | 80µs | my $noSearch = Foswiki::isTrue( $params{nosearch}, $nonoise ); # spent 80µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
402 | 41 | 15µs | unless ($noSearch) { | ||
403 | my $searchStr = $searchString; | ||||
404 | $searchStr =~ s/&/&/go; | ||||
405 | $searchStr =~ s/</</go; | ||||
406 | $searchStr =~ s/>/>/go; | ||||
407 | |||||
408 | $tmplSearch =~ s/%SEARCHSTRING%/$searchStr/go; | ||||
409 | &$callback( $cbdata, $tmplSearch ); | ||||
410 | } | ||||
411 | |||||
412 | # We now format the results. | ||||
413 | # All the | ||||
414 | 41 | 299µs | 41 | 1.07s | my ( $numberOfResults, $web_searchResult ) = # spent 1.07s making 41 calls to Foswiki::Search::formatResults, avg 26.0ms/call |
415 | $this->formatResults( $query, $infoCache, \%params ); | ||||
416 | |||||
417 | #worst possible way to do this: copy the entire if() statement from a few lines higher up | ||||
418 | #we need to repeat it in 1.1, as the paging is in formatResults, whereas in 1.2, search returns | ||||
419 | #a Paging Iterator. | ||||
420 | #Sven has decided as this is probably the final 1.1 patch release, that its going to be more | ||||
421 | #obvious not to extract to a function | ||||
422 | 41 | 17µs | if ( $numberOfResults == 0 ) { | ||
423 | if ( not $zeroResults ) { | ||||
424 | return ''; | ||||
425 | } | ||||
426 | else { | ||||
427 | if ( not _isSetTrue( $params{zeroresults}, 1 ) ) { | ||||
428 | |||||
429 | #foswiki 1.1 Feature Proposal: SEARCH needs an alt parameter in case of zero results | ||||
430 | |||||
431 | #TODO: extract & merge with extraction of footer processing code below | ||||
432 | my $result = $params{zeroresults}; | ||||
433 | |||||
434 | $result =~ s/\$web/$baseWeb/gos; # expand name of web | ||||
435 | $result =~ s/([^\n])$/$1\n/os; # add new line at end | ||||
436 | |||||
437 | # output footer of $web | ||||
438 | $result =~ s/\$ntopics/0/gs; | ||||
439 | $result =~ s/\$nhits/0/gs; | ||||
440 | $result =~ s/\$index/0/gs; | ||||
441 | |||||
442 | #legacy SEARCH counter support | ||||
443 | $result =~ s/%NTOPICS%/0/go; | ||||
444 | |||||
445 | $result = Foswiki::expandStandardEscapes($result); | ||||
446 | $result =~ s/\n$//os; # remove trailing new line | ||||
447 | |||||
448 | return $result; | ||||
449 | } | ||||
450 | } | ||||
451 | } | ||||
452 | |||||
453 | 41 | 46µs | return if ( defined $params{_callback} ); | ||
454 | |||||
455 | 40 | 272µs | my $searchResult = join( '', @{ $params{_cbdata} } ); | ||
456 | |||||
457 | 40 | 196µs | 40 | 508µs | $searchResult = Foswiki::expandStandardEscapes($searchResult); # spent 508µs making 40 calls to Foswiki::expandStandardEscapes, avg 13µs/call |
458 | |||||
459 | # Remove trailing separator or new line if nofinalnewline parameter is set | ||||
460 | 40 | 129µs | 40 | 90µs | my $noFinalNewline = Foswiki::isTrue( $params{nofinalnewline}, 1 ); # spent 90µs making 40 calls to Foswiki::isTrue, avg 2µs/call |
461 | 40 | 33µs | if ( $formatDefined && $noFinalNewline ) { | ||
462 | 40 | 22µs | if ( $params{separator} ) { | ||
463 | my $separator = quotemeta( $params{separator} ); | ||||
464 | $searchResult =~ s/$separator$//s; # remove separator at end | ||||
465 | } | ||||
466 | else { | ||||
467 | 40 | 52µs | $searchResult =~ s/\n$//os; # remove trailing new line | ||
468 | } | ||||
469 | } | ||||
470 | |||||
471 | 40 | 1.98ms | return $searchResult; | ||
472 | } | ||||
473 | |||||
474 | =begin TML | ||||
475 | |||||
476 | ---++ ObjectMethod loadTemplates (...) | ||||
477 | |||||
478 | this code was extracted from searchWeb, and should probably be private. | ||||
479 | |||||
480 | =cut | ||||
481 | |||||
482 | # spent 70.5ms (2.33+68.1) within Foswiki::Search::loadTemplates which was called 41 times, avg 1.72ms/call:
# 41 times (2.33ms+68.1ms) by Foswiki::Search::searchWeb at line 394, avg 1.72ms/call | ||||
483 | my ( | ||||
484 | 41 | 118µs | $this, $params, $baseWebObject, | ||
485 | $formatDefined, $doBookView, $noHeader, | ||||
486 | $noSummary, $noTotal, $noFooter | ||||
487 | ) = @_; | ||||
488 | |||||
489 | 41 | 52µs | my $session = $this->{session}; | ||
490 | |||||
491 | #tmpl loading code. | ||||
492 | 41 | 30µs | my $tmpl = ''; | ||
493 | |||||
494 | 41 | 44µs | my $template = $params->{template} || ''; | ||
495 | 41 | 26µs | if ($formatDefined) { | ||
496 | $template = 'searchformat'; | ||||
497 | } | ||||
498 | elsif ($template) { | ||||
499 | |||||
500 | # template definition overrides book and rename views | ||||
501 | } | ||||
502 | elsif ($doBookView) { | ||||
503 | $template = 'searchbookview'; | ||||
504 | } | ||||
505 | else { | ||||
506 | $template = 'search'; | ||||
507 | } | ||||
508 | 41 | 433µs | 82 | 53.4ms | $tmpl = $session->templates->readTemplate($template); # spent 53.2ms making 41 calls to Foswiki::Templates::readTemplate, avg 1.30ms/call
# spent 213µs making 41 calls to Foswiki::templates, avg 5µs/call |
509 | |||||
510 | #print STDERR "}}} $tmpl {{{\n"; | ||||
511 | # SMELL: the only META tags in a template will be METASEARCH | ||||
512 | # Why the heck are they being filtered???? | ||||
513 | #TODO: write a unit test that uses topic based templates with META's in them and if ok, remove. | ||||
514 | 41 | 45µs | $tmpl =~ s/\%META{.*?}\%//go; # remove %META{'parent'}% | ||
515 | |||||
516 | # Split template into 5 sections | ||||
517 | 41 | 67µs | my ( $tmplHead, $tmplSearch, $tmplTable, $tmplNumber, $tmplTail ) = | ||
518 | split( /%SPLIT%/, $tmpl ); | ||||
519 | |||||
520 | 41 | 11µs | my $repeatText; | ||
521 | |||||
522 | 41 | 34µs | if ( !defined($tmplTail) ) { | ||
523 | 41 | 252µs | 82 | 4.41ms | $tmplSearch = $session->templates->expandTemplate('SEARCH:searched'); # spent 4.31ms making 41 calls to Foswiki::Templates::expandTemplate, avg 105µs/call
# spent 104µs making 41 calls to Foswiki::templates, avg 3µs/call |
524 | 41 | 131µs | 82 | 8.45ms | $tmplNumber = $session->templates->expandTemplate('SEARCH:count'); # spent 8.37ms making 41 calls to Foswiki::Templates::expandTemplate, avg 204µs/call
# spent 75µs making 41 calls to Foswiki::templates, avg 2µs/call |
525 | |||||
526 | #it'd be nice to not need this if, but it seem that the noheader setting is ignored if a header= is set. truely bizzare | ||||
527 | #TODO: push up the 'noheader' evaluation to take not of this quirk | ||||
528 | #TODO: um, we die when ASSERT is on with a wide char in print | ||||
529 | 41 | 23µs | unless ($noHeader) { | ||
530 | $params->{header} = | ||||
531 | $session->templates->expandTemplate('SEARCH:header') | ||||
532 | unless defined $params->{header}; | ||||
533 | } | ||||
534 | |||||
535 | 41 | 127µs | 82 | 1.82ms | $repeatText = $session->templates->expandTemplate('SEARCH:format'); # spent 1.75ms making 41 calls to Foswiki::Templates::expandTemplate, avg 43µs/call
# spent 73µs making 41 calls to Foswiki::templates, avg 2µs/call |
536 | |||||
537 | 41 | 75µs | unless ($noFooter) { | ||
538 | $params->{footer} = | ||||
539 | $session->templates->expandTemplate('SEARCH:footer') | ||||
540 | unless defined $params->{footer}; | ||||
541 | } | ||||
542 | } | ||||
543 | else { | ||||
544 | |||||
545 | #Historical legacy form of the search TMPL's | ||||
546 | # header and footer of $web | ||||
547 | my $beforeText; | ||||
548 | my $afterText; | ||||
549 | ( $beforeText, $repeatText, $afterText ) = | ||||
550 | split( /%REPEAT%/, $tmplTable ); | ||||
551 | |||||
552 | unless ($noHeader) { | ||||
553 | $params->{header} = $beforeText unless defined $params->{header}; | ||||
554 | } | ||||
555 | |||||
556 | unless ($noFooter) { | ||||
557 | $params->{footer} ||= $afterText; | ||||
558 | } | ||||
559 | } | ||||
560 | |||||
561 | #nosummary="on" nosearch="on" noheader="on" nototal="on" | ||||
562 | 41 | 28µs | if ($noSummary) { | ||
563 | 41 | 25µs | $repeatText =~ s/%TEXTHEAD%//go; | ||
564 | 41 | 12µs | $repeatText =~ s/ //go; | ||
565 | } | ||||
566 | else { | ||||
567 | $repeatText =~ s/%TEXTHEAD%/\$summary(searchcontext)/go; | ||||
568 | } | ||||
569 | 41 | 45µs | $params->{format} ||= $repeatText; | ||
570 | |||||
571 | 41 | 92µs | $params->{footercounter} ||= $tmplNumber; | ||
572 | |||||
573 | 41 | 145µs | return $tmplSearch; | ||
574 | } | ||||
575 | |||||
576 | =begin TML | ||||
577 | ---++ formatResults | ||||
578 | |||||
579 | the implementation of %FORMAT{}% | ||||
580 | |||||
581 | TODO: rewrite to take a resultset, a set of params? and a hash of sub's to | ||||
582 | enable evaluations of things like '$include(blah)' in format strings. | ||||
583 | |||||
584 | have a default set of replacements like $lt, $nop, $percnt, $dollar etc, and then | ||||
585 | the hash of subs can take care of %MACRO{}% specific complex to evaluate replacements.. | ||||
586 | |||||
587 | (that way we don't pre-evaluate and then subst) | ||||
588 | |||||
589 | =cut | ||||
590 | |||||
591 | # spent 1.07s (372ms+693ms) within Foswiki::Search::formatResults which was called 41 times, avg 26.0ms/call:
# 41 times (372ms+693ms) by Foswiki::Search::searchWeb at line 414, avg 26.0ms/call | ||||
592 | 41 | 50µs | my ( $this, $query, $infoCache, $params ) = @_; | ||
593 | 41 | 27µs | my $session = $this->{session}; | ||
594 | 41 | 56µs | my $users = $session->{users}; | ||
595 | |||||
596 | 41 | 180µs | 41 | 348µs | my ( $callback, $cbdata ) = setup_callback($params); # spent 348µs making 41 calls to Foswiki::Search::setup_callback, avg 8µs/call |
597 | |||||
598 | 41 | 63µs | my $baseTopic = $session->{topicName}; | ||
599 | 41 | 27µs | my $baseWeb = $session->{webName}; | ||
600 | 41 | 79µs | 41 | 66µs | my $doBookView = Foswiki::isTrue( $params->{bookview} ); # spent 66µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
601 | 41 | 102µs | 41 | 215µs | my $caseSensitive = Foswiki::isTrue( $params->{casesensitive} ); # spent 215µs making 41 calls to Foswiki::isTrue, avg 5µs/call |
602 | 41 | 105µs | 41 | 72µs | my $doExpandVars = Foswiki::isTrue( $params->{expandvariables} ); # spent 72µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
603 | 41 | 66µs | 41 | 182µs | my $nonoise = Foswiki::isTrue( $params->{nonoise} ); # spent 182µs making 41 calls to Foswiki::isTrue, avg 4µs/call |
604 | 41 | 91µs | 41 | 64µs | my $noSearch = Foswiki::isTrue( $params->{nosearch}, $nonoise ); # spent 64µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
605 | 41 | 24µs | my $formatDefined = defined $params->{format}; | ||
606 | 41 | 18µs | my $format = $params->{format} || ''; | ||
607 | 41 | 25µs | my $header = $params->{header} || ''; | ||
608 | 41 | 29µs | my $footer = $params->{footer} || ''; | ||
609 | 41 | 24µs | my $limit = $params->{limit} || ''; | ||
610 | |||||
611 | # Limit search results. Cannot be deprecated | ||||
612 | # Limit will still be needed for the application types of SEARCHES | ||||
613 | # even if pagesize is added as feature. Example for searching and listing | ||||
614 | # text from first 5 bullets in a formatted multiple type search | ||||
615 | 41 | 36µs | if ( $limit =~ /(^\d+$)/o ) { | ||
616 | |||||
617 | # only digits, all else is the same as | ||||
618 | # an empty string. "+10" won't work. | ||||
619 | $limit = $1; | ||||
620 | } | ||||
621 | else { | ||||
622 | |||||
623 | # change 'all' to 0, then to big number | ||||
624 | 41 | 15µs | $limit = 0; | ||
625 | } | ||||
626 | 41 | 16µs | $limit = 32000 unless ($limit); | ||
627 | |||||
628 | #pager formatting | ||||
629 | 41 | 12µs | my %pager_formatting; | ||
630 | 41 | 27µs | if ( defined( $params->{pager_show_results_to} ) | ||
631 | and $params->{pager_show_results_to} > 0 ) | ||||
632 | { | ||||
633 | $limit = $params->{pager_show_results_to}; | ||||
634 | |||||
635 | #paging - this code should be hidden in the InfoCache iterator, but atm, that won't let me do multi-web | ||||
636 | my $pagesize = | ||||
637 | $params->{pagesize} | ||||
638 | || $Foswiki::cfg{Search}{DefaultPageSize} | ||||
639 | || 25; | ||||
640 | |||||
641 | #TODO: paging only implemented for SEARCH atm :/ | ||||
642 | my $paging_ID = $params->{pager_urlparam_id}; | ||||
643 | |||||
644 | # 1-based system; 0 is not a valid page number | ||||
645 | my $showpage = | ||||
646 | $session->{request}->param($paging_ID) | ||||
647 | || $params->{showpage} | ||||
648 | || 1; | ||||
649 | |||||
650 | #TODO: need to ask the ResultSet | ||||
651 | my $numberofpages = 0; | ||||
652 | $numberofpages = ( $infoCache->numberOfTopics / $params->{pagesize} ) | ||||
653 | if $params->{pagesize}; | ||||
654 | $numberofpages = int($numberofpages) + 1; | ||||
655 | |||||
656 | #TODO: excuse me? | ||||
657 | my $sep = ' '; | ||||
658 | |||||
659 | my $nextidx = $showpage + 1; | ||||
660 | my $previousidx = $showpage - 1; | ||||
661 | |||||
662 | my %new_params; | ||||
663 | |||||
664 | #kill me please, i can't find a way to just load up the hash :( | ||||
665 | foreach my $key ( $session->{request}->param ) { | ||||
666 | $new_params{$key} = $session->{request}->param($key); | ||||
667 | } | ||||
668 | |||||
669 | $session->templates->readTemplate('searchformat'); | ||||
670 | |||||
671 | my $previouspagebutton = ''; | ||||
672 | my $previouspageurl = ''; | ||||
673 | if ( $previousidx >= 1 ) { | ||||
674 | $new_params{$paging_ID} = $previousidx; | ||||
675 | $previouspageurl = | ||||
676 | Foswiki::Func::getScriptUrl( $baseWeb, $baseTopic, 'view', | ||||
677 | %new_params ); | ||||
678 | $previouspagebutton = | ||||
679 | $session->templates->expandTemplate('SEARCH:pager_previous'); | ||||
680 | } | ||||
681 | my $nextpagebutton = ''; | ||||
682 | my $nextpageurl = ''; | ||||
683 | if ( $nextidx <= $numberofpages ) { | ||||
684 | $new_params{$paging_ID} = $nextidx; | ||||
685 | $nextpageurl = | ||||
686 | Foswiki::Func::getScriptUrl( $baseWeb, $baseTopic, 'view', | ||||
687 | %new_params ); | ||||
688 | $nextpagebutton = | ||||
689 | $session->templates->expandTemplate('SEARCH:pager_next'); | ||||
690 | } | ||||
691 | %pager_formatting = ( | ||||
692 | '\$previouspage' => sub { return $previousidx }, | ||||
693 | '\$currentpage' => sub { return $showpage }, | ||||
694 | '\$nextpage' => sub { return $showpage + 1 }, | ||||
695 | '\$numberofpages' => sub { return $numberofpages }, | ||||
696 | '\$pagesize' => sub { return $pagesize }, | ||||
697 | '\$previousurl' => sub { return $previouspageurl }, | ||||
698 | '\$nexturl' => sub { return $nextpageurl }, | ||||
699 | '\$sep' => sub { return $sep; } | ||||
700 | ); | ||||
701 | |||||
702 | $previouspagebutton = | ||||
703 | $this->formatCommon( $previouspagebutton, \%pager_formatting ); | ||||
704 | $pager_formatting{'\$previousbutton'} = | ||||
705 | sub { return $previouspagebutton }; | ||||
706 | |||||
707 | $nextpagebutton = | ||||
708 | $this->formatCommon( $nextpagebutton, \%pager_formatting ); | ||||
709 | $pager_formatting{'\$nextbutton'} = sub { return $nextpagebutton }; | ||||
710 | |||||
711 | my $pager_control = ''; | ||||
712 | if ( $numberofpages > 1 ) { | ||||
713 | $pager_control = $params->{pagerformat} | ||||
714 | || $session->templates->expandTemplate('SEARCH:pager'); | ||||
715 | $pager_control = | ||||
716 | $this->formatCommon( $pager_control, \%pager_formatting ); | ||||
717 | } | ||||
718 | $pager_formatting{'\$pager'} = sub { return $pager_control; }; | ||||
719 | } | ||||
720 | |||||
721 | #TODO: multiple is an attribute of the ResultSet | ||||
722 | 41 | 114µs | 41 | 194µs | my $doMultiple = Foswiki::isTrue( $params->{multiple} ); # spent 194µs making 41 calls to Foswiki::isTrue, avg 5µs/call |
723 | 41 | 88µs | 41 | 182µs | my $noEmpty = Foswiki::isTrue( $params->{noempty}, $nonoise ); # spent 182µs making 41 calls to Foswiki::isTrue, avg 4µs/call |
724 | |||||
725 | # Note: a defined header/footer overrides noheader/nofooter | ||||
726 | # To maintain Cairo compatibility we ommit default header/footer if the | ||||
727 | 41 | 65µs | my $noHeader = | ||
728 | !defined($header) && Foswiki::isTrue( $params->{noheader}, $nonoise ) | ||||
729 | || ( !$header && $formatDefined ); | ||||
730 | |||||
731 | 41 | 31µs | my $noFooter = | ||
732 | !defined($footer) && Foswiki::isTrue( $params->{nofooter}, $nonoise ) | ||||
733 | || ( !$footer && $formatDefined ); | ||||
734 | |||||
735 | 41 | 89µs | 41 | 66µs | my $noSummary = Foswiki::isTrue( $params->{nosummary}, $nonoise ); # spent 66µs making 41 calls to Foswiki::isTrue, avg 2µs/call |
736 | 41 | 72µs | 41 | 55µs | my $zeroResults = # spent 55µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
737 | Foswiki::isTrue( ( $params->{zeroresults} ), $nonoise || 1 ); | ||||
738 | 41 | 87µs | 41 | 55µs | my $noTotal = Foswiki::isTrue( $params->{nototal}, $nonoise ); # spent 55µs making 41 calls to Foswiki::isTrue, avg 1µs/call |
739 | 41 | 36µs | my $newLine = $params->{newline} || ''; | ||
740 | 41 | 18µs | my $separator = $params->{separator}; | ||
741 | 41 | 30µs | my $type = $params->{type} || ''; | ||
742 | |||||
743 | # output the list of topics in $web | ||||
744 | 41 | 10µs | my $ntopics = 0; # number of topics in current web | ||
745 | 41 | 5µs | my $nhits = 0; # number of hits (if multiple=on) in current web | ||
746 | 41 | 12µs | my $headerDone = $noHeader; | ||
747 | |||||
748 | 41 | 11µs | my $web = $baseWeb; | ||
749 | 41 | 372µs | 41 | 1.20ms | my $webObject = new Foswiki::Meta( $session, $web ); # spent 1.20ms making 41 calls to Foswiki::Meta::new, avg 29µs/call |
750 | 41 | 18µs | my $lastWebProcessed = ''; | ||
751 | |||||
752 | #total number of topics and hits - not reset when we swap webs | ||||
753 | 41 | 6µs | my $ttopics = 0; | ||
754 | 41 | 9µs | my $thits = 0; | ||
755 | |||||
756 | 41 | 88µs | 41 | 65µs | while ( $infoCache->hasNext() ) { # spent 65µs making 41 calls to Foswiki::Search::ResultSet::hasNext, avg 2µs/call |
757 | 8845 | 14.3ms | 8845 | 51.6ms | my $listItem = $infoCache->next(); # spent 51.6ms making 8845 calls to Foswiki::Search::ResultSet::next, avg 6µs/call |
758 | 8845 | 8.64ms | 8845 | 7.46ms | ASSERT( defined($listItem) ) if DEBUG; # spent 7.46ms making 8845 calls to Assert::ASSERTS_OFF, avg 843ns/call |
759 | |||||
760 | #pager.. | ||||
761 | 8845 | 2.76ms | if ( defined( $params->{pager_skip_results_from} ) | ||
762 | and $params->{pager_skip_results_from} > 0 ) | ||||
763 | { | ||||
764 | $params->{pager_skip_results_from}--; | ||||
765 | next; | ||||
766 | } | ||||
767 | |||||
768 | ############################################################# | ||||
769 | #TOPIC specific | ||||
770 | 8845 | 3.33ms | my $topic = $listItem; | ||
771 | 8845 | 712µs | my $text; #undef means the formatResult() gets it from $info->text; | ||
772 | 8845 | 659µs | my $info; | ||
773 | 8845 | 3.08ms | my @multipleHitLines = (); | ||
774 | 8845 | 47.2ms | 8845 | 7.84ms | if ( # spent 7.84ms making 8845 calls to UNIVERSAL::isa, avg 887ns/call |
775 | ( $infoCache->isa('Foswiki::Search::ResultSet') ) or #SEARCH | ||||
776 | ( $infoCache->isa('Foswiki::Search::InfoCache') ) | ||||
777 | ) #FORMAT | ||||
778 | { | ||||
779 | 8845 | 15.3ms | 8845 | 191ms | ( $web, $topic ) = # spent 191ms making 8845 calls to Foswiki::Func::normalizeWebTopicName, avg 22µs/call |
780 | Foswiki::Func::normalizeWebTopicName( '', $listItem ); | ||||
781 | |||||
782 | # add dependencies (TODO: unclear if this should be before the paging, or after the allowView - sadly, it can't be _in_ the infoCache) | ||||
783 | 8845 | 4.78ms | if ( my $cache = $session->{cache} ) { | ||
784 | $cache->addDependency( $web, $topic ); | ||||
785 | } | ||||
786 | |||||
787 | 8845 | 19.0ms | 17690 | 170ms | $info = $this->metacache->get( $web, $topic ); # spent 157ms making 8845 calls to Foswiki::MetaCache::get, avg 18µs/call
# spent 13.4ms making 8845 calls to Foswiki::Search::metacache, avg 2µs/call |
788 | |||||
789 | # Check security (don't show topics the current user does not have permission to view) | ||||
790 | 8845 | 3.67ms | next unless $info->{allowView}; | ||
791 | |||||
792 | # Special handling for format='...' | ||||
793 | 8845 | 2.33ms | if ($formatDefined) { | ||
794 | 8845 | 12.3ms | 8845 | 45.5ms | $text = $info->{tom}->text(); # spent 45.5ms making 8845 calls to Foswiki::Meta::text, avg 5µs/call |
795 | 8845 | 1.16ms | $text = '' unless defined $text; | ||
796 | |||||
797 | 8845 | 2.01ms | if ($doExpandVars) { | ||
798 | if ( $web eq $baseWeb && $topic eq $baseTopic ) { | ||||
799 | |||||
800 | # primitive way to prevent recursion | ||||
801 | $text =~ s/%SEARCH/%<nop>SEARCH/g; | ||||
802 | } | ||||
803 | $text = $info->{tom}->expandMacros($text); | ||||
804 | } | ||||
805 | } | ||||
806 | |||||
807 | #TODO: should extract this somehow | ||||
808 | |||||
809 | 8845 | 2.53ms | if ( $doMultiple && $query->{tokens} ) { | ||
810 | |||||
811 | #TODO: Sven wonders if this should be a HoistRE.. | ||||
812 | #TODO: well, um, and how does this work for query search? | ||||
813 | my @tokens = @{ $query->{tokens} }; | ||||
814 | my $pattern = $tokens[$#tokens]; # last token in an AND search | ||||
815 | $pattern = quotemeta($pattern) if ( $type ne 'regex' ); | ||||
816 | $text = $info->{tom}->text() unless defined $text; | ||||
817 | $text = '' unless defined $text; | ||||
818 | |||||
819 | if ($caseSensitive) { | ||||
820 | @multipleHitLines = | ||||
821 | reverse grep { /$pattern/ } split( /[\n\r]+/, $text ); | ||||
822 | } | ||||
823 | else { | ||||
824 | @multipleHitLines = | ||||
825 | reverse grep { /$pattern/i } split( /[\n\r]+/, $text ); | ||||
826 | } | ||||
827 | } | ||||
828 | } | ||||
829 | |||||
830 | 8845 | 2.35ms | $ntopics += 1; | ||
831 | 8845 | 1.06ms | $ttopics += 1; | ||
832 | 8845 | 5.33ms | do { # multiple=on loop | ||
833 | |||||
834 | 8845 | 1.17ms | $nhits += 1; | ||
835 | 8845 | 763µs | $thits += 1; | ||
836 | |||||
837 | 8845 | 2.22ms | $text = pop(@multipleHitLines) if ( scalar(@multipleHitLines) ); | ||
838 | |||||
839 | 8845 | 1.62ms | my $justdidHeaderOrFooter = 0; | ||
840 | 8845 | 6.48ms | if ( ( defined( $params->{groupby} ) ) | ||
841 | and ( $params->{groupby} eq 'web' ) ) | ||||
842 | { | ||||
843 | 8845 | 1.87ms | if ( $lastWebProcessed ne $web ) { | ||
844 | |||||
845 | #output the footer for the previous listItem | ||||
846 | 41 | 15µs | if ( $lastWebProcessed ne '' ) { | ||
847 | |||||
848 | #c&p from below | ||||
849 | #TODO: needs refactoring. | ||||
850 | my $processedfooter = $footer; | ||||
851 | if ( not $noTotal ) { | ||||
852 | $processedfooter .= $params->{footercounter}; | ||||
853 | } | ||||
854 | if ( defined($processedfooter) | ||||
855 | and ( $processedfooter ne '' ) ) | ||||
856 | { | ||||
857 | |||||
858 | #footer comes before result | ||||
859 | $ntopics--; | ||||
860 | $nhits--; | ||||
861 | |||||
862 | #because $pager contains more $ntopics like format strings, it needs to be expanded first. | ||||
863 | $processedfooter = | ||||
864 | $this->formatCommon( $processedfooter, | ||||
865 | \%pager_formatting ); | ||||
866 | $processedfooter =~ s/\$web/$lastWebProcessed/gos | ||||
867 | ; # expand name of web | ||||
868 | |||||
869 | # $processedfooter =~ | ||||
870 | # s/([^\n])$/$1\n/os; # add new line at end | ||||
871 | # output footer of $web | ||||
872 | |||||
873 | $processedfooter =~ s/\$ntopics/$ntopics/gs; | ||||
874 | $processedfooter =~ s/\$nhits/$nhits/gs; | ||||
875 | $processedfooter =~ s/\$index/$thits/gs; | ||||
876 | |||||
877 | #legacy SEARCH counter support | ||||
878 | $processedfooter =~ s/%NTOPICS%/$ntopics/go; | ||||
879 | |||||
880 | $processedfooter = | ||||
881 | $this->formatCommon( $processedfooter, | ||||
882 | \%pager_formatting ); | ||||
883 | |||||
884 | # $processedfooter =~ | ||||
885 | # s/\n$//os; # remove trailing new line | ||||
886 | |||||
887 | $justdidHeaderOrFooter = 1; | ||||
888 | &$callback( $cbdata, $processedfooter ); | ||||
889 | |||||
890 | #go back to counting results | ||||
891 | $ntopics++; | ||||
892 | $nhits++; | ||||
893 | } | ||||
894 | } | ||||
895 | |||||
896 | #trigger a header for this new web | ||||
897 | 41 | 9µs | $headerDone = undef; | ||
898 | } | ||||
899 | } | ||||
900 | |||||
901 | 8845 | 1.30ms | if ( $lastWebProcessed ne $web ) { | ||
902 | 41 | 177µs | 41 | 627µs | $webObject = new Foswiki::Meta( $session, $web ); # spent 627µs making 41 calls to Foswiki::Meta::new, avg 15µs/call |
903 | 41 | 8µs | $lastWebProcessed = $web; | ||
904 | |||||
905 | #reset our web partitioned legacy counts | ||||
906 | 41 | 14µs | $ntopics = 1; | ||
907 | 41 | 11µs | $nhits = 1; | ||
908 | } | ||||
909 | |||||
910 | # lazy output of header (only if needed for the first time) | ||||
911 | 8845 | 3.05ms | if ( ( !$headerDone and ( defined($header) ) ) | ||
912 | and ( $header ne '' ) ) | ||||
913 | { | ||||
914 | |||||
915 | my $processedheader = $header; | ||||
916 | |||||
917 | # because $pager contains more $ntopics like format | ||||
918 | # strings, it needs to be expanded first. | ||||
919 | $processedheader = | ||||
920 | $this->formatCommon( $processedheader, \%pager_formatting ); | ||||
921 | $processedheader =~ s/\$web/$web/gos; # expand name of web | ||||
922 | |||||
923 | # add new line after the header unless separator is defined | ||||
924 | # per Item1773 / SearchSeparatorDefaultHeaderFooter | ||||
925 | unless ( defined $separator ) { | ||||
926 | $processedheader =~ s/([^\n])$/$1\n/os; | ||||
927 | } | ||||
928 | |||||
929 | $headerDone = 1; | ||||
930 | my $thisWebBGColor = $webObject->getPreference('WEBBGCOLOR') | ||||
931 | || '\#FF00FF'; | ||||
932 | $processedheader =~ s/%WEBBGCOLOR%/$thisWebBGColor/go; | ||||
933 | $processedheader =~ s/%WEB%/$web/go; | ||||
934 | $processedheader =~ s/\$ntopics/($ntopics-1)/gse; | ||||
935 | $processedheader =~ s/\$nhits/($nhits-1)/gse; | ||||
936 | $processedheader =~ s/\$index/($thits-1)/gs; | ||||
937 | $processedheader = | ||||
938 | $this->formatCommon( $processedheader, \%pager_formatting ); | ||||
939 | &$callback( $cbdata, $processedheader ); | ||||
940 | $justdidHeaderOrFooter = 1; | ||||
941 | } | ||||
942 | |||||
943 | 8845 | 1.36ms | 84 | 732µs | if ( defined($separator) # spent 732µs making 84 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1336], avg 9µs/call |
944 | and ( $thits > 1 ) | ||||
945 | and ( $justdidHeaderOrFooter != 1 ) ) | ||||
946 | { | ||||
947 | &$callback( $cbdata, $separator ); | ||||
948 | } | ||||
949 | |||||
950 | ###################Render the result | ||||
951 | 8845 | 870µs | my $out; | ||
952 | 8845 | 3.47ms | if ( $formatDefined and ( $format ne '' ) ) { | ||
953 | |||||
954 | #TODO: hack to convert a bad SEARCH format to the one used by getRevInfo.. | ||||
955 | 85 | 34µs | $format =~ s/\$createdate/\$createlongdate/gs; | ||
956 | |||||
957 | #it looks like $isodate in format is equive to $iso in renderRevisionInfo | ||||
958 | #TODO: clean these 3 hacks up | ||||
959 | 85 | 16µs | $format =~ s/\$isodate/\$iso/gs; | ||
960 | 85 | 45µs | $format =~ s/\%TIME\%/\$date/gs; | ||
961 | 85 | 23µs | $format =~ s/\$date/\$longdate/gs; | ||
962 | |||||
963 | #other tmpl based renderings | ||||
964 | 85 | 20µs | $format =~ s/%WEB%/\$web/go; | ||
965 | 85 | 14µs | $format =~ s/%TOPICNAME%/\$topic/go; | ||
966 | 85 | 15µs | $format =~ s/%AUTHOR%/\$wikiusername/g; | ||
967 | |||||
968 | # pass search options to summary parser | ||||
969 | 85 | 250µs | my $searchOptions = { | ||
970 | type => $params->{type}, | ||||
971 | wordboundaries => $params->{wordboundaries}, | ||||
972 | casesensitive => $caseSensitive, | ||||
973 | tokens => $query->{tokens} | ||||
974 | }; | ||||
975 | |||||
976 | #TODO: why is this not part of the callback? at least the non-result element format strings can be common here. | ||||
977 | #or do i need a formatCommon sub that formatResult can also call.. (which then goes into the callback? | ||||
978 | $out = $this->formatResult( | ||||
979 | $format, | ||||
980 | $info->{tom} || $webObject, #SMELL: horrid hack | ||||
981 | $text, | ||||
982 | $searchOptions, | ||||
983 | { | ||||
984 | '\$ntopics' => sub { return $ntopics }, | ||||
985 | '\$nhits' => sub { return $nhits }, | ||||
986 | '\$index' => sub { return $thits }, | ||||
987 | '\$item' => sub { return $listItem }, | ||||
988 | |||||
989 | %pager_formatting, | ||||
990 | |||||
991 | #rev1 info | ||||
992 | #TODO: move the $create* formats into Render::renderRevisionInfo.. | ||||
993 | #which implies moving the infocache's pre-extracted data into the tom obj too. | ||||
994 | # $out =~ s/\$create(longdate|username|wikiname|wikiusername)/ | ||||
995 | # $infoCache->getRev1Info( $topic, "create$1" )/ges; | ||||
996 | '\$createlongdate' => sub { | ||||
997 | return $this->metacache->get( $web, $topic )->{tom} | ||||
998 | ->getRev1Info("createlongdate"); | ||||
999 | }, | ||||
1000 | '\$createusername' => sub { | ||||
1001 | return $this->metacache->get( $web, $topic )->{tom} | ||||
1002 | ->getRev1Info("createusername"); | ||||
1003 | }, | ||||
1004 | '\$createwikiname' => sub { | ||||
1005 | return $this->metacache->get( $web, $topic )->{tom} | ||||
1006 | ->getRev1Info("createwikiname"); | ||||
1007 | }, | ||||
1008 | '\$createwikiusername' => sub { | ||||
1009 | return $this->metacache->get( $web, $topic )->{tom} | ||||
1010 | ->getRev1Info("createwikiusername"); | ||||
1011 | }, | ||||
1012 | |||||
1013 | #TODO: hacky bits that need to be moved out of formatResult() | ||||
1014 | 85 | 250µs | # spent 143µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1014] which was called 85 times, avg 2µs/call:
# 85 times (143µs+0s) by Foswiki::Search::formatResult at line 1128, avg 2µs/call | ||
1015 | 85 | 205µs | # spent 93µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1015] which was called 85 times, avg 1µs/call:
# 85 times (93µs+0s) by Foswiki::Search::formatResult at line 1129, avg 1µs/call | ||
1016 | 85 | 198µs | # spent 88µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1016] which was called 85 times, avg 1µs/call:
# 85 times (88µs+0s) by Foswiki::Search::formatResult at line 1130, avg 1µs/call | ||
1017 | 85 | 197µs | # spent 88µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1017] which was called 85 times, avg 1µs/call:
# 85 times (88µs+0s) by Foswiki::Search::formatResult at line 1131, avg 1µs/call | ||
1018 | 85 | 195µs | # spent 92µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1018] which was called 85 times, avg 1µs/call:
# 85 times (92µs+0s) by Foswiki::Search::formatResult at line 1132, avg 1µs/call | ||
1019 | 85 | 191µs | # spent 88µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1019] which was called 85 times, avg 1µs/call:
# 85 times (88µs+0s) by Foswiki::Search::formatResult at line 1133, avg 1µs/call | ||
1020 | 85 | 182µs | # spent 79µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1020] which was called 85 times, avg 934ns/call:
# 85 times (79µs+0s) by Foswiki::Search::formatResult at line 1134, avg 934ns/call | ||
1021 | 85 | 183µs | # spent 71µs within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1021] which was called 85 times, avg 835ns/call:
# 85 times (71µs+0s) by Foswiki::Search::formatResult at line 1135, avg 835ns/call | ||
1022 | } | ||||
1023 | 85 | 2.48ms | 85 | 9.65ms | ); # spent 9.65ms making 85 calls to Foswiki::Search::formatResult, avg 113µs/call |
1024 | } | ||||
1025 | else { | ||||
1026 | 8760 | 1.94ms | $out = ''; | ||
1027 | } | ||||
1028 | |||||
1029 | 8845 | 9.85ms | 8845 | 14.7ms | &$callback( $cbdata, $out ); # spent 13.8ms making 8760 calls to Foswiki::Search::_collate_to_list, avg 2µs/call
# spent 916µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1336], avg 11µs/call |
1030 | } while (@multipleHitLines); # multiple=on loop | ||||
1031 | |||||
1032 | 8845 | 20.0ms | 8845 | 191ms | if ( # spent 191ms making 8845 calls to Foswiki::Search::ResultSet::hasNext, avg 22µs/call |
1033 | ( defined( $params->{pager_skip_results_from} ) ) | ||||
1034 | or ( ( defined( $params->{groupby} ) ) | ||||
1035 | and ( $params->{groupby} ne 'web' ) ) | ||||
1036 | ) | ||||
1037 | { | ||||
1038 | last if ( $ttopics >= $limit ); | ||||
1039 | } | ||||
1040 | else { | ||||
1041 | 8845 | 1.67ms | if ( $ntopics >= $limit ) { | ||
1042 | $infoCache->nextWeb(); | ||||
1043 | } | ||||
1044 | } | ||||
1045 | } # end topic loop | ||||
1046 | |||||
1047 | # output footer only if hits in web | ||||
1048 | 41 | 52µs | if ( $ntopics == 0 ) { | ||
1049 | if ( $zeroResults and not $noTotal ) { | ||||
1050 | $footer = $params->{footercounter}; | ||||
1051 | } | ||||
1052 | else { | ||||
1053 | $footer = ''; | ||||
1054 | } | ||||
1055 | ##MOVEDUP $webObject = new Foswiki::Meta( $session, $baseWeb ); | ||||
1056 | } | ||||
1057 | else { | ||||
1058 | 41 | 26µs | if ( ( not $noTotal ) and ( defined( $params->{footercounter} ) ) ) { | ||
1059 | $footer .= $params->{footercounter}; | ||||
1060 | } | ||||
1061 | |||||
1062 | 41 | 27µs | if ( ( defined( $params->{pager} ) ) and ( $params->{pager} eq 'on' ) ) | ||
1063 | { | ||||
1064 | $footer .= '$pager'; | ||||
1065 | } | ||||
1066 | } | ||||
1067 | 41 | 27µs | if ( defined $footer ) { | ||
1068 | |||||
1069 | #because $pager contains more $ntopics like format strings, it needs to be expanded first. | ||||
1070 | 41 | 180µs | 41 | 293µs | $footer = $this->formatCommon( $footer, \%pager_formatting ); # spent 293µs making 41 calls to Foswiki::Search::formatCommon, avg 7µs/call |
1071 | 41 | 81µs | $footer =~ s/\$web/$web/gos; # expand name of web | ||
1072 | |||||
1073 | # $footer =~ s/([^\n])$/$1\n/os; # add new line at end | ||||
1074 | |||||
1075 | # output footer of $web | ||||
1076 | 41 | 21µs | $footer =~ s/\$ntopics/$ntopics/gs; | ||
1077 | 41 | 113µs | $footer =~ s/\$nhits/$nhits/gs; | ||
1078 | 41 | 15µs | $footer =~ s/\$index/$thits/gs; | ||
1079 | |||||
1080 | #legacy SEARCH counter support | ||||
1081 | 41 | 23µs | $footer =~ s/%NTOPICS%/$ntopics/go; | ||
1082 | |||||
1083 | # $footer =~ s/\n$//os; # remove trailing new line | ||||
1084 | |||||
1085 | 41 | 63µs | 41 | 76µs | &$callback( $cbdata, $footer ); # spent 70µs making 40 calls to Foswiki::Search::_collate_to_list, avg 2µs/call
# spent 6µs making 1 call to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1336] |
1086 | } | ||||
1087 | |||||
1088 | 41 | 606µs | return ( $ntopics, | ||
1089 | ( ( not defined( $params->{_callback} ) ) and ( $nhits >= 0 ) ) | ||||
1090 | ? join( '', @$cbdata ) | ||||
1091 | : '' ); | ||||
1092 | } | ||||
1093 | |||||
1094 | # spent 293µs within Foswiki::Search::formatCommon which was called 41 times, avg 7µs/call:
# 41 times (293µs+0s) by Foswiki::Search::formatResults at line 1070, avg 7µs/call | ||||
1095 | 41 | 42µs | my ( $this, $out, $customKeys ) = @_; | ||
1096 | |||||
1097 | 41 | 31µs | my $session = $this->{session}; | ||
1098 | |||||
1099 | 41 | 87µs | foreach my $key ( keys(%$customKeys) ) { | ||
1100 | $out =~ s/$key/&{$customKeys->{$key}}()/ges; | ||||
1101 | } | ||||
1102 | 41 | 144µs | return $out; | ||
1103 | } | ||||
1104 | |||||
1105 | =begin TML | ||||
1106 | |||||
1107 | ---++ ObjectMethod formatResult | ||||
1108 | * $text can be undefined. | ||||
1109 | * $searchOptions is an options hash to pass on to the summary parser | ||||
1110 | * customKeys is a hash of {'$key' => sub {my $item = shift; return value;} } | ||||
1111 | where $item is a tom object (initially a Foswiki::Meta, but I'd like to be more generic) | ||||
1112 | |||||
1113 | TODO: i don't really know what we'll need to do about order of processing. | ||||
1114 | TODO: at minimum, the keys need to be sorted by length so that $datatime is processed before $date | ||||
1115 | TODO: need to cater for $summary(params) style too | ||||
1116 | |||||
1117 | =cut | ||||
1118 | |||||
1119 | # spent 9.65ms (7.71+1.94) within Foswiki::Search::formatResult which was called 85 times, avg 113µs/call:
# 85 times (7.71ms+1.94ms) by Foswiki::Search::formatResults at line 1023, avg 113µs/call | ||||
1120 | 85 | 97µs | my ( $this, $out, $topicObject, $text, $searchOptions, $customKeys ) = @_; | ||
1121 | |||||
1122 | 85 | 36µs | my $session = $this->{session}; | ||
1123 | |||||
1124 | 85 | 155µs | 85 | 215µs | my $web = $topicObject->web(); # spent 215µs making 85 calls to Foswiki::Meta::web, avg 3µs/call |
1125 | 85 | 137µs | 85 | 183µs | my $topic = $topicObject->topic(); # spent 183µs making 85 calls to Foswiki::Meta::topic, avg 2µs/call |
1126 | |||||
1127 | #TODO: these need to go away. | ||||
1128 | 85 | 161µs | 85 | 143µs | my $revNum = &{ $customKeys->{'$revNum'} }(); # spent 143µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1014], avg 2µs/call |
1129 | 85 | 135µs | 85 | 93µs | my $doBookView = &{ $customKeys->{'$doBookView'} }(); # spent 93µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1015], avg 1µs/call |
1130 | 85 | 124µs | 85 | 88µs | my $baseWeb = &{ $customKeys->{'$baseWeb'} }(); # spent 88µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1016], avg 1µs/call |
1131 | 85 | 118µs | 85 | 88µs | my $baseTopic = &{ $customKeys->{'$baseTopic'} }(); # spent 88µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1017], avg 1µs/call |
1132 | 85 | 123µs | 85 | 92µs | my $newLine = &{ $customKeys->{'$newLine'} }(); # spent 92µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1018], avg 1µs/call |
1133 | 85 | 128µs | 85 | 88µs | my $separator = &{ $customKeys->{'$separator'} }(); # spent 88µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1019], avg 1µs/call |
1134 | 85 | 116µs | 85 | 79µs | my $noTotal = &{ $customKeys->{'$noTotal'} }(); # spent 79µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1020], avg 934ns/call |
1135 | 85 | 105µs | 85 | 71µs | my $params = &{ $customKeys->{'$paramsHash'} }(); # spent 71µs making 85 calls to Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1021], avg 835ns/call |
1136 | 85 | 115µs | foreach my $key ( | ||
1137 | '$revNum', '$doBookView', '$baseWeb', '$baseTopic', | ||||
1138 | '$newLine', '$separator', '$noTotal', '$paramsHash' | ||||
1139 | ) | ||||
1140 | { | ||||
1141 | 680 | 413µs | delete $customKeys->{$key}; | ||
1142 | } | ||||
1143 | |||||
1144 | #SMELL: Because hash order is not predictable, any user supplied | ||||
1145 | # format which might contain other $format tokens must be expanded first, | ||||
1146 | # otherwise the results are unpredictable. | ||||
1147 | # For example, pagerformat contains $ntopics. If $ntopics expands first, | ||||
1148 | # then pagerformat, then ntopics will remain un-expanded in the output. | ||||
1149 | # Since key processing order is unpredictable, add any potentially nested | ||||
1150 | # keys here | ||||
1151 | 85 | 98µs | foreach my $key ('\$pager') { | ||
1152 | 85 | 61µs | next unless defined $customKeys->{$key}; | ||
1153 | $out =~ s/$key/&{$customKeys->{$key}}()/ges; | ||||
1154 | delete $customKeys->{$key}; | ||||
1155 | } | ||||
1156 | |||||
1157 | 85 | 260µs | foreach my $key ( keys(%$customKeys) ) { | ||
1158 | 680 | 2.82ms | $out =~ s/$key/&{$customKeys->{$key}}()/ges; | ||
1159 | } | ||||
1160 | |||||
1161 | #SMELL: hack to stop non-topic based FORMAT's from doing topic code | ||||
1162 | #TODO: this should be extracted into the customKeys above | ||||
1163 | # Note that we cannot send a formatted search through renderRevisionInfo | ||||
1164 | # without expanding tokens we should not because the function also sends | ||||
1165 | # the input through formatTime and probably other nasty filters | ||||
1166 | # So we send each token through one by one. | ||||
1167 | 85 | 38µs | if ( defined $topic ) { | ||
1168 | |||||
1169 | 85 | 33µs | $out =~ s/\$web/$web/gs; | ||
1170 | 85 | 17µs | $out =~ s/\$topic\(([^\)]*)\)/Foswiki::Render::breakName( | ||
1171 | $topic, $1 )/ges; | ||||
1172 | 85 | 278µs | $out =~ s/\$topic/$topic/gs; | ||
1173 | $out =~ | ||||
1174 | 85 | 40µs | s{(\$rev|\$wikiusername|\$wikiname|\$username|\$createlongdate|\$iso|\$longdate|\$date)} | ||
1175 | {$session->renderer->renderRevisionInfo($topicObject, $revNum, $1 )}ges; | ||||
1176 | } | ||||
1177 | |||||
1178 | 85 | 58µs | if ( $out =~ m/\$text/ and defined($topic) ) | ||
1179 | { #TODO: don't muck with text if we're not even a topic | ||||
1180 | $text = $topicObject->text() unless defined $text; | ||||
1181 | $text = '' unless defined $text; | ||||
1182 | |||||
1183 | if ( $topic eq $session->{topicName} ) { | ||||
1184 | |||||
1185 | #TODO: extract the diffusion and generalise to whatever MACRO we are processing - anything with a format can loop | ||||
1186 | |||||
1187 | # defuse SEARCH in current topic to prevent loop | ||||
1188 | $text =~ s/%SEARCH{.*?}%/SEARCH{...}/go; | ||||
1189 | } | ||||
1190 | $out =~ s/\$text/$text/gos; | ||||
1191 | } | ||||
1192 | |||||
1193 | #TODO: extract the rev | ||||
1194 | 85 | 58µs | my $srev = 'r' . $revNum; | ||
1195 | 85 | 172µs | 36 | 798µs | if ( $revNum eq '0' || $revNum eq '1' ) { # spent 690µs making 12 calls to CGI::span, avg 57µs/call
# spent 64µs making 12 calls to Foswiki::I18N::Fallback::maketext, avg 5µs/call
# spent 44µs making 12 calls to Foswiki::i18n, avg 4µs/call |
1196 | $srev = CGI::span( { class => 'foswikiNew' }, | ||||
1197 | ( $session->i18n->maketext('NEW') ) ); | ||||
1198 | } | ||||
1199 | 85 | 39µs | $out =~ s/%REVISION%/$srev/o; | ||
1200 | |||||
1201 | 85 | 33µs | if ($doBookView) { | ||
1202 | |||||
1203 | # BookView | ||||
1204 | $text = $topicObject->text() unless defined $text; | ||||
1205 | $text = '' unless defined $text; | ||||
1206 | |||||
1207 | if ( $web eq $baseWeb && $topic eq $baseTopic ) { | ||||
1208 | |||||
1209 | # primitive way to prevent recursion | ||||
1210 | $text =~ s/%SEARCH/%<nop>SEARCH/g; | ||||
1211 | } | ||||
1212 | $text = $topicObject->expandMacros($text); | ||||
1213 | $text = $topicObject->renderTML($text); | ||||
1214 | |||||
1215 | $out =~ s/%TEXTHEAD%/$text/go; | ||||
1216 | |||||
1217 | } | ||||
1218 | else { | ||||
1219 | |||||
1220 | #TODO: more topic specific bits | ||||
1221 | 85 | 34µs | if ( defined($topic) ) { | ||
1222 | 85 | 31µs | $out =~ s/\$summary(?:\(([^\)]*)\))?/ | ||
1223 | $topicObject->summariseText( $1, $text, $searchOptions )/ges; | ||||
1224 | 85 | 25µs | $out =~ s/\$changes(?:\(([^\)]*)\))?/ | ||
1225 | $topicObject->summariseChanges(Foswiki::Store::cleanUpRevID($1), $revNum)/ges; | ||||
1226 | 85 | 22µs | $out =~ s/\$formfield\(\s*([^\)]*)\s*\)/ | ||
1227 | displayFormField( $topicObject, $1 )/ges; | ||||
1228 | 85 | 23µs | $out =~ s/\$parent\(([^\)]*)\)/ | ||
1229 | Foswiki::Render::breakName( | ||||
1230 | $topicObject->getParent(), $1 )/ges; | ||||
1231 | 85 | 29µs | $out =~ s/\$parent/$topicObject->getParent()/ges; | ||
1232 | 85 | 22µs | $out =~ s/\$formname/$topicObject->getFormName()/ges; | ||
1233 | 85 | 27µs | $out =~ s/\$count\((.*?\s*\.\*)\)/_countPattern( $text, $1 )/ges; | ||
1234 | |||||
1235 | # FIXME: Allow all regex characters but escape them | ||||
1236 | # Note: The RE requires a .* at the end of a pattern to avoid false positives | ||||
1237 | # in pattern matching | ||||
1238 | $out =~ | ||||
1239 | 85 | 26µs | s/\$pattern\((.*?\s*\.\*)\)/_extractPattern( $text, $1 )/ges; | ||
1240 | } | ||||
1241 | 85 | 14µs | $out =~ s/\r?\n/$newLine/gos if ($newLine); | ||
1242 | |||||
1243 | # If separator is not defined we default to \n | ||||
1244 | # We also add new line after last search result but before footer | ||||
1245 | # when separator is not defined for backwards compatibility | ||||
1246 | # per Item1773 / SearchSeparatorDefaultHeaderFooter | ||||
1247 | 85 | 21µs | if ( !defined($separator) ) { | ||
1248 | unless ( $noTotal && !$params->{formatdefined} ) { | ||||
1249 | $out =~ s/([^\n])$/$1\n/s; | ||||
1250 | } | ||||
1251 | } | ||||
1252 | } | ||||
1253 | |||||
1254 | #see http://foswiki.org/Tasks/Item2371 - needs unit test exploration | ||||
1255 | #the problem is that when I separated the formating from the searching, I set the format string to what is in the template, | ||||
1256 | #and thus here, format is always set. | ||||
1257 | # elsif ($noSummary) { | ||||
1258 | # #TODO: i think that means I've broken SEARCH{nosummary=on" with no format specified | ||||
1259 | # $out =~ s/%TEXTHEAD%//go; | ||||
1260 | # $out =~ s/ //go; | ||||
1261 | # } | ||||
1262 | # else { | ||||
1263 | # #SEARCH with no format and nonoise="off" or nosummary="off" | ||||
1264 | # #TODO: BROKEN, need to fix the meaning of nosummary and nonoise in SEARCH | ||||
1265 | # # regular search view | ||||
1266 | # $text = $info->{tom}->summariseText( '', $text ); | ||||
1267 | # $out =~ s/%TEXTHEAD%/$text/go; | ||||
1268 | # } | ||||
1269 | 85 | 392µs | return $out; | ||
1270 | } | ||||
1271 | |||||
1272 | =begin TML | ||||
1273 | |||||
1274 | ---++ StaticMethod displayFormField( $meta, $args ) -> $text | ||||
1275 | |||||
1276 | Parse the arguments to a $formfield specification and extract | ||||
1277 | the relevant formfield from the given meta data. | ||||
1278 | |||||
1279 | * =args= string containing name of form field | ||||
1280 | |||||
1281 | In addition to the name of a field =args= can be appended with a commas | ||||
1282 | followed by a string format (\d+)([,\s*]\.\.\.)?). This supports the formatted | ||||
1283 | search function $formfield and is used to shorten the returned string or a | ||||
1284 | hyphenated string. | ||||
1285 | |||||
1286 | =cut | ||||
1287 | |||||
1288 | sub displayFormField { | ||||
1289 | my ( $meta, $args ) = @_; | ||||
1290 | |||||
1291 | my ( $name, @params ) = split( /,\s*/, $args ); | ||||
1292 | my $displayedVal = 0; # default is to show the unmapped value | ||||
1293 | my $breakArgs = ''; | ||||
1294 | if (@params) { | ||||
1295 | if ( $params[0] eq 'display' ) { | ||||
1296 | |||||
1297 | # The displayed value is required | ||||
1298 | $displayedVal = 1; | ||||
1299 | shift @params; | ||||
1300 | } | ||||
1301 | $breakArgs = join( ',', @params ); | ||||
1302 | } | ||||
1303 | |||||
1304 | # SMELL: this is a *terrible* idea. Not only does it munge the result | ||||
1305 | # so it can only be used for display, it also munges it such that it | ||||
1306 | # can't be repaired by the options on %SEARCH. 'nomap' goes some | ||||
1307 | # way to curing the problem, but it's still not ideal :-( | ||||
1308 | return $meta->renderFormFieldForDisplay( | ||||
1309 | $name, '$value', | ||||
1310 | { | ||||
1311 | break => $breakArgs, | ||||
1312 | protectdollar => 1, | ||||
1313 | showhidden => 1, | ||||
1314 | display => $displayedVal | ||||
1315 | } | ||||
1316 | ); | ||||
1317 | } | ||||
1318 | |||||
1319 | #my ($callback, $cbdata) = setup_callback(\%params, $baseWebObject); | ||||
1320 | sub setup_callback { | ||||
1321 | 82 | 51µs | my ( $params, $webObj ) = @_; | ||
1322 | |||||
1323 | 82 | 53µs | my $callback = $params->{_callback}; | ||
1324 | 82 | 28µs | my $cbdata = $params->{_cbdata}; | ||
1325 | |||||
1326 | #add in the rendering.. | ||||
1327 | 82 | 69µs | if ( defined( $params->{_callback} ) ) { | ||
1328 | # spent 1.65ms (1.24+418µs) within Foswiki::Search::__ANON__[/var/www/foswiki11/lib/Foswiki/Search.pm:1336] which was called 170 times, avg 10µs/call:
# 85 times (661µs+255µs) by Foswiki::Search::formatResults at line 1029, avg 11µs/call
# 84 times (570µs+161µs) by Foswiki::Search::formatResults at line 943, avg 9µs/call
# once (4µs+1µs) by Foswiki::Search::formatResults at line 1085 | ||||
1329 | 170 | 58µs | my $cbdata = shift; | ||
1330 | 170 | 43µs | my $text = shift; | ||
1331 | 170 | 101µs | my $oldcallback = $params->{_callback}; | ||
1332 | |||||
1333 | 170 | 29µs | $text = $webObj->renderTML($text) if defined($webObj); | ||
1334 | 170 | 112µs | $text =~ s|</*nop/*>||goi; # remove <nop> tag | ||
1335 | 170 | 650µs | 170 | 418µs | &$oldcallback( $cbdata, $text ); # spent 418µs making 170 calls to Foswiki::Users::TopicUserMapping::_collateGroups, avg 2µs/call |
1336 | 2 | 13µs | }; | ||
1337 | } | ||||
1338 | else { | ||||
1339 | 80 | 46µs | $cbdata = $params->{_cbdata} = [] unless ( defined($cbdata) ); | ||
1340 | 80 | 56µs | $callback = \&_collate_to_list; | ||
1341 | } | ||||
1342 | 82 | 268µs | return ( $callback, $cbdata ); | ||
1343 | } | ||||
1344 | |||||
1345 | # callback for search function to collate to list | ||||
1346 | sub _collate_to_list { | ||||
1347 | 8800 | 2.06ms | my $ref = shift; | ||
1348 | |||||
1349 | 8800 | 28.5ms | push( @$ref, @_ ); | ||
1350 | } | ||||
1351 | |||||
1352 | 1 | 2µs | 1; | ||
1353 | __END__ |