← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:15 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Search.pm
StatementsExecuted 567737 statements in 680ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
4011516ms28.3sFoswiki::Search::::formatResultsFoswiki::Search::formatResults
700837599.9ms99.9msFoswiki::Search::::metacacheFoswiki::Search::metacache
88002117.1ms17.1msFoswiki::Search::::_collate_to_listFoswiki::Search::_collate_to_list
401113.8ms137sFoswiki::Search::::searchWebFoswiki::Search::searchWeb
40112.25ms114msFoswiki::Search::::loadTemplatesFoswiki::Search::loadTemplates
1112.06ms2.90msFoswiki::Search::::BEGIN@19Foswiki::Search::BEGIN@19
1111.27ms1.42msFoswiki::Search::::BEGIN@26Foswiki::Search::BEGIN@26
40111.23ms13.9msFoswiki::Search::::parseSearchFoswiki::Search::parseSearch
1111.01ms1.10msFoswiki::Search::::BEGIN@20Foswiki::Search::BEGIN@20
111861µs952µsFoswiki::Search::::BEGIN@23Foswiki::Search::BEGIN@23
8021552µs552µsFoswiki::Search::::setup_callbackFoswiki::Search::setup_callback
111442µs488µsFoswiki::Search::::BEGIN@25Foswiki::Search::BEGIN@25
4011282µs282µsFoswiki::Search::::formatCommonFoswiki::Search::formatCommon
4011227µs11.2msFoswiki::Search::::__ANON__[:134]Foswiki::Search::__ANON__[:134]
11176µs6.10msFoswiki::Search::::finishFoswiki::Search::finish
11115µs15µsFoswiki::Search::::newFoswiki::Search::new
11115µs27µsFoswiki::Search::::BEGIN@12Foswiki::Search::BEGIN@12
11110µs14µsFoswiki::Search::::BEGIN@13Foswiki::Search::BEGIN@13
1119µs33µsFoswiki::Search::::BEGIN@14Foswiki::Search::BEGIN@14
1119µs39µsFoswiki::Search::::BEGIN@29Foswiki::Search::BEGIN@29
1118µs111µsFoswiki::Search::::BEGIN@15Foswiki::Search::BEGIN@15
1115µs5µsFoswiki::Search::::BEGIN@24Foswiki::Search::BEGIN@24
1115µs5µsFoswiki::Search::::BEGIN@21Foswiki::Search::BEGIN@21
1115µs5µsFoswiki::Search::::BEGIN@17Foswiki::Search::BEGIN@17
1115µs5µsFoswiki::Search::::BEGIN@27Foswiki::Search::BEGIN@27
1115µs5µsFoswiki::Search::::BEGIN@31Foswiki::Search::BEGIN@31
1113µs3µsFoswiki::Search::::BEGIN@22Foswiki::Search::BEGIN@22
1113µs3µsFoswiki::Search::::BEGIN@18Foswiki::Search::BEGIN@18
0000s0sFoswiki::Search::::__ANON__[:1013]Foswiki::Search::__ANON__[:1013]
0000s0sFoswiki::Search::::__ANON__[:1014]Foswiki::Search::__ANON__[:1014]
0000s0sFoswiki::Search::::__ANON__[:1015]Foswiki::Search::__ANON__[:1015]
0000s0sFoswiki::Search::::__ANON__[:1016]Foswiki::Search::__ANON__[:1016]
0000s0sFoswiki::Search::::__ANON__[:1020]Foswiki::Search::__ANON__[:1020]
0000s0sFoswiki::Search::::__ANON__[:1021]Foswiki::Search::__ANON__[:1021]
0000s0sFoswiki::Search::::__ANON__[:1022]Foswiki::Search::__ANON__[:1022]
0000s0sFoswiki::Search::::__ANON__[:1023]Foswiki::Search::__ANON__[:1023]
0000s0sFoswiki::Search::::__ANON__[:1024]Foswiki::Search::__ANON__[:1024]
0000s0sFoswiki::Search::::__ANON__[:1025]Foswiki::Search::__ANON__[:1025]
0000s0sFoswiki::Search::::__ANON__[:1026]Foswiki::Search::__ANON__[:1026]
0000s0sFoswiki::Search::::__ANON__[:1027]Foswiki::Search::__ANON__[:1027]
0000s0sFoswiki::Search::::__ANON__[:1028]Foswiki::Search::__ANON__[:1028]
0000s0sFoswiki::Search::::__ANON__[:1044]Foswiki::Search::__ANON__[:1044]
0000s0sFoswiki::Search::::__ANON__[:1053]Foswiki::Search::__ANON__[:1053]
0000s0sFoswiki::Search::::__ANON__[:1398]Foswiki::Search::__ANON__[:1398]
0000s0sFoswiki::Search::::__ANON__[:139]Foswiki::Search::__ANON__[:139]
0000s0sFoswiki::Search::::__ANON__[:191]Foswiki::Search::__ANON__[:191]
0000s0sFoswiki::Search::::__ANON__[:194]Foswiki::Search::__ANON__[:194]
0000s0sFoswiki::Search::::__ANON__[:649]Foswiki::Search::__ANON__[:649]
0000s0sFoswiki::Search::::__ANON__[:650]Foswiki::Search::__ANON__[:650]
0000s0sFoswiki::Search::::__ANON__[:651]Foswiki::Search::__ANON__[:651]
0000s0sFoswiki::Search::::__ANON__[:652]Foswiki::Search::__ANON__[:652]
0000s0sFoswiki::Search::::__ANON__[:653]Foswiki::Search::__ANON__[:653]
0000s0sFoswiki::Search::::__ANON__[:654]Foswiki::Search::__ANON__[:654]
0000s0sFoswiki::Search::::__ANON__[:666]Foswiki::Search::__ANON__[:666]
0000s0sFoswiki::Search::::__ANON__[:678]Foswiki::Search::__ANON__[:678]
0000s0sFoswiki::Search::::__ANON__[:689]Foswiki::Search::__ANON__[:689]
0000s0sFoswiki::Search::::__ANON__[:701]Foswiki::Search::__ANON__[:701]
0000s0sFoswiki::Search::::__ANON__[:712]Foswiki::Search::__ANON__[:712]
0000s0sFoswiki::Search::::__ANON__[:968]Foswiki::Search::__ANON__[:968]
0000s0sFoswiki::Search::::__ANON__[:975]Foswiki::Search::__ANON__[:975]
0000s0sFoswiki::Search::::_countPatternFoswiki::Search::_countPattern
0000s0sFoswiki::Search::::_extractPatternFoswiki::Search::_extractPattern
0000s0sFoswiki::Search::::_isSetTrueFoswiki::Search::_isSetTrue
0000s0sFoswiki::Search::::displayFormFieldFoswiki::Search::displayFormField
0000s0sFoswiki::Search::::formatResultFoswiki::Search::formatResult
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2package Foswiki::Search;
3
4=begin TML
5
6---+ package Foswiki::Search
7
8This module implements all the search functionality.
9
10=cut
11
12229µs240µs
# spent 27µs (15+13) within Foswiki::Search::BEGIN@12 which was called: # once (15µs+13µs) by Foswiki::search at line 12
use strict;
# spent 27µs making 1 call to Foswiki::Search::BEGIN@12 # spent 13µs making 1 call to strict::import
13223µs218µs
# spent 14µs (10+4) within Foswiki::Search::BEGIN@13 which was called: # once (10µs+4µs) by Foswiki::search at line 13
use warnings;
# spent 14µs making 1 call to Foswiki::Search::BEGIN@13 # spent 4µs making 1 call to warnings::import
14232µs256µs
# spent 33µs (9+24) within Foswiki::Search::BEGIN@14 which was called: # once (9µs+24µs) by Foswiki::search at line 14
use Assert;
# spent 33µs making 1 call to Foswiki::Search::BEGIN@14 # spent 24µs making 1 call to Exporter::import
15229µs2214µs
# spent 111µs (8+103) within Foswiki::Search::BEGIN@15 which was called: # once (8µs+103µs) by Foswiki::search at line 15
use Error qw( :try );
# spent 111µs making 1 call to Foswiki::Search::BEGIN@15 # spent 103µs making 1 call to Error::import
16
17218µs15µs
# spent 5µs within Foswiki::Search::BEGIN@17 which was called: # once (5µs+0s) by Foswiki::search at line 17
use Foswiki ();
# spent 5µs making 1 call to Foswiki::Search::BEGIN@17
18217µs13µs
# spent 3µs within Foswiki::Search::BEGIN@18 which was called: # once (3µs+0s) by Foswiki::search at line 18
use Foswiki::Sandbox ();
# spent 3µs making 1 call to Foswiki::Search::BEGIN@18
19294µs12.90ms
# spent 2.90ms (2.06+835µs) within Foswiki::Search::BEGIN@19 which was called: # once (2.06ms+835µs) by Foswiki::search at line 19
use Foswiki::Search::InfoCache ();
# spent 2.90ms making 1 call to Foswiki::Search::BEGIN@19
20297µs11.10ms
# spent 1.10ms (1.01+95µs) within Foswiki::Search::BEGIN@20 which was called: # once (1.01ms+95µs) by Foswiki::search at line 20
use Foswiki::Search::ResultSet ();
# spent 1.10ms making 1 call to Foswiki::Search::BEGIN@20
21219µs15µs
# spent 5µs within Foswiki::Search::BEGIN@21 which was called: # once (5µs+0s) by Foswiki::search at line 21
use Foswiki::ListIterator ();
# spent 5µs making 1 call to Foswiki::Search::BEGIN@21
22221µs13µs
# spent 3µs within Foswiki::Search::BEGIN@22 which was called: # once (3µs+0s) by Foswiki::search at line 22
use Foswiki::Iterator::FilterIterator ();
# spent 3µs making 1 call to Foswiki::Search::BEGIN@22
23291µs1952µs
# spent 952µs (861+91) within Foswiki::Search::BEGIN@23 which was called: # once (861µs+91µs) by Foswiki::search at line 23
use Foswiki::Iterator::PagerIterator ();
# spent 952µs making 1 call to Foswiki::Search::BEGIN@23
24219µs15µs
# spent 5µs within Foswiki::Search::BEGIN@24 which was called: # once (5µs+0s) by Foswiki::search at line 24
use Foswiki::Render ();
# spent 5µs making 1 call to Foswiki::Search::BEGIN@24
25282µs1488µs
# spent 488µs (442+46) within Foswiki::Search::BEGIN@25 which was called: # once (442µs+46µs) by Foswiki::search at line 25
use Foswiki::WebFilter ();
# spent 488µs making 1 call to Foswiki::Search::BEGIN@25
26288µs11.42ms
# spent 1.42ms (1.27+156µs) within Foswiki::Search::BEGIN@26 which was called: # once (1.27ms+156µs) by Foswiki::search at line 26
use Foswiki::MetaCache ();
# spent 1.42ms making 1 call to Foswiki::Search::BEGIN@26
27223µs15µs
# spent 5µs within Foswiki::Search::BEGIN@27 which was called: # once (5µs+0s) by Foswiki::search at line 27
use Foswiki::Infix::Error ();
# spent 5µs making 1 call to Foswiki::Search::BEGIN@27
28
29251µs269µs
# spent 39µs (9+30) within Foswiki::Search::BEGIN@29 which was called: # once (9µs+30µs) by Foswiki::search at line 29
use constant MONITOR => 0;
# spent 39µs making 1 call to Foswiki::Search::BEGIN@29 # spent 30µs making 1 call to constant::import
30
31
# spent 5µs within Foswiki::Search::BEGIN@31 which was called: # once (5µs+0s) by Foswiki::search at line 36
BEGIN {
3215µs if ( $Foswiki::cfg{UseLocale} ) {
33 require locale;
34 import locale();
35 }
3615.32ms15µs}
# spent 5µs making 1 call to Foswiki::Search::BEGIN@31
37
38=begin TML
39
40---++ ClassMethod new ($session)
41
42Constructor for the singleton Search engine object.
43
44=cut
45
46
# spent 15µs within Foswiki::Search::new which was called: # once (15µs+0s) by Foswiki::search at line 2406 of /var/www/foswikidev/core/lib/Foswiki.pm
sub new {
4711µs my ( $class, $session ) = @_;
48112µs my $this = bless( { session => $session }, $class );
49
5016µs return $this;
51}
52
53=begin TML
54
55---++ ObjectMethod finish()
56Break circular references.
57
58 Note to developers; please undef *all* fields in the object explicitly,
59 whether they are references or not. That way this method is "golden
60 documentation" of the live fields in the object.
61
62=cut
63
64
# spent 6.10ms (76µs+6.03) within Foswiki::Search::finish which was called: # once (76µs+6.03ms) by Foswiki::finish at line 2488 of /var/www/foswikidev/core/lib/Foswiki.pm
sub finish {
651600ns my $this = shift;
6611µs undef $this->{session};
67
68# these may well be function objects, but if (a setting changes, it needs to be picked up again.
69143µs if ( defined( $this->{queryParser} ) ) {
70110µs12µs $this->{queryParser}->finish();
# spent 2µs making 1 call to Foswiki::Infix::Parser::finish
7111µs undef $this->{queryParser};
72 }
731700ns if ( defined( $this->{searchParser} ) ) {
74 $this->{searchParser}->finish();
75 undef $this->{searchParser};
76 }
7715µs if ( defined( $this->{MetaCache} ) ) {
7815µs16.03ms $this->{MetaCache}->finish();
# spent 6.03ms making 1 call to Foswiki::MetaCache::finish
791800ns undef $this->{MetaCache};
80 }
81}
82
83=begin TML
84
85---++ ObjectMethod metacache
86returns the metacache.
87
88=cut
89
90
# spent 99.9ms (99.9+8µs) within Foswiki::Search::metacache which was called 70083 times, avg 1µs/call: # 17520 times (25.5ms+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:194] at line 176 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 1µs/call # 17520 times (24.1ms+0s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:194] at line 186 of /var/www/foswikidev/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 1µs/call # 8760 times (14.3ms+0s) by Foswiki::Search::formatResults at line 775, avg 2µs/call # 8760 times (13.1ms+0s) by Foswiki::Search::InfoCache::addTopic at line 105 of /var/www/foswikidev/core/lib/Foswiki/Search/InfoCache.pm, avg 1µs/call # 8760 times (12.5ms+0s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 203 of /var/www/foswikidev/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm, avg 1µs/call # 8760 times (10.4ms+0s) by Foswiki::Search::formatResults at line 782, avg 1µs/call # 3 times (12µs+8µs) by Foswiki::Meta::unload at line 504 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 7µs/call
sub metacache {
917008316.7ms my $this = shift;
92
93# these may well be function objects, but if (a setting changes, it needs to be picked up again.
947008319.6ms18µs if ( !defined( $this->{MetaCache} ) ) {
# spent 8µs making 1 call to Foswiki::MetaCache::new
95 $this->{MetaCache} = new Foswiki::MetaCache( $this->{session} );
96 }
9770083218ms return $this->{MetaCache};
98}
99
100=begin TML
101
102---++ ObjectMethod parseSearch($searchString, $params) -> Foswiki::*::Node
103
104parses the search string and builds the appropriate nodes (uses $param->{type} to work out which parser
105
106TODO: make parser register themselves with their type, so that we could plug in anything.
107
108=cut
109
110
# spent 13.9ms (1.23+12.7) within Foswiki::Search::parseSearch which was called 40 times, avg 348µs/call: # 40 times (1.23ms+12.7ms) by Foswiki::Search::searchWeb at line 371, avg 348µs/call
sub parseSearch {
1114022µs my $this = shift;
1124014µs my $searchString = shift;
113409µs my $params = shift;
114
115404µs my $query;
116407µs my $theParser;
1174054µs if ( $params->{type} eq 'query' ) {
1184028µs unless ( defined( $this->{queryParser} ) ) {
11911µs require Foswiki::Query::Parser;
12016µs1833µs $this->{queryParser} = new Foswiki::Query::Parser();
# spent 833µs making 1 call to Foswiki::Query::Parser::new
121 }
1224025µs $theParser = $this->{queryParser};
123 }
124 else {
125 unless ( defined( $this->{searchParser} ) ) {
126 require Foswiki::Search::Parser;
127 $this->{searchParser} =
128 new Foswiki::Search::Parser( $this->{session} );
129 }
130 $theParser = $this->{searchParser};
131 }
132
# spent 11.2ms (227µs+11.0) within Foswiki::Search::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Search.pm:134] which was called 40 times, avg 280µs/call: # 40 times (227µs+11.0ms) by Error::subs::try at line 419 of Error.pm, avg 280µs/call
try {
13340286µs4011.0ms $query = $theParser->parse( $searchString, $params );
# spent 11.0ms making 40 calls to Foswiki::Infix::Parser::parse, avg 275µs/call
134 }
135 catch Foswiki::Infix::Error with {
136
137 # Pass the error on to the caller
138 throw Error::Simple( shift->stringify() );
13940825µs120197µs };
# spent 143µs making 40 calls to Error::catch, avg 4µs/call # spent 54µs making 40 calls to Error::subs::with, avg 1µs/call # spent 11.7ms making 40 calls to Error::subs::try, avg 292µs/call, recursion: max depth 2, sum of overlapping time 11.7ms
140
141#print STDERR "parseSearch($searchString) => ".$query->stringify()."\n" if MONITOR;
142
14340119µs return $query;
144}
145
146sub _extractPattern {
147 my ( $text, $pattern, $encode ) = @_;
148
149 # Pattern comes from topic, therefore tainted
150 $pattern =
151 Foswiki::Sandbox::untaint( $pattern, \&Foswiki::validatePattern );
152
153 my $ok = 0;
154 eval {
155
156 # The eval acts as a try block in case there is anything evil in
157 # the pattern.
158 $ok = 1 if ( $text =~ s/$pattern/$1/is );
159 };
160 $text = '' unless $ok;
161
162 if ($encode) {
163
164 # Reverse the action of Foswiki::expandStandardEscapes
165 $text =~ s/$/\$dollar/g;
166 $text =~ s/&/\$amp()/g;
167 $text =~ s/>/\$gt()/g;
168 $text =~ s/</\$lt()/g;
169 $text =~ s/%/\$percent()/g;
170 $text =~ s/>/\$comma()/g;
171 $text =~ s/"/\$quot()/g;
172 $text =~ s/\n/\$n()/g;
173 }
174
175 return $text;
176}
177
178# With the same argument as $pattern, returns a number which is the count of
179# occurences of the pattern argument.
180sub _countPattern {
181 my ( $text, $pattern ) = @_;
182
183 $pattern =
184 Foswiki::Sandbox::untaint( $pattern, \&Foswiki::validatePattern );
185
186 my $count;
187 try {
188
189 # see: perldoc -q count
190 $count = () = $text =~ m/$pattern/g;
191 }
192 catch Error with {
193 $count = 0;
194 };
195
196 return $count;
197}
198
199=begin TML
200
201---++ StaticMethod _isSetTrue( $value, $default ) -> $boolean
202
203Returns 1 if =$value= is _actually set to_ true, and 0 otherwise.
204
205If the value is undef, then =$default= is returned. If =$default= is
206not specified it is taken as 0.
207
208=cut
209
210sub _isSetTrue {
211 my ( $value, $default ) = @_;
212
213 $default ||= 0;
214
215 return $default unless defined($value);
216
217 $value =~ s/on//gi;
218 $value =~ s/yes//gi;
219 $value =~ s/true//gi;
220 return ($value) ? 0 : 1;
221}
222
223=begin TML
224
225---++ ObjectMethod searchWeb (...)
226
227Search one or more webs according to the parameters.
228
229If =_callback= is set, that means the caller wants results as
230soon as they are ready. =_callback_ should be set to a reference
231to a function which takes =_cbdata= as the first parameter and
232remaining parameters the same as 'print'.
233
234If =_callback= is set, the result is always undef. Otherwise the
235result is a string containing the rendered search results.
236
237The function will throw Error::Simple if it encounters any problems with the
238syntax of the search string.
239
240Note: If =format= is set, =template= will be ignored.
241
242Note: For legacy, if =regex= is defined, it will force type='regex'
243
244If =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.
245
246SMELL: If =template= is defined =bookview= will not work
247
248SMELL: it seems that if you define =_callback= then you are
249 responsible for converting the TML to HTML yourself!
250
251FIXME: =callback= cannot work with format parameter (consider format='| $topic |'
252
253=cut
254
255
# spent 137s (13.8ms+137) within Foswiki::Search::searchWeb which was called 40 times, avg 3.42s/call: # 40 times (13.8ms+137s) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/SEARCH.pm:40] at line 39 of /var/www/foswikidev/core/lib/Foswiki/Macros/SEARCH.pm, avg 3.42s/call
sub searchWeb {
2564021µs my $this = shift;
2574023µs my $session = $this->{session};
258 ASSERT( defined $session->{webName} ) if DEBUG;
25940169µs my %params = @_;
260
26140152µs40428µs my $baseWebObject = Foswiki::Meta->new( $session, $session->{webName} );
# spent 428µs making 40 calls to Foswiki::Meta::new, avg 11µs/call
262
26340125µs40237µs my ( $callback, $cbdata ) = setup_callback( \%params, $baseWebObject );
# spent 237µs making 40 calls to Foswiki::Search::setup_callback, avg 6µs/call
264
2654030µs my $baseTopic = $params{basetopic} || $session->{topicName};
2664018µs my $baseWeb = $params{baseweb} || $session->{webName};
26740132µs4085µs $params{casesensitive} = Foswiki::isTrue( $params{casesensitive} );
# spent 85µs making 40 calls to Foswiki::isTrue, avg 2µs/call
2684044µs $params{excludeTopics} = $params{excludetopic} || '';
2694045µs my $formatDefined = $params{formatdefined} = defined $params{format};
2704014µs my $format = $params{format};
271
2724093µs4061µs $params{multiple} = Foswiki::isTrue( $params{multiple} );
# spent 61µs making 40 calls to Foswiki::isTrue, avg 2µs/call
273
274 # Let the search know we're only concerned with one hit per file
2754092µs40319µs $params{files_without_match} = not Foswiki::isTrue( $params{multiple} );
# spent 319µs making 40 calls to Foswiki::isTrue, avg 8µs/call
276
2774076µs40266µs $params{nonoise} = Foswiki::isTrue( $params{nonoise} );
# spent 266µs making 40 calls to Foswiki::isTrue, avg 7µs/call
2784097µs4069µs $params{noempty} = Foswiki::isTrue( $params{noempty}, $params{nonoise} );
# spent 69µs making 40 calls to Foswiki::isTrue, avg 2µs/call
279### $params{zeroresults} = Foswiki::isTrue( ( $params{zeroresults} ), $params{nonoise} );
280
281 #TODO: refactorme
2824024µs my $header = $params{header};
2834062µs my $footer = $params{footer};
2844092µs4058µs my $noTotal = Foswiki::isTrue( $params{nototal}, $params{nonoise} );
# spent 58µs making 40 calls to Foswiki::isTrue, avg 1µs/call
285
2864067µs40193µs my $noEmpty = Foswiki::isTrue( $params{noempty}, $params{nonoise} );
# spent 193µs making 40 calls to Foswiki::isTrue, avg 5µs/call
287
288 # Note: a defined header/footer overrides noheader/nofooter
289 # To maintain Cairo compatibility we ommit default header/footer if the
29040116µs4062µs my $noHeader =
# spent 62µs making 40 calls to Foswiki::isTrue, avg 2µs/call
291 !defined($header)
292 && Foswiki::isTrue( $params{noheader}, $params{nonoise} )
293 || ( !$header && $formatDefined );
294
2954033µs my $noFooter =
296 !defined($footer)
297 && Foswiki::isTrue( $params{nofooter}, $params{nonoise} )
298 || ( !$footer && $formatDefined );
299
3004091µs4056µs my $noSummary = Foswiki::isTrue( $params{nosummary}, $params{nonoise} );
# spent 56µs making 40 calls to Foswiki::isTrue, avg 1µs/call
3014083µs4054µs my $zeroResults =
# spent 54µs making 40 calls to Foswiki::isTrue, avg 1µs/call
302 Foswiki::isTrue( $params{zeroresults}, $params{nonoise} || 1 );
303
304 #END TODO
305
3064075µs4058µs my $doBookView = Foswiki::isTrue( $params{bookview} );
# spent 58µs making 40 calls to Foswiki::isTrue, avg 1µs/call
307
3084075µs4057µs my $revSort = Foswiki::isTrue( $params{reverse} );
# spent 57µs making 40 calls to Foswiki::isTrue, avg 1µs/call
3094040µs $params{scope} = $params{scope} || '';
3104023µs my $searchString = defined $params{search} ? $params{search} : '';
311
3124050µs $params{includeTopics} = $params{topic} || '';
3134025µs $params{type} = $params{type} || '';
314
3154027µs $params{wordboundaries} = 0;
3164029µs if ( $params{type} eq 'word' ) {
317
318 # 'word' is exactly the same as 'keyword', except we will be searching
319 # with word boundaries
320 $params{type} = 'keyword';
321 $params{wordboundaries} = 1;
322 }
323
3244028µs my $webNames = $params{web} || '';
3254016µs my $date = $params{date} || '';
3264015µs my $recurse = $params{'recurse'} || '';
327
3284081µs $baseWeb =~ s/\./\//g;
329
3304022µs $params{type} = 'regex' if ( $params{regex} );
331
332#TODO: quick hackjob - see what the feature proposal gives before it becomes public
3334053µs if ( defined( $params{groupby} ) and ( $params{groupby} eq 'none' ) ) {
334
335 #_only_ allow groupby="none" - as its a secrect none public setting.
336 }
337 else {
3384034µs $params{groupby} = 'web';
339 }
340
341 # need to tell the Meta::query pager settings so it can optimise
3424033µs require Digest::MD5;
3434019µs my $string_id = $params{_RAW} || 'we had better not go there';
34440162µs40402µs $string_id = Foswiki::encode_utf8($string_id);
# spent 402µs making 40 calls to Encode::encode_utf8, avg 10µs/call
34540375µs40237µs my $paging_ID = 'SEARCH' . Digest::MD5::md5_hex($string_id);
# spent 237µs making 40 calls to Digest::MD5::md5_hex, avg 6µs/call
3464043µs $params{pager_urlparam_id} = $paging_ID;
347
348 # 1-based system; 0 is not a valid page number
34940183µs401.02ms $params{showpage} = $session->{request}->param($paging_ID)
# spent 1.02ms making 40 calls to Foswiki::Request::param, avg 26µs/call
350 || $params{showpage};
351
352 #append a pager to the end of the search result.
35340122µs4082µs $params{pager} = Foswiki::isTrue( $params{pager} );
# spent 82µs making 40 calls to Foswiki::isTrue, avg 2µs/call
354
3554062µs if ( defined( $params{pagesize} )
356 or defined( $params{showpage} )
357 or $params{pager} )
358 {
359 if ( !defined( $params{pagesize} ) ) {
360 $params{pagesize} = $Foswiki::cfg{Search}{DefaultPageSize} || 25;
361 }
362 $params{showpage} = 1 unless ( defined( $params{showpage} ) );
363 $params{paging_on} = 1;
364 }
365 else {
366
367 #denote the pager is off.
3684037µs $params{paging_on} = 0;
369 }
370################### Perform The Search
37140122µs4013.9ms my $query = $this->parseSearch( $searchString, \%params );
# spent 13.9ms making 40 calls to Foswiki::Search::parseSearch, avg 348µs/call
372
373#setting the inputTopicSet to be undef allows the search/query algo to use
374#the topic="" and excludetopic="" params and web Obj to get a new list of topics.
375#this allows the algo's to customise and optimise the getting of this list themselves.
376
377#NOTE: as of Jun2011 foswiki 2.0's query() returns a result set filtered by ACL and paged to $showpage
378#TODO: work out if and how to avoid it
37940162µs40108s my $infoCache = Foswiki::Meta::query( $query, undef, \%params );
# spent 108s making 40 calls to Foswiki::Meta::query, avg 2.71s/call
380
381################### Do the Rendering
382
383 # If the search did not return anything, return the rendered zeroresults
384 # if it is defined as a string.
385 # (http://foswiki.org/Development/AddDefaultTopicParameterToINCLUDE)
3864097µs40128ms if ( not $infoCache->hasNext() ) {
# spent 128ms making 40 calls to Foswiki::Iterator::FilterIterator::hasNext, avg 3.20ms/call
387 if ( not $zeroResults ) {
388 return '';
389 }
390 else {
391 if ( not _isSetTrue( $params{zeroresults}, 1 ) ) {
392
393#foswiki 1.1 Feature Proposal: SEARCH needs an alt parameter in case of zero results
394
395 #TODO: extract & merge with extraction of footer processing code below
396 my $result = $params{zeroresults};
397
398 $result =~ s/\$web/$baseWeb/gs; # expand name of web
399 $result =~ s/([^\n])$/$1\n/s; # add new line at end
400
401 # output footer of $web
402 $result =~ s/\$ntopics/0/gs;
403 $result =~ s/\$nhits/0/gs;
404 $result =~ s/\$index/0/gs;
405
406 #legacy SEARCH counter support
407 $result =~ s/%NTOPICS%/0/g;
408
409 $result = Foswiki::expandStandardEscapes($result);
410 $result =~ s/\n$//s; # remove trailing new line
411
412 return $result;
413 }
414 }
415 }
416
41740323µs40114ms my $tmplSearch =
# spent 114ms making 40 calls to Foswiki::Search::loadTemplates, avg 2.85ms/call
418 $this->loadTemplates( \%params, $baseWebObject, $formatDefined,
419 $doBookView, $noHeader, $noSummary, $noTotal, $noFooter );
420
421 # Generate 'Search:' part showing actual search string used
422 # Ommit any text before search results if either nosearch or nonoise is on
42340105µs40423µs my $nonoise = Foswiki::isTrue( $params{nonoise} );
# spent 423µs making 40 calls to Foswiki::isTrue, avg 11µs/call
42440104µs4076µs my $noSearch = Foswiki::isTrue( $params{nosearch}, $nonoise );
# spent 76µs making 40 calls to Foswiki::isTrue, avg 2µs/call
4254017µs unless ($noSearch) {
426 my $searchStr = $searchString;
427 $searchStr =~ s/&/&amp;/g;
428 $searchStr =~ s/</&lt;/g;
429 $searchStr =~ s/>/&gt;/g;
430
431 $tmplSearch =~ s/%SEARCHSTRING%/$searchStr/g;
432 &$callback( $cbdata, $tmplSearch );
433 }
434
435 # We now format the results.
436 # All the
43740295µs4028.3s my ( $numberOfResults, $web_searchResult ) =
# spent 28.3s making 40 calls to Foswiki::Search::formatResults, avg 707ms/call
438 $this->formatResults( $query, $infoCache, \%params );
439
4404028µs return if ( defined $params{_callback} );
441
44240300µs my $searchResult = join( '', @{ $params{_cbdata} } );
443
44440212µs40460µs $searchResult = Foswiki::expandStandardEscapes($searchResult);
# spent 460µs making 40 calls to Foswiki::expandStandardEscapes, avg 12µs/call
445
446 # Remove trailing separator or new line if nofinalnewline parameter is set
44740142µs40122µs my $noFinalNewline = Foswiki::isTrue( $params{nofinalnewline}, 1 );
# spent 122µs making 40 calls to Foswiki::isTrue, avg 3µs/call
4484032µs if ( $formatDefined && $noFinalNewline ) {
4494032µs if ( $params{separator} ) {
450 my $separator = quotemeta( $params{separator} );
451 $searchResult =~ s/$separator$//s; # remove separator at end
452 }
453 else {
4544040µs $searchResult =~ s/\n$//s; # remove trailing new line
455 }
456 }
457
458402.44ms return $searchResult;
459}
460
461=begin TML
462
463---++ ObjectMethod loadTemplates (...)
464
465this code was extracted from searchWeb, and should probably be private.
466
467=cut
468
469
# spent 114ms (2.25+112) within Foswiki::Search::loadTemplates which was called 40 times, avg 2.85ms/call: # 40 times (2.25ms+112ms) by Foswiki::Search::searchWeb at line 417, avg 2.85ms/call
sub loadTemplates {
470 my (
47140127µs $this, $params, $baseWebObject,
472 $formatDefined, $doBookView, $noHeader,
473 $noSummary, $noTotal, $noFooter
474 ) = @_;
475
4764031µs my $session = $this->{session};
477
478 #tmpl loading code.
4794027µs my $tmpl = '';
480
4814074µs my $template = $params->{template} || '';
4824025µs if ($formatDefined) {
483 $template = 'searchformat';
484 }
485 elsif ($template) {
486
487 # template definition overrides book and rename views
488 }
489 elsif ($doBookView) {
490 $template = 'searchbookview';
491 }
492 else {
493 $template = 'search';
494 }
49540442µs80102ms $tmpl = $session->templates->readTemplate($template);
# spent 101ms making 40 calls to Foswiki::Templates::readTemplate, avg 2.54ms/call # spent 230µs making 40 calls to Foswiki::templates, avg 6µs/call
496
497#print STDERR "}}} $tmpl {{{\n";
498# SMELL: the only META tags in a template will be METASEARCH
499# Why the heck are they being filtered????
500#TODO: write a unit test that uses topic based templates with META's in them and if ok, remove.
5014055µs $tmpl =~ s/\%META\{.*?\}\%//g; # remove %META{'parent'}%
502
503 # Split template into 5 sections
5044061µs my ( $tmplHead, $tmplSearch, $tmplTable, $tmplNumber, $tmplTail ) =
505 split( /%SPLIT%/, $tmpl );
506
5074031µs my $repeatText;
508
5094045µs if ( !defined($tmplTail) ) {
51040238µs803.10ms $tmplSearch = $session->templates->expandTemplate('SEARCH:searched');
# spent 3.02ms making 40 calls to Foswiki::Templates::expandTemplate, avg 75µs/call # spent 79µs making 40 calls to Foswiki::templates, avg 2µs/call
51140124µs804.86ms $tmplNumber = $session->templates->expandTemplate('SEARCH:count');
# spent 4.80ms making 40 calls to Foswiki::Templates::expandTemplate, avg 120µs/call # spent 69µs making 40 calls to Foswiki::templates, avg 2µs/call
512
513#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
514#TODO: push up the 'noheader' evaluation to take not of this quirk
515#TODO: um, we die when ASSERT is on with a wide char in print
5164018µs unless ($noHeader) {
517 $params->{header} =
518 $session->templates->expandTemplate('SEARCH:header')
519 unless defined $params->{header};
520 }
521
52240129µs802.09ms $repeatText = $session->templates->expandTemplate('SEARCH:format');
# spent 2.02ms making 40 calls to Foswiki::Templates::expandTemplate, avg 51µs/call # spent 72µs making 40 calls to Foswiki::templates, avg 2µs/call
523
5244057µs unless ($noFooter) {
525 $params->{footer} =
526 $session->templates->expandTemplate('SEARCH:footer')
527 unless defined $params->{footer};
528 }
529 }
530 else {
531
532 #Historical legacy form of the search TMPL's
533 # header and footer of $web
534 my $beforeText;
535 my $afterText;
536 ( $beforeText, $repeatText, $afterText ) =
537 split( /%REPEAT%/, $tmplTable );
538
539 unless ($noHeader) {
540 $params->{header} = $beforeText unless defined $params->{header};
541 }
542
543 unless ($noFooter) {
544 $params->{footer} ||= $afterText;
545 }
546 }
547
548 #nosummary="on" nosearch="on" noheader="on" nototal="on"
5494029µs if ($noSummary) {
5504020µs $repeatText =~ s/%TEXTHEAD%//g;
5514051µs $repeatText =~ s/&nbsp;//g;
552 }
553 else {
554 $repeatText =~ s/%TEXTHEAD%/\$summary(searchcontext)/g;
555 }
5564049µs $params->{format} ||= $repeatText;
557
55840121µs $params->{footercounter} ||= $tmplNumber;
559
56040143µs return $tmplSearch;
561}
562
563=begin TML
564---++ formatResults
565
566the implementation of %FORMAT{}%
567
568TODO: rewrite to take a resultset, a set of params? and a hash of sub's to
569enable evaluations of things like '$include(blah)' in format strings.
570
571have a default set of replacements like $lt, $nop, $percnt, $dollar etc, and then
572the hash of subs can take care of %MACRO{}% specific complex to evaluate replacements..
573
574(that way we don't pre-evaluate and then subst)
575
576=cut
577
578
# spent 28.3s (516ms+27.7) within Foswiki::Search::formatResults which was called 40 times, avg 707ms/call: # 40 times (516ms+27.7s) by Foswiki::Search::searchWeb at line 437, avg 707ms/call
sub formatResults {
5794035µs my ( $this, $query, $infoCache, $params ) = @_;
5804031µs my $session = $this->{session};
5814032µs my $users = $session->{users};
58240186µs40314µs my ( $callback, $cbdata ) = setup_callback($params);
# spent 314µs making 40 calls to Foswiki::Search::setup_callback, avg 8µs/call
583
5844035µs my $baseTopic = $session->{topicName};
5854021µs my $baseWeb = $session->{webName};
5864075µs4067µs my $doBookView = Foswiki::isTrue( $params->{bookview} );
# spent 67µs making 40 calls to Foswiki::isTrue, avg 2µs/call
5874059µs40205µs my $caseSensitive = Foswiki::isTrue( $params->{casesensitive} );
# spent 205µs making 40 calls to Foswiki::isTrue, avg 5µs/call
5884090µs4065µs my $doExpandVars = Foswiki::isTrue( $params->{expandvariables} );
# spent 65µs making 40 calls to Foswiki::isTrue, avg 2µs/call
5894067µs40171µs my $nonoise = Foswiki::isTrue( $params->{nonoise} );
# spent 171µs making 40 calls to Foswiki::isTrue, avg 4µs/call
5904073µs4060µs my $noSearch = Foswiki::isTrue( $params->{nosearch}, $nonoise );
# spent 60µs making 40 calls to Foswiki::isTrue, avg 2µs/call
5914055µs my $formatDefined = defined $params->{format};
5924021µs my $format = $params->{format} || '';
5934033µs my $header = $params->{header} || '';
5944027µs my $footer = $params->{footer} || '';
5954043µs my $limit = $params->{limit} || '';
5964019µs my $itemView = $params->{itemview};
597
598 # Limit search results. Cannot be deprecated
599 # Limit will still be needed for the application types of SEARCHES
600 # even if pagesize is added as feature. Example for searching and listing
601 # text from first 5 bullets in a formatted multiple type search
6024039µs if ( $limit =~ m/(^\d+$)/ ) {
603
604 # only digits, all else is the same as
605 # an empty string. "+10" won't work.
606 $limit = $1;
607 }
608 else {
609
610 # change 'all' to 0, then to big number
6114016µs $limit = 0;
612 }
6134016µs $limit = 32000 unless ($limit);
614
615 #pager formatting
6164012µs my %pager_formatting;
6174015µs if ( $params->{paging_on} ) #TODO: if can skip()
618 {
619
620 #TODO: this is a dangerous assumption that should be abstracted
621 ASSERT( $infoCache->isa('Foswiki::Iterator::PagerIterator') ) if DEBUG;
622
623 $limit = $infoCache->pagesize();
624
625 my $paging_ID = $params->{pager_urlparam_id};
626
627 # 1-based system; 0 is not a valid page number
628 my $showpage = $infoCache->showpage();
629
630 #TODO: need to ask the result set
631 my $numberofpages = $infoCache->numberOfPages();
632
633 #TODO: excuse me?
634 my $sep = ' ';
635
636 my $nextidx = $showpage + 1;
637 my $previousidx = $showpage - 1;
638
639 my %new_params;
640
641 #kill me please, i can't find a way to just load up the hash :(
642 foreach my $key ( $session->{request}->param ) {
643 $new_params{$key} = $session->{request}->param($key);
644 }
645
646 $session->templates->readTemplate('searchformat');
647
648 %pager_formatting = (
649 'previouspage' => sub { return $previousidx },
650 'currentpage' => sub { return $showpage },
651 'nextpage' => sub { return $showpage + 1 },
652 'numberofpages' => sub { return $infoCache->numberOfPages() },
653 'pagesize' => sub { return $infoCache->pagesize() },
654 'sep' => sub { return $sep; }
655 );
656
657 $pager_formatting{'previousurl'} = sub {
658 my $previouspageurl = '';
659 if ( $previousidx >= 1 ) {
660 $new_params{$paging_ID} = $previousidx;
661 $previouspageurl =
662 Foswiki::Func::getScriptUrl( $baseWeb, $baseTopic, 'view',
663 %new_params );
664 }
665 return $previouspageurl;
666 };
667
668 $pager_formatting{'previousbutton'} = sub {
669 my $previouspagebutton = '';
670 if ( $previousidx >= 1 ) {
671 $new_params{$paging_ID} = $previousidx;
672 $previouspagebutton =
673 $session->templates->expandTemplate('SEARCH:pager_previous');
674 }
675 $previouspagebutton =
676 $this->formatCommon( $previouspagebutton, \%pager_formatting );
677 return $previouspagebutton;
678 };
679
680 $pager_formatting{'nexturl'} = sub {
681 my $nextpageurl = '';
682 if ( $nextidx <= $infoCache->numberOfPages() ) {
683 $new_params{$paging_ID} = $nextidx;
684 $nextpageurl =
685 Foswiki::Func::getScriptUrl( $baseWeb, $baseTopic, 'view',
686 %new_params );
687 }
688 return $nextpageurl;
689 };
690
691 $pager_formatting{'nextbutton'} = sub {
692 my $nextpagebutton = '';
693 if ( $nextidx <= $infoCache->numberOfPages() ) {
694 $new_params{$paging_ID} = $nextidx;
695 $nextpagebutton =
696 $session->templates->expandTemplate('SEARCH:pager_next');
697 }
698 $nextpagebutton =
699 $this->formatCommon( $nextpagebutton, \%pager_formatting );
700 return $nextpagebutton;
701 };
702
703 $pager_formatting{'pager'} = sub {
704 my $pager_control = '';
705 if ( $infoCache->numberOfPages() > 1 ) {
706 $pager_control = $params->{pagerformat}
707 || $session->templates->expandTemplate('SEARCH:pager');
708 $pager_control =
709 $this->formatCommon( $pager_control, \%pager_formatting );
710 }
711 return $pager_control;
712 };
713 }
714
715 #TODO: multiple is an attribute of the ResultSet
71640107µs40192µs my $doMultiple = Foswiki::isTrue( $params->{multiple} );
# spent 192µs making 40 calls to Foswiki::isTrue, avg 5µs/call
7174066µs40184µs my $noEmpty = Foswiki::isTrue( $params->{noempty}, $nonoise );
# spent 184µs making 40 calls to Foswiki::isTrue, avg 5µs/call
718
719 # Note: a defined header/footer overrides noheader/nofooter
720 # To maintain Cairo compatibility we ommit default header/footer if the
7214081µs my $noHeader =
722 !defined($header) && Foswiki::isTrue( $params->{noheader}, $nonoise )
723 || ( !$header && $formatDefined );
724
7254032µs my $noFooter =
726 !defined($footer) && Foswiki::isTrue( $params->{nofooter}, $nonoise )
727 || ( !$footer && $formatDefined );
728
7294091µs4065µs my $noSummary = Foswiki::isTrue( $params->{nosummary}, $nonoise );
# spent 65µs making 40 calls to Foswiki::isTrue, avg 2µs/call
7304078µs4054µs my $zeroResults =
# spent 54µs making 40 calls to Foswiki::isTrue, avg 1µs/call
731 Foswiki::isTrue( ( $params->{zeroresults} ), $nonoise || 1 );
7324073µs4053µs my $noTotal = Foswiki::isTrue( $params->{nototal}, $nonoise );
# spent 53µs making 40 calls to Foswiki::isTrue, avg 1µs/call
7334046µs my $newLine = $params->{newline} || '';
7344026µs my $separator = $params->{separator};
7354062µs my $type = $params->{type} || '';
736
737 # output the list of topics in $web
7384037µs my $ntopics = 0; # number of topics in current web
739409µs my $nhits = 0; # number of hits (if multiple=on) in current web
7404031µs my $headerDone = $noHeader;
741
742408µs my $web = $baseWeb;
74340115µs40441µs my $webObject = new Foswiki::Meta( $session, $web );
# spent 441µs making 40 calls to Foswiki::Meta::new, avg 11µs/call
7444017µs my $lastWebProcessed = '';
745
746 #total number of topics and hits - not reset when we swap webs
7474012µs my $ttopics = 0;
7484012µs my $thits = 0;
749
7504092µs4064µs while ( $infoCache->hasNext() ) {
# spent 64µs making 40 calls to Foswiki::Iterator::FilterIterator::hasNext, avg 2µs/call
751876014.6ms876059.9ms my $listItem = $infoCache->next();
# spent 59.9ms making 8760 calls to Foswiki::Iterator::FilterIterator::next, avg 7µs/call
752 ASSERT( defined($listItem) ) if DEBUG;
753
754 #############################################################
755 #TOPIC specific
75687604.20ms my $topic = $listItem;
7578760866µs my $text; #undef means the formatResult() gets it from $info->text;
7588760630µs my $info;
75987605.74ms my @multipleHitLines = ();
760876084.8ms1752028.2ms if (
# spent 28.2ms making 17520 calls to UNIVERSAL::isa, avg 2µs/call
761 ( $infoCache->isa('Foswiki::Search::ResultSet') ) or #SEARCH
762 ( $infoCache->isa('Foswiki::Iterator::FilterIterator') ) or #SEARCH
763 ( $infoCache->isa('Foswiki::Iterator::PagerIterator') ) or #SEARCH
764 ( $infoCache->isa('Foswiki::Search::InfoCache') ) #FORMAT
765 )
766 {
767876025.1ms8760130ms ( $web, $topic ) =
# spent 130ms making 8760 calls to Foswiki::Func::normalizeWebTopicName, avg 15µs/call
768 Foswiki::Func::normalizeWebTopicName( '', $listItem );
769
770# add dependencies (TODO: unclear if this should be before the paging, or after the allowView - sadly, it can't be _in_ the infoCache)
77187605.71ms if ( my $cache = $session->{cache} ) {
772 $cache->addDependency( $web, $topic );
773 }
774
775876021.3ms1752087.3ms my $topicMeta = $this->metacache->getMeta( $web, $topic );
# spent 73.0ms making 8760 calls to Foswiki::MetaCache::getMeta, avg 8µs/call # spent 14.3ms making 8760 calls to Foswiki::Search::metacache, avg 2µs/call
77687601.16ms if ( not defined($topicMeta) ) {
777
778#TODO: OMG! Search.pm relies on Meta::load (in the metacache) returning a meta object even when the topic does not exist.
779#lets change that
780 $topicMeta = new Foswiki::Meta( $session, $web, $topic );
781 }
782876017.3ms17520357ms $info = $this->metacache->get( $web, $topic, $topicMeta );
# spent 346ms making 8760 calls to Foswiki::MetaCache::get, avg 40µs/call # spent 10.4ms making 8760 calls to Foswiki::Search::metacache, avg 1µs/call
783 ASSERT( defined( $info->{tom} ) ) if DEBUG;
784
78587602.16ms $text = '';
786
787 # Special handling for format='...'
78887603.22ms if ($formatDefined) {
789876014.8ms876027.6ms $text = $info->{tom}->text();
# spent 27.6ms making 8760 calls to Foswiki::Meta::text, avg 3µs/call
79087601.14ms $text = '' unless defined $text;
791
79287602.35ms if ($doExpandVars) {
793 if ( $web eq $baseWeb && $topic eq $baseTopic ) {
794
795 # primitive way to prevent recursion
796 $text =~ s/%SEARCH/%<nop>SEARCH/g;
797 }
798 $text = $info->{tom}->expandMacros($text);
799 }
800 }
801
802 #TODO: should extract this somehow
803
80487601.03ms if ( $doMultiple && !$query->isEmpty() ) {
805
806 #TODO: Sven wonders if this should be a HoistRE..
807 #TODO: well, um, and how does this work for query search?
808 my @tokens = @{ $query->tokens() };
809 my $pattern = $tokens[$#tokens]; # last token in an AND search
810 $pattern = quotemeta($pattern) if ( $type ne 'regex' );
811 $text = $info->{tom}->text() unless defined $text;
812 $text = '' unless defined $text;
813
814 if ($caseSensitive) {
815 @multipleHitLines =
816 reverse grep { /$pattern/ } split( /[\n\r]+/, $text );
817 }
818 else {
819 @multipleHitLines =
820 reverse grep { /$pattern/i } split( /[\n\r]+/, $text );
821 }
822 }
823
824 # Apply heading offset - posibly to each hit result independently
82587605.42ms if ( $params->{headingoffset}
826 && $params->{headingoffset} =~ m/^([-+]?\d+)$/
827 && $1 != 0 )
828 {
829 my ( $off, $noff ) = ( 0 + $1, 0 - $1 );
830 if ( scalar(@multipleHitLines) ) {
831 @multipleHitLines = map {
832 $_ =~ "<ho off=\"$off\">\n$text\n<ho off=\"$noff\"/>"
833 } @multipleHitLines;
834 }
835 else {
836 $text = "<ho off=\"$off\">\n$text\n<ho off=\"$noff\"/>";
837 }
838 }
839 }
840
84187603.04ms $ntopics += 1;
84287601.23ms $ttopics += 1;
843876011.7ms do { # multiple=on loop
844
8458760848µs $nhits += 1;
8468760778µs $thits += 1;
847
84887603.13ms $text = pop(@multipleHitLines) if ( scalar(@multipleHitLines) );
849
85087602.02ms my $justdidHeaderOrFooter = 0;
85187607.29ms if ( ( defined( $params->{groupby} ) )
852 and ( $params->{groupby} eq 'web' ) )
853 {
85487602.55ms if ( $lastWebProcessed ne $web ) {
855
856 #output the footer for the previous listItem
8574014µs if ( $lastWebProcessed ne '' ) {
858
859 #c&p from below
860 #TODO: needs refactoring.
861 my $processedfooter = $footer;
862 if ( not $noTotal ) {
863 $processedfooter .= $params->{footercounter};
864 }
865 if ( defined($processedfooter)
866 and ( $processedfooter ne '' ) )
867 {
868
869 #footer comes before result
870 $ntopics--;
871 $nhits--;
872
873#because $pager contains more $ntopics like format strings, it needs to be expanded first.
874 $processedfooter =
875 $this->formatCommon( $processedfooter,
876 \%pager_formatting );
877 $processedfooter =~
878 s/\$web/$lastWebProcessed/gs; # expand name of web
879
880# $processedfooter =~ # s/([^\n])$/$1\n/s; # add new line at end
881# output footer of $web
882
883 $processedfooter =~ s/\$ntopics/$ntopics/gs;
884 $processedfooter =~ s/\$nhits/$nhits/gs;
885 $processedfooter =~ s/\$index/$thits/gs;
886
887 #legacy SEARCH counter support
888 $processedfooter =~ s/%NTOPICS%/$ntopics/g;
889
890 $processedfooter =
891 $this->formatCommon( $processedfooter,
892 \%pager_formatting );
893
894# $processedfooter =~ # s/\n$//s; # remove trailing new line
895
896 $justdidHeaderOrFooter = 1;
897 &$callback( $cbdata, $processedfooter );
898
899 #go back to counting results
900 $ntopics++;
901 $nhits++;
902 }
903 }
904
905 #trigger a header for this new web
9064010µs $headerDone = undef;
907 }
908 }
909
91087601.34ms if ( $lastWebProcessed ne $web ) {
91140195µs40462µs $webObject = new Foswiki::Meta( $session, $web );
# spent 462µs making 40 calls to Foswiki::Meta::new, avg 12µs/call
9124014µs $lastWebProcessed = $web;
913
914 #reset our web partitioned legacy counts
9154015µs $ntopics = 1;
9164016µs $nhits = 1;
917 }
918
919 # lazy output of header (only if needed for the first time)
92087603.88ms if ( ( !$headerDone and ( defined($header) ) )
921 and ( $header ne '' ) )
922 {
923
924 my $processedheader = $header;
925
926 # because $pager contains more $ntopics like format
927 # strings, it needs to be expanded first.
928 $processedheader =
929 $this->formatCommon( $processedheader, \%pager_formatting );
930 $processedheader =~ s/\$web/$web/gs; # expand name of web
931
932 # add new line after the header unless separator is defined
933 # per Item1773 / SearchSeparatorDefaultHeaderFooter
934 unless ( defined $separator ) {
935 $processedheader =~ s/([^\n])$/$1\n/s;
936 }
937
938 $headerDone = 1;
939 my $thisWebBGColor = $webObject->getPreference('WEBBGCOLOR')
940 || '\#FF00FF';
941 $processedheader =~ s/%WEBBGCOLOR%/$thisWebBGColor/g;
942 $processedheader =~ s/%WEB%/$web/g;
943 $processedheader =~ s/\$ntopics/($ntopics-1)/gse;
944 $processedheader =~ s/\$nhits/($nhits-1)/gse;
945 $processedheader =~ s/\$index/($thits-1)/gs;
946 $processedheader =
947 $this->formatCommon( $processedheader, \%pager_formatting );
948 &$callback( $cbdata, $processedheader );
949 $justdidHeaderOrFooter = 1;
950 }
951
95287601.06ms if ( defined($separator)
953 and ( $thits > 1 )
954 and ( $justdidHeaderOrFooter != 1 ) )
955 {
956 &$callback( $cbdata, $separator );
957 }
958
959 ###################Render the result
9608760985µs my $out;
961 my $handleRev1Info = sub {
962
963 # Handle e.g. createdate, createwikiuser etc
964 my $info =
965 $this->metacache->get( $_[1]->web, $_[1]->topic, $_[1] );
966 my $r = $info->{tom}->getRev1Info( $_[0] );
967 return $r;
968876029.0ms };
969 my $handleRevInfo = sub {
970 return $session->renderer->renderRevisionInfo(
971 $_[1],
972 $info->{revNum} || 0,
973 '$' . $_[0]
974 );
975876013.7ms };
976
97787603.50ms if ( $formatDefined and ( $format ne '' ) ) {
978
979 # SMELL: hack to convert a bad SEARCH format to the one
980 # used by getRevInfo..
981 $format =~ s/\$createdate/\$createlongdate/gs;
982
983 # SMELL: it looks like $isodate in format is equive to $iso
984 # in renderRevisionInfo
985 # SMELL: clean these 3 hacks up
986 $format =~ s/\$isodate/\$iso/gs;
987 $format =~ s/\%TIME\%/\$date/gs;
988 $format =~ s/\$date/\$longdate/gs;
989
990 # other tmpl based renderings
991 $format =~ s/%WEB%/\$web/g;
992 $format =~ s/%TOPICNAME%/\$topic/g;
993 $format =~ s/%AUTHOR%/\$wikiusername/g;
994
995 # pass search options to summary parser
996 my $searchOptions = {
997 type => $params->{type},
998 wordboundaries => $params->{wordboundaries},
999 casesensitive => $caseSensitive,
1000 tokens => $query ? $query->tokens() : undef,
1001 };
1002
1003 # SMELL: why is this not part of the callback? at least the
1004 # non-result element format strings can be common here.
1005 # or does Sven need a formatCommon sub that formatResult can
1006 # also call.. (which then goes into the callback?
1007 $out = $this->formatResult(
1008 $format,
1009 $info->{tom} || $webObject, #SMELL: horrid hack
1010 $text,
1011 $searchOptions,
1012 {
1013 'ntopics' => sub { return $ntopics },
1014 'nhits' => sub { return $nhits },
1015 'index' => sub { return $thits },
1016 'item' => sub { return $listItem },
1017
1018 %pager_formatting,
1019
1020 'revNum' => sub { return ( $info->{revNum} || 0 ); },
1021 'doBookView' => sub { return $doBookView; },
1022 'baseWeb' => sub { return $baseWeb; },
1023 'baseTopic' => sub { return $baseTopic; },
1024 'newLine' => sub { return $newLine; },
1025 'separator' => sub { return $separator; },
1026 'noTotal' => sub { return $noTotal; },
1027 'paramsHash' => sub { return $params; },
1028 'itemView' => sub { return $itemView; }
1029 },
1030 {
1031
1032 #rev1 info
1033 # TODO: move the $create* formats into Render::renderRevisionInfo..
1034 # which implies moving the infocache's pre-extracted data into the tom obj too.
1035 # $out =~ s/\$create(longdate|username|wikiname|wikiusername)/
1036 # $infoCache->getRev1Info( $topic, "create$1" )/ges;
1037 'createlongdate' => $handleRev1Info,
1038 'createusername' => $handleRev1Info,
1039 'createwikiname' => $handleRev1Info,
1040 'createwikiusername' => $handleRev1Info,
1041 'createusername' => $handleRev1Info,
1042
1043 # rev info
1044 'web' => sub { return $_[1]->web },
1045 'topic' => sub {
1046 if ( defined $_[2] ) {
1047 return Foswiki::Render::breakName( $_[1]->topic,
1048 $_[2] );
1049 }
1050 else {
1051 return $_[1]->topic;
1052 }
1053 },
1054 'rev' => $handleRevInfo,
1055 'wikiusername' => $handleRevInfo,
1056 'wikiname' => $handleRevInfo,
1057 'username' => $handleRevInfo,
1058 'iso' => $handleRevInfo,
1059 'longdate' => $handleRevInfo,
1060 'date' => $handleRevInfo,
1061 }
1062 );
1063 }
1064 else {
106587602.70ms $out = '';
1066 }
1067
1068876040.8ms876017.0ms &$callback( $cbdata, $out );
# spent 17.0ms making 8760 calls to Foswiki::Search::_collate_to_list, avg 2µs/call
1069 } while (@multipleHitLines); # multiple=on loop
1070
1071876024.2ms876027.0s if (
# spent 27.0s making 8760 calls to Foswiki::Iterator::FilterIterator::hasNext, avg 3.09ms/call
1072 ( $params->{paging_on} )
1073 or ( ( defined( $params->{groupby} ) )
1074 and ( $params->{groupby} ne 'web' ) )
1075 )
1076 {
1077 last if ( $ttopics >= $limit );
1078 }
1079 else {
108087602.20ms if ( $ntopics >= $limit ) {
1081 $infoCache->nextWeb();
1082 }
1083 }
1084 } # end topic loop
1085
1086 # output footer only if hits in web
10874025µs if ( $ntopics == 0 ) {
1088 if ( $zeroResults and not $noTotal ) {
1089 $footer = $params->{footercounter};
1090 }
1091 else {
1092 $footer = '';
1093 }
1094 ##MOVEDUP $webObject = new Foswiki::Meta( $session, $baseWeb );
1095 }
1096 else {
10974017µs if ( ( not $noTotal ) and ( defined( $params->{footercounter} ) ) ) {
1098 $footer .= $params->{footercounter};
1099 }
1100
11014032µs if ( $params->{pager} ) {
1102
1103 #auto-append the pager
1104 $footer .= '$pager';
1105 }
1106 }
11074025µs if ( defined $footer ) {
1108
1109#because $pager contains more $ntopics like format strings, it needs to be expanded first.
111040113µs40282µs $footer = $this->formatCommon( $footer, \%pager_formatting );
# spent 282µs making 40 calls to Foswiki::Search::formatCommon, avg 7µs/call
11114050µs $footer =~ s/\$web/$web/gs; # expand name of web
1112
1113 # $footer =~ s/([^\n])$/$1\n/s; # add new line at end
1114
1115 # output footer of $web
11164021µs $footer =~ s/\$ntopics/$ntopics/gs;
111740182µs $footer =~ s/\$nhits/$nhits/gs;
11184027µs $footer =~ s/\$index/$thits/gs;
1119
1120 #legacy SEARCH counter support
11214045µs $footer =~ s/%NTOPICS%/$ntopics/g;
1122
1123 # $footer =~ s/\n$//s; # remove trailing new line
1124
11254071µs4068µs &$callback( $cbdata, $footer );
# spent 68µs making 40 calls to Foswiki::Search::_collate_to_list, avg 2µs/call
1126 }
1127
11284039µs my $result = '';
112940369µs if ( !defined $params->{_callback} && $nhits >= 0 ) {
1130 $result = join( '', @$cbdata );
1131 }
113240275µs return ( $ntopics, $result );
1133}
1134
1135
# spent 282µs within Foswiki::Search::formatCommon which was called 40 times, avg 7µs/call: # 40 times (282µs+0s) by Foswiki::Search::formatResults at line 1110, avg 7µs/call
sub formatCommon {
11364043µs my ( $this, $out, $customKeys ) = @_;
1137
11384023µs my $session = $this->{session};
1139
114040148µs foreach my $key ( keys(%$customKeys) ) {
1141 $out =~ s/\$$key/&{$customKeys->{$key}}()/ges;
1142 }
1143
114440122µs return $out;
1145}
1146
1147=begin TML
1148
1149---++ ObjectMethod formatResult
1150 * $text can be undefined.
1151 * $searchOptions is an options hash to pass on to the summary parser
1152 * $nonTomKeys is a hash of key -> sub($key,$item) where $item is *not* assumed to be a topic object.
1153 * tomKeys is a hash of {'$key' => sub($key,$item,$params) }
1154 where $item is a tom object (initially a Foswiki::Meta, but I'd like to be more generic) and $params is whatever is in trailing ()
1155
1156TODO: i don't really know what we'll need to do about order of processing.
1157TODO: at minimum, the keys need to be sorted by length so that $datatime is processed before $date
1158TODO: need to cater for $summary(params) style too
1159
1160=cut
1161
1162sub formatResult {
1163 my ( $this, $out, $item, $text, $searchOptions, $nonTomKeys, $tomKeys ) =
1164 @_;
1165
1166 my $session = $this->{session};
1167
1168 #TODO: these need to go away.
1169 my $revNum = &{ $nonTomKeys->{'revNum'} }();
1170 my $doBookView = &{ $nonTomKeys->{'doBookView'} }();
1171 my $baseWeb = &{ $nonTomKeys->{'baseWeb'} }();
1172 my $baseTopic = &{ $nonTomKeys->{'baseTopic'} }();
1173 my $newLine = &{ $nonTomKeys->{'newLine'} }();
1174 my $separator = &{ $nonTomKeys->{'separator'} }();
1175 my $noTotal = &{ $nonTomKeys->{'noTotal'} }();
1176 my $params = &{ $nonTomKeys->{'paramsHash'} }();
1177 my $itemView = &{ $nonTomKeys->{'itemView'} }();
1178 foreach my $key (
1179 'revNum', 'doBookView', 'baseWeb', 'baseTopic',
1180 'newLine', 'separator', 'noTotal', 'paramsHash',
1181 'itemView'
1182 )
1183 {
1184 delete $tomKeys->{$key};
1185 delete $nonTomKeys->{$key};
1186 }
1187
1188 # render each item differently, based on SEARCH param 'itemview'
1189 if ( $item->topic
1190 && defined $itemView
1191 && $itemView =~ m/([[:alnum:].\s\(\)\$]+)/ )
1192 {
1193
1194 # brackets added to regex to allow $formfield(name)
1195
1196 # add to skinpath - only to pass as param to readTemplate
1197 $itemView = $1;
1198
1199 # parse formatted search tokens
1200 $itemView =~
1201s/\$formfield\(\s*([^\)]*)\s*\)/displayFormField( $item, $1, $newLine )/ges;
1202 foreach my $key ( keys(%$tomKeys) ) {
1203 $itemView =~ s[\$$key(?:\(([^\)]*)\))?]
1204 [&{$tomKeys->{$key}}($key, $item, $1)]ges;
1205 }
1206
1207 # load the appropriate template for this item
1208 my $tmpl =
1209 $session->templates->readTemplate( ucfirst $itemView . 'ItemView' );
1210 my $text = $session->templates->expandTemplate('LISTITEM');
1211 $out = $text if $text;
1212 }
1213
1214 foreach my $key ( keys(%$nonTomKeys) ) {
1215 $out =~ s/\$$key/&{$nonTomKeys->{$key}}($key, $item)/ges;
1216
1217 #print STDERR "1: $key $out\n";
1218 }
1219 if ( $item->topic ) {
1220
1221 # Only process tomKeys if the item is a valid topicObject
1222 foreach my $key ( keys(%$tomKeys) ) {
1223 $out =~ s[\$$key(?:\(([^\)]*)\))?]
1224 [&{$tomKeys->{$key}}($key, $item, $1)]ges;
1225 }
1226
1227 # Note that we cannot send a formatted search through renderRevisionInfo
1228 # without expanding tokens we should not because the function also sends
1229 # the input through formatTime and probably other nasty filters
1230 # So we send each token through one by one.
1231 if ( $out =~ m/\$text/ ) {
1232 $text = $item->text() unless defined $text;
1233 $text = '' unless defined $text;
1234
1235 if ( $item->topic eq $session->{topicName} ) {
1236
1237#TODO: extract the diffusion and generalise to whatever MACRO we are processing - anything with a format can loop
1238
1239 # defuse SEARCH in current topic to prevent loop
1240 $text =~ s/%SEARCH\{.*?\}%/SEARCH{...}/g;
1241 }
1242 $out =~ s/\$text/$text/gs;
1243 }
1244 }
1245 foreach my $key ( keys(%$nonTomKeys) ) {
1246 $out =~ s/\$$key/&{$nonTomKeys->{$key}}($key, $item)/ges;
1247
1248 #print STDERR "2: $key $out\n";
1249 }
1250
1251 #TODO: extract the rev
1252 my $srev = 'r' . $revNum;
1253 if ( $revNum eq '0' || $revNum eq '1' ) {
1254 $srev = CGI::span( { class => 'foswikiNew' },
1255 ( $session->i18n->maketext('NEW') ) );
1256 }
1257 $out =~ s/%REVISION%/$srev/;
1258
1259 if ($doBookView) {
1260
1261 # BookView
1262 $text = $item->text() unless defined $text;
1263 $text = '' unless defined $text;
1264
1265 if ( $item->web eq $baseWeb && $item->topic eq $baseTopic ) {
1266
1267 # primitive way to prevent recursion
1268 $text =~ s/%SEARCH/%<nop>SEARCH/g;
1269 }
1270 $text = $item->expandMacros($text);
1271 $text = $item->renderTML($text);
1272
1273 $out =~ s/%TEXTHEAD%/$text/g;
1274
1275 }
1276 else {
1277
1278 #TODO: more topic specific bits
1279 if ( defined( $item->topic ) ) {
1280 $out =~ s/\$summary(?:\(([^\)]*)\))?/
1281 $item->summariseText( $1, $text, $searchOptions )/ges;
1282 $out =~ s/\$changes(?:\(([^\)]*)\))?/
1283 $item->summariseChanges(Foswiki::Store::cleanUpRevID($1), $revNum)/ges;
1284 $out =~ s/\$formfield\(\s*([^\)]*)\s*\)/
1285 displayFormField( $item, $1, $newLine )/ges;
1286 $out =~ s/\$parent\(([^\)]*)\)/
1287 Foswiki::Render::breakName(
1288 $item->getParent(), $1 )/ges;
1289 $out =~ s/\$parent/$item->getParent()/ges;
1290 $out =~ s/\$formname/$item->getFormName()/ges;
1291 $out =~ s/\$count\((.*?\s*\.\*)\)/_countPattern( $text, $1 )/ges;
1292
1293 # FIXME: Allow all regex characters but escape them
1294 # Note: The RE requires a .* at the end of a pattern to avoid false positives
1295 # in pattern matching
1296 $out =~
1297 s/\$extract\((.*?\s*\.\*)\)/_extractPattern( $text, $1, 1 )/ges;
1298 $out =~
1299 s/\$pattern\((.*?\s*\.\*)\)/_extractPattern( $text, $1, 0 )/ges;
1300 }
1301 $out =~ s/\r?\n/$newLine/gs if ($newLine);
1302
1303 # If separator is not defined we default to \n
1304 # We also add new line after last search result but before footer
1305 # when separator is not defined for backwards compatibility
1306 # per Item1773 / SearchSeparatorDefaultHeaderFooter
1307 if ( !defined($separator) ) {
1308 unless ( $noTotal && !$params->{formatdefined} ) {
1309 $out =~ s/([^\n])$/$1\n/s;
1310 }
1311 }
1312 }
1313
1314#see http://foswiki.org/Tasks/Item2371 - needs unit test exploration
1315#the problem is that when I separated the formating from the searching, I set the format string to what is in the template,
1316#and thus here, format is always set.
1317# elsif ($noSummary) {
1318# #TODO: i think that means I've broken SEARCH{nosummary=on" with no format specified
1319# $out =~ s/%TEXTHEAD%//g;
1320# $out =~ s/&nbsp;//g;
1321# }
1322# else {
1323# #SEARCH with no format and nonoise="off" or nosummary="off"
1324# #TODO: BROKEN, need to fix the meaning of nosummary and nonoise in SEARCH
1325# # regular search view
1326# $text = $info->{tom}->summariseText( '', $text );
1327# $out =~ s/%TEXTHEAD%/$text/g;
1328# }
1329 return $out;
1330}
1331
1332=begin TML
1333
1334---++ StaticMethod displayFormField( $meta, $args, $newline ) -> $text
1335
1336Parse the arguments to a $formfield specification and extract
1337the relevant formfield from the given meta data.
1338
1339 * =args= string containing name of form field
1340 * =$newline= - replacement text for newlines within the form field, if
1341 not defined defaults to &lt;br />
1342
1343In addition to the name of a field =args= can be appended with a commas
1344followed by a string format (\d+)([,\s*]\.\.\.)?). This supports the formatted
1345search function $formfield and is used to shorten the returned string or a
1346hyphenated string.
1347
1348=cut
1349
1350sub displayFormField {
1351 my ( $meta, $args, $newline ) = @_;
1352
1353 $newline ||= '<br />';
1354 my ( $name, @params ) = split( /,\s*/, $args );
1355 my $format = '$value'; # default is to show the unmapped value
1356 my $breakArgs = '';
1357 if (@params) {
1358 if ( $params[0] eq 'display' ) {
1359
1360 # The displayed value is required
1361 $format = '$value(display)';
1362 shift @params;
1363 }
1364 $breakArgs = join( ',', @params );
1365 }
1366
1367 # SMELL: this is a *terrible* idea. Not only does it munge the result
1368 # so it can only be used for display, it also munges it such that it
1369 # can't be repaired by the options on %SEARCH. :-(
1370 return $meta->renderFormFieldForDisplay(
1371 $name, $format,
1372 {
1373 break => $breakArgs,
1374 protectdollar => 1,
1375 showhidden => 1,
1376 newline => $newline
1377 }
1378 );
1379}
1380
1381#my ($callback, $cbdata) = setup_callback(\%params, $baseWebObject);
1382
# spent 552µs within Foswiki::Search::setup_callback which was called 80 times, avg 7µs/call: # 40 times (314µs+0s) by Foswiki::Search::formatResults at line 582, avg 8µs/call # 40 times (237µs+0s) by Foswiki::Search::searchWeb at line 263, avg 6µs/call
sub setup_callback {
13838063µs my ( $params, $webObj ) = @_;
1384
13858052µs my $callback = $params->{_callback};
13868027µs my $cbdata = $params->{_cbdata};
1387
1388 #add in the rendering..
13898053µs if ( defined( $params->{_callback} ) ) {
1390 $callback = sub {
1391 my $cbdata = shift;
1392 my $text = shift;
1393 my $oldcallback = $params->{_callback};
1394
1395 $text = $webObj->renderTML($text) if defined($webObj);
1396 $text =~ s|</*nop/*>||gi; # remove <nop> tag
1397 &$oldcallback( $cbdata, $text );
1398 };
1399 }
1400 else {
14018065µs $cbdata = $params->{_cbdata} = [] unless ( defined($cbdata) );
14028078µs $callback = \&_collate_to_list;
1403 }
140480225µs return ( $callback, $cbdata );
1405}
1406
1407# callback for search function to collate to list
1408
# spent 17.1ms within Foswiki::Search::_collate_to_list which was called 8800 times, avg 2µs/call: # 8760 times (17.0ms+0s) by Foswiki::Search::formatResults at line 1068, avg 2µs/call # 40 times (68µs+0s) by Foswiki::Search::formatResults at line 1125, avg 2µs/call
sub _collate_to_list {
140988001.78ms my $ref = shift;
1410
1411880034.6ms push( @$ref, @_ );
1412}
1413
141412µs1;
1415__END__