Filename | /var/www/foswiki11/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm |
Statements | Executed 194969 statements in 394ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
41 | 1 | 1 | 450ms | 48.6s | _webQuery | Foswiki::Store::QueryAlgorithms::BruteForce::
8760 | 1 | 1 | 229ms | 448ms | getField | Foswiki::Store::QueryAlgorithms::BruteForce::
41 | 1 | 1 | 7.16ms | 48.7s | query | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 1.45ms | 1.53ms | BEGIN@37 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 238µs | 412µs | BEGIN@31 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 14µs | 28µs | BEGIN@26 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 11µs | 43µs | BEGIN@40 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 9µs | 14µs | BEGIN@27 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 5µs | 5µs | BEGIN@32 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 4µs | 4µs | BEGIN@33 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 3µs | 3µs | BEGIN@36 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 3µs | 3µs | BEGIN@34 | Foswiki::Store::QueryAlgorithms::BruteForce::
1 | 1 | 1 | 3µs | 3µs | BEGIN@35 | Foswiki::Store::QueryAlgorithms::BruteForce::
0 | 0 | 0 | 0s | 0s | getRefTopic | Foswiki::Store::QueryAlgorithms::BruteForce::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Store::QueryAlgorithms::BruteForce | ||||
6 | |||||
7 | Default brute-force query algorithm | ||||
8 | |||||
9 | Has some basic optimisation: it hoists regular expressions out of the | ||||
10 | query to use with grep, so we can narrow down the set of topics that we | ||||
11 | have to evaluate the query on. | ||||
12 | |||||
13 | Not sure exactly where the breakpoint is between the | ||||
14 | costs of hoisting and the advantages of hoisting. Benchmarks suggest | ||||
15 | that it's around 6 topics, though this may vary depending on disk | ||||
16 | speed and memory size. It also depends on the complexity of the query. | ||||
17 | |||||
18 | TODO: There is an additional opprotunity for optimisation; if we assume | ||||
19 | the grep is solid, we can cut those parts of the query out for the full | ||||
20 | evaluation path. Not done yet, because CDot strongly suspects it won't make | ||||
21 | much difference. | ||||
22 | |||||
23 | =cut | ||||
24 | |||||
25 | package Foswiki::Store::QueryAlgorithms::BruteForce; | ||||
26 | 2 | 28µs | 2 | 41µs | # spent 28µs (14+14) within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@26 which was called:
# once (14µs+14µs) by Foswiki::Store::VC::Store::query at line 26 # spent 28µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@26
# spent 14µs making 1 call to strict::import |
27 | 2 | 33µs | 2 | 20µs | # spent 14µs (9+5) within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@27 which was called:
# once (9µs+5µs) by Foswiki::Store::VC::Store::query at line 27 # spent 14µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@27
# spent 5µs making 1 call to warnings::import |
28 | |||||
29 | #@ISA = ( 'Foswiki::Query::QueryAlgorithms' ); # interface | ||||
30 | |||||
31 | 2 | 97µs | 1 | 412µs | # spent 412µs (238+174) within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@31 which was called:
# once (238µs+174µs) by Foswiki::Store::VC::Store::query at line 31 # spent 412µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@31 |
32 | 2 | 20µs | 1 | 5µs | # spent 5µs within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@32 which was called:
# once (5µs+0s) by Foswiki::Store::VC::Store::query at line 32 # spent 5µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@32 |
33 | 2 | 18µs | 1 | 4µs | # spent 4µs within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@33 which was called:
# once (4µs+0s) by Foswiki::Store::VC::Store::query at line 33 # spent 4µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@33 |
34 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@34 which was called:
# once (3µs+0s) by Foswiki::Store::VC::Store::query at line 34 # spent 3µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@34 |
35 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@35 which was called:
# once (3µs+0s) by Foswiki::Store::VC::Store::query at line 35 # spent 3µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@35 |
36 | 2 | 17µs | 1 | 3µs | # spent 3µs within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@36 which was called:
# once (3µs+0s) by Foswiki::Store::VC::Store::query at line 36 # spent 3µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@36 |
37 | 2 | 107µs | 1 | 1.53ms | # spent 1.53ms (1.45+85µs) within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 which was called:
# once (1.45ms+85µs) by Foswiki::Store::VC::Store::query at line 37 # spent 1.53ms making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@37 |
38 | |||||
39 | # See Foswiki::Query::QueryAlgorithms.pm for details | ||||
40 | 2 | 1.26ms | 2 | 76µs | # spent 43µs (11+32) within Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@40 which was called:
# once (11µs+32µs) by Foswiki::Store::VC::Store::query at line 40 # spent 43µs making 1 call to Foswiki::Store::QueryAlgorithms::BruteForce::BEGIN@40
# spent 32µs making 1 call to constant::import |
41 | |||||
42 | # spent 48.7s (7.16ms+48.7) within Foswiki::Store::QueryAlgorithms::BruteForce::query which was called 41 times, avg 1.19s/call:
# 41 times (7.16ms+48.7s) by Foswiki::Store::VC::Store::query at line 518 of /var/www/foswiki11/lib/Foswiki/Store/VC/Store.pm, avg 1.19s/call | ||||
43 | 41 | 53µs | my ( $query, $inputTopicSet, $session, $options ) = @_; | ||
44 | |||||
45 | # Fold constants | ||||
46 | 41 | 156µs | 41 | 681µs | my $context = Foswiki::Meta->new( $session, $session->{webName} ); # spent 681µs making 41 calls to Foswiki::Meta::new, avg 17µs/call |
47 | print STDERR "--- before: " . $query->toString() . "\n" if MONITOR; | ||||
48 | 41 | 177µs | 41 | 3.58ms | $query->simplify( tom => $context, data => $context ); # spent 3.58ms making 41 calls to Foswiki::Query::Node::simplify, avg 87µs/call |
49 | print STDERR "--- simplified: " . $query->toString() . "\n" if MONITOR; | ||||
50 | |||||
51 | 41 | 72µs | my $webNames = $options->{web} || ''; | ||
52 | 41 | 42µs | my $recurse = $options->{'recurse'} || ''; | ||
53 | 41 | 272µs | 41 | 412µs | my $isAdmin = $session->{users}->isAdmin( $session->{user} ); # spent 412µs making 41 calls to Foswiki::Users::isAdmin, avg 10µs/call |
54 | |||||
55 | 41 | 55µs | my $searchAllFlag = ( $webNames =~ /(^|[\,\s])(all|on)([\,\s]|$)/i ); | ||
56 | 41 | 205µs | 41 | 2.77ms | my @webs = Foswiki::Search::InfoCache::_getListOfWebs( $webNames, $recurse, # spent 2.77ms making 41 calls to Foswiki::Search::InfoCache::_getListOfWebs, avg 68µs/call |
57 | $searchAllFlag ); | ||||
58 | |||||
59 | 41 | 9µs | my @resultCacheList; | ||
60 | 41 | 38µs | foreach my $web (@webs) { | ||
61 | |||||
62 | # can't process what ain't thar | ||||
63 | 41 | 129µs | 41 | 2.44ms | next unless $session->webExists($web); # spent 2.44ms making 41 calls to Foswiki::webExists, avg 59µs/call |
64 | |||||
65 | 41 | 146µs | 41 | 649µs | my $webObject = Foswiki::Meta->new( $session, $web ); # spent 649µs making 41 calls to Foswiki::Meta::new, avg 16µs/call |
66 | 41 | 244µs | 82 | 11.8ms | my $thisWebNoSearchAll = # spent 11.5ms making 41 calls to Foswiki::Meta::getPreference, avg 281µs/call
# spent 313µs making 41 calls to Foswiki::isTrue, avg 8µs/call |
67 | Foswiki::isTrue( $webObject->getPreference('NOSEARCHALL') ); | ||||
68 | |||||
69 | # make sure we can report this web on an 'all' search | ||||
70 | # DON'T filter out unless it's part of an 'all' search. | ||||
71 | next | ||||
72 | 41 | 12µs | if ( $searchAllFlag | ||
73 | && !$isAdmin | ||||
74 | && ( $thisWebNoSearchAll || $web =~ /^[\.\_]/ ) | ||||
75 | && $web ne $session->{webName} ); | ||||
76 | |||||
77 | #TODO: combine these into one great ResultSet | ||||
78 | 41 | 123µs | 41 | 48.6s | my $infoCache = # spent 48.6s making 41 calls to Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery, avg 1.19s/call |
79 | _webQuery( $query, $web, $inputTopicSet, $session, $options ); | ||||
80 | 41 | 293µs | push( @resultCacheList, $infoCache ); | ||
81 | } | ||||
82 | 41 | 386µs | 82 | 391µs | my $resultset = # spent 271µs making 41 calls to Foswiki::Search::ResultSet::new, avg 7µs/call
# spent 120µs making 41 calls to Foswiki::isTrue, avg 3µs/call |
83 | new Foswiki::Search::ResultSet( \@resultCacheList, $options->{groupby}, | ||||
84 | $options->{order}, Foswiki::isTrue( $options->{reverse} ) ); | ||||
85 | |||||
86 | # first filter, then sort the remaining | ||||
87 | 41 | 47µs | my $date = $options->{'date'} || ''; | ||
88 | 41 | 12µs | if ($date) { | ||
89 | $resultset->filterByDate($date); | ||||
90 | } | ||||
91 | |||||
92 | #TODO: $options should become redundant | ||||
93 | 41 | 77µs | 41 | 54.0ms | $resultset->sortResults($options); # spent 54.0ms making 41 calls to Foswiki::Search::ResultSet::sortResults, avg 1.32ms/call |
94 | 41 | 448µs | return $resultset; | ||
95 | } | ||||
96 | |||||
97 | # Query over a single web | ||||
98 | # spent 48.6s (450ms+48.1) within Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery which was called 41 times, avg 1.19s/call:
# 41 times (450ms+48.1s) by Foswiki::Store::QueryAlgorithms::BruteForce::query at line 78, avg 1.19s/call | ||||
99 | 41 | 54µs | my ( $query, $web, $inputTopicSet, $session, $options ) = @_; | ||
100 | |||||
101 | 41 | 163µs | 41 | 924µs | my $resultTopicSet = # spent 924µs making 41 calls to Foswiki::Search::InfoCache::new, avg 23µs/call |
102 | new Foswiki::Search::InfoCache( $Foswiki::Plugins::SESSION, $web ); | ||||
103 | |||||
104 | # see if this query can be fasttracked. | ||||
105 | # TODO: is this simplification call appropriate here, or should it | ||||
106 | # go in Search.pm | ||||
107 | # TODO: what about simplify to constant in _this_ web? | ||||
108 | 41 | 12µs | my $queryIsAConstantFastpath; # undefined if this is a 'real' query' | ||
109 | 41 | 72µs | 41 | 2.37ms | $query->simplify(); # spent 2.37ms making 41 calls to Foswiki::Query::Node::simplify, avg 58µs/call |
110 | 41 | 78µs | 41 | 353µs | if ( $query->evaluatesToConstant() ) { # spent 353µs making 41 calls to Foswiki::Query::Node::evaluatesToConstant, avg 9µs/call |
111 | print STDERR "-- constant?\n" if MONITOR; | ||||
112 | |||||
113 | # SMELL: use any old topic | ||||
114 | 1 | 10µs | 3 | 737µs | my $cache = $Foswiki::Plugins::SESSION->search->metacache->get( $web, # spent 731µs making 1 call to Foswiki::MetaCache::get
# spent 4µs making 1 call to Foswiki::Search::metacache
# spent 2µs making 1 call to Foswiki::search |
115 | 'WebPreferences' ); | ||||
116 | 1 | 900ns | my $meta = $cache->{tom}; | ||
117 | 1 | 4µs | 1 | 23µs | $queryIsAConstantFastpath = # spent 23µs making 1 call to Foswiki::Query::Node::evaluate |
118 | $query->evaluate( tom => $meta, data => $meta ); | ||||
119 | } | ||||
120 | |||||
121 | 41 | 21µs | if ( defined($queryIsAConstantFastpath) ) { | ||
122 | 1 | 300ns | if ( not $queryIsAConstantFastpath ) { | ||
123 | print STDERR "-- no results\n" if MONITOR; | ||||
124 | |||||
125 | #CONSTANT _and_ FALSE - return no results | ||||
126 | return $resultTopicSet; | ||||
127 | } | ||||
128 | } | ||||
129 | else { | ||||
130 | print STDERR "-- not constant\n" if MONITOR; | ||||
131 | |||||
132 | # from here on, FALSE means its not a constant, TRUE | ||||
133 | # means is is a constant and evals to TRUE | ||||
134 | 40 | 22µs | $queryIsAConstantFastpath = 0; | ||
135 | } | ||||
136 | |||||
137 | # Try and hoist regular expressions out of the query that we | ||||
138 | # can use to refine the topic set | ||||
139 | |||||
140 | 41 | 194µs | 41 | 3.02ms | my $hoistedREs = Foswiki::Query::HoistREs::collatedHoist($query); # spent 3.02ms making 41 calls to Foswiki::Query::HoistREs::collatedHoist, avg 74µs/call |
141 | |||||
142 | # Reduce the input topic set by matching simple topic names hoisted | ||||
143 | # from the query. | ||||
144 | |||||
145 | 41 | 40µs | if ( ( !defined( $options->{topic} ) ) | ||
146 | and ( $hoistedREs->{name} ) | ||||
147 | and ( scalar( @{ $hoistedREs->{name} } ) == 1 ) ) | ||||
148 | { | ||||
149 | |||||
150 | # only do this if the 'name' query is simple | ||||
151 | # (ie, has only one element) | ||||
152 | my @filter = @{ $hoistedREs->{name_source} }; | ||||
153 | |||||
154 | #set the 'includetopic' matcher.. | ||||
155 | $options->{topic} = $filter[0]; | ||||
156 | } | ||||
157 | |||||
158 | # Reduce the input topic set by matching the hoisted REs against | ||||
159 | # the topics in it. | ||||
160 | |||||
161 | 41 | 18µs | my $topicSet = $inputTopicSet; | ||
162 | 41 | 29µs | if ( !defined($topicSet) ) { | ||
163 | print STDERR "-- new topic Set from $web\n" if MONITOR; | ||||
164 | |||||
165 | # then we start with the whole web? | ||||
166 | # TODO: i'm sure that is a flawed assumption | ||||
167 | 41 | 134µs | 41 | 655µs | my $webObject = Foswiki::Meta->new( $session, $web ); # spent 655µs making 41 calls to Foswiki::Meta::new, avg 16µs/call |
168 | 41 | 116µs | 41 | 333ms | $topicSet = # spent 333ms making 41 calls to Foswiki::Search::InfoCache::getTopicListIterator, avg 8.11ms/call |
169 | Foswiki::Search::InfoCache::getTopicListIterator( $webObject, | ||||
170 | $options ); | ||||
171 | } | ||||
172 | |||||
173 | # TODO: how to ask iterator for list length? | ||||
174 | # TODO: once the inputTopicSet isa ResultSet we might have an idea | ||||
175 | # TODO: I presume $hoisetedRE's is undefined for constant queries.. | ||||
176 | # if (() and ( scalar(@$topics) > 6 )) { | ||||
177 | 41 | 8.98ms | if ( defined( $hoistedREs->{text} ) ) { | ||
178 | 40 | 79µs | my $searchOptions = { | ||
179 | type => 'regex', | ||||
180 | casesensitive => 1, | ||||
181 | files_without_match => 1, | ||||
182 | }; | ||||
183 | 40 | 51µs | my @filter = @{ $hoistedREs->{text} }; | ||
184 | 40 | 327µs | 80 | 2.97ms | my $searchQuery = # spent 2.74ms making 40 calls to Foswiki::Query::Node::toString, avg 68µs/call
# spent 234µs making 40 calls to Foswiki::Search::Node::new, avg 6µs/call |
185 | new Foswiki::Search::Node( $query->toString(), \@filter, | ||||
186 | $searchOptions ); | ||||
187 | |||||
188 | #use Data::Dumper; | ||||
189 | #print STDERR "--- hoisted: ".Dumper($hoistedREs)."\n" if MONITOR; | ||||
190 | |||||
191 | 40 | 106µs | 40 | 480µs | $topicSet->reset(); # spent 480µs making 40 calls to Foswiki::Iterator::FilterIterator::reset, avg 12µs/call |
192 | 40 | 805µs | 40 | 43.5s | $topicSet = # spent 43.5s making 40 calls to Foswiki::Store::VC::Store::searchInWebMetaData, avg 1.09s/call |
193 | $session->{store} | ||||
194 | ->searchInWebMetaData( $searchQuery, $web, $topicSet, $session, | ||||
195 | $searchOptions ); | ||||
196 | } | ||||
197 | else { | ||||
198 | |||||
199 | # TODO: clearly _this_ can be re-written as a FilterIterator, | ||||
200 | # and if we are able to use the sorting hints (ie DB Store) | ||||
201 | # can propogate all the way to FORMAT | ||||
202 | |||||
203 | 1 | 300ns | print STDERR "WARNING: couldn't hoistREs on " . $query->toString() | ||
204 | if MONITOR; | ||||
205 | } | ||||
206 | |||||
207 | 41 | 177µs | local $/; | ||
208 | 41 | 475µs | 41 | 813µs | $topicSet->reset(); # spent 798µs making 40 calls to Foswiki::Iterator::reset, avg 20µs/call
# spent 15µs making 1 call to Foswiki::Iterator::FilterIterator::reset |
209 | 41 | 248µs | 41 | 3.17ms | while ( $topicSet->hasNext() ) { # spent 2.57ms making 40 calls to Foswiki::Search::ResultSet::hasNext, avg 64µs/call
# spent 599µs making 1 call to Foswiki::Iterator::FilterIterator::hasNext |
210 | 8845 | 14.8ms | 8845 | 79.2ms | my $webtopic = $topicSet->next(); # spent 78.8ms making 8760 calls to Foswiki::Search::ResultSet::next, avg 9µs/call
# spent 424µs making 85 calls to Foswiki::Iterator::FilterIterator::next, avg 5µs/call |
211 | 8845 | 17.2ms | 8845 | 237ms | my ( $Iweb, $topic ) = # spent 237ms making 8845 calls to Foswiki::Func::normalizeWebTopicName, avg 27µs/call |
212 | Foswiki::Func::normalizeWebTopicName( $web, $webtopic ); | ||||
213 | print STDERR "-- $Iweb, $topic\n" if MONITOR; | ||||
214 | |||||
215 | 8845 | 21.3ms | 8845 | 294ms | if ($queryIsAConstantFastpath) { # spent 236ms making 8760 calls to Foswiki::Search::ResultSet::hasNext, avg 27µs/call
# spent 58.1ms making 85 calls to Foswiki::Iterator::FilterIterator::hasNext, avg 683µs/call |
216 | print STDERR "-- add $Iweb, $topic\n" if MONITOR; | ||||
217 | 85 | 40µs | if ( defined( $options->{date} ) ) { | ||
218 | |||||
219 | # TODO: preload the meta cache if we're doing date | ||||
220 | # based filtering - else the wrong filedate will be used | ||||
221 | $Foswiki::Plugins::SESSION->search->metacache->get( $Iweb, | ||||
222 | $topic ); | ||||
223 | } | ||||
224 | |||||
225 | # TODO: frustratingly, there is no way to evaluate a | ||||
226 | # filterIterator without actually iterating over it.. | ||||
227 | 85 | 131µs | 85 | 2.01ms | $resultTopicSet->addTopics( $Iweb, $topic ); # spent 2.01ms making 85 calls to Foswiki::Search::InfoCache::addTopics, avg 24µs/call |
228 | } | ||||
229 | else { | ||||
230 | 8760 | 35.8ms | 26280 | 927ms | my $cache = # spent 891ms making 8760 calls to Foswiki::MetaCache::get, avg 102µs/call
# spent 19.1ms making 8760 calls to Foswiki::search, avg 2µs/call
# spent 17.1ms making 8760 calls to Foswiki::Search::metacache, avg 2µs/call |
231 | $Foswiki::Plugins::SESSION->search->metacache->get( $Iweb, | ||||
232 | $topic ); | ||||
233 | |||||
234 | 8760 | 8.93ms | my $meta = $cache->{tom}; | ||
235 | 8760 | 1.20ms | next unless ( defined($meta) ); #not a valid or loadable topic | ||
236 | |||||
237 | # this 'lazy load' will become useful when @$topics becomes | ||||
238 | # an infoCache | ||||
239 | 8760 | 14.0ms | 8760 | 54.6ms | $meta = $meta->load() unless ( $meta->latestIsLoaded() ); # spent 54.6ms making 8760 calls to Foswiki::Meta::latestIsLoaded, avg 6µs/call |
240 | 8760 | 10.9ms | 8760 | 9.10ms | print STDERR "Processing $topic\n" # spent 9.10ms making 8760 calls to Foswiki::Query::Node::MONITOR_EVAL, avg 1µs/call |
241 | if Foswiki::Query::Node::MONITOR_EVAL; | ||||
242 | 8760 | 15.0ms | 8760 | 1.99s | my $match = $query->evaluate( tom => $meta, data => $meta ); # spent 1.99s making 8760 calls to Foswiki::Query::Node::evaluate, avg 227µs/call |
243 | 8760 | 19.1ms | 8760 | 701ms | if ($match) { # spent 701ms making 8760 calls to Foswiki::Search::InfoCache::addTopic, avg 80µs/call |
244 | $resultTopicSet->addTopic($meta); | ||||
245 | } | ||||
246 | } | ||||
247 | } | ||||
248 | |||||
249 | 41 | 1.01ms | return $resultTopicSet; | ||
250 | } | ||||
251 | |||||
252 | # The getField function is here to allow for Store specific optimisations | ||||
253 | # such as direct database lookups. | ||||
254 | # spent 448ms (229+219) within Foswiki::Store::QueryAlgorithms::BruteForce::getField which was called 8760 times, avg 51µs/call:
# 8760 times (229ms+219ms) by Foswiki::Query::Node::evaluate at line 171 of /var/www/foswiki11/lib/Foswiki/Query/Node.pm, avg 51µs/call | ||||
255 | 8760 | 8.92ms | my ( $this, $node, $data, $field ) = @_; | ||
256 | |||||
257 | 8760 | 1.28ms | my $result; | ||
258 | 8760 | 60.6ms | 8760 | 12.2ms | if ( UNIVERSAL::isa( $data, 'Foswiki::Meta' ) ) { # spent 12.2ms making 8760 calls to UNIVERSAL::isa, avg 1µs/call |
259 | |||||
260 | # The object being indexed is a Foswiki::Meta object, so | ||||
261 | # we have to use a different approach to treating it | ||||
262 | # as an associative array. The first thing to do is to | ||||
263 | # apply our "alias" shortcuts. | ||||
264 | 8760 | 3.17ms | my $realField = $field; | ||
265 | 8760 | 4.59ms | if ( $Foswiki::Query::Node::aliases{$field} ) { | ||
266 | $realField = $Foswiki::Query::Node::aliases{$field}; | ||||
267 | } | ||||
268 | 8760 | 2.51ms | if ( $realField eq 'META:TOPICINFO' ) { | ||
269 | |||||
270 | # Ensure the revision info is populated from the store | ||||
271 | $data->getRevisionInfo(); | ||||
272 | } | ||||
273 | 8760 | 30.7ms | 8760 | 29.7ms | if ( $realField =~ s/^META:// ) { # spent 29.7ms making 8760 calls to Foswiki::Meta::topic, avg 3µs/call |
274 | if ( $Foswiki::Query::Node::isArrayType{$realField} ) { | ||||
275 | |||||
276 | # Array type, have to use find | ||||
277 | my @e = $data->find($realField); | ||||
278 | $result = \@e; | ||||
279 | } | ||||
280 | else { | ||||
281 | $result = $data->get($realField); | ||||
282 | } | ||||
283 | } | ||||
284 | elsif ( $realField eq 'name' ) { | ||||
285 | |||||
286 | # Special accessor to compensate for lack of a topic | ||||
287 | # name anywhere in the saved fields of meta | ||||
288 | return $data->topic(); | ||||
289 | } | ||||
290 | elsif ( $realField eq 'text' ) { | ||||
291 | |||||
292 | # Special accessor to compensate for lack of the topic text | ||||
293 | # name anywhere in the saved fields of meta | ||||
294 | return $data->text(); | ||||
295 | } | ||||
296 | elsif ( $realField eq 'web' ) { | ||||
297 | |||||
298 | # Special accessor to compensate for lack of a web | ||||
299 | # name anywhere in the saved fields of meta | ||||
300 | return $data->web(); | ||||
301 | } | ||||
302 | elsif ( $data->topic() ) { | ||||
303 | |||||
304 | # The field name isn't an alias, check to see if it's | ||||
305 | # the form name | ||||
306 | 8760 | 13.8ms | 8760 | 95.0ms | my $form = $data->get('FORM'); # spent 95.0ms making 8760 calls to Foswiki::Meta::get, avg 11µs/call |
307 | 8760 | 13.0ms | if ( $form && $field eq $form->{name} ) { | ||
308 | |||||
309 | # SHORTCUT;it's the form name, so give me the fields | ||||
310 | # as if the 'field' keyword had been used. | ||||
311 | # TODO: This is where multiple form support needs to reside. | ||||
312 | # Return the array of FIELD for further indexing. | ||||
313 | my @e = $data->find('FIELD'); | ||||
314 | return \@e; | ||||
315 | } | ||||
316 | else { | ||||
317 | |||||
318 | # SHORTCUT; not a predefined name; assume it's a field | ||||
319 | # 'name' instead. | ||||
320 | # SMELL: Needs to error out if there are multiple forms - | ||||
321 | # or perhaps have a heuristic that gives access to the | ||||
322 | # uniquely named field. | ||||
323 | 8760 | 14.0ms | 8760 | 82.3ms | $result = $data->get( 'FIELD', $field ); # spent 82.3ms making 8760 calls to Foswiki::Meta::get, avg 9µs/call |
324 | 8760 | 13.3ms | $result = $result->{value} if $result; | ||
325 | } | ||||
326 | } | ||||
327 | } | ||||
328 | elsif ( ref($data) eq 'ARRAY' ) { | ||||
329 | |||||
330 | # Array objects are returned during evaluation, e.g. when | ||||
331 | # a subset of an array is matched for further processing. | ||||
332 | |||||
333 | # Indexing an array object. The index will be one of: | ||||
334 | # 1. An integer, which is an implicit index='x' query | ||||
335 | # 2. A name, which is an implicit name='x' query | ||||
336 | if ( $field =~ /^\d+$/ ) { | ||||
337 | |||||
338 | # Integer index | ||||
339 | $result = $data->[$field]; | ||||
340 | } | ||||
341 | else { | ||||
342 | |||||
343 | # String index | ||||
344 | my @res; | ||||
345 | |||||
346 | # Get all array entries that match the field | ||||
347 | foreach my $f (@$data) { | ||||
348 | my $val = getField( undef, $node, $f, $field ); | ||||
349 | push( @res, $val ) if defined($val); | ||||
350 | } | ||||
351 | if ( scalar(@res) ) { | ||||
352 | $result = \@res; | ||||
353 | } | ||||
354 | else { | ||||
355 | |||||
356 | # The field name wasn't explicitly seen in any of the records. | ||||
357 | # Try again, this time matching 'name' and returning 'value' | ||||
358 | foreach my $f (@$data) { | ||||
359 | next unless ref($f) eq 'HASH'; | ||||
360 | if ( $f->{name} | ||||
361 | && $f->{name} eq $field | ||||
362 | && defined $f->{value} ) | ||||
363 | { | ||||
364 | push( @res, $f->{value} ); | ||||
365 | } | ||||
366 | } | ||||
367 | if ( scalar(@res) ) { | ||||
368 | $result = \@res; | ||||
369 | } | ||||
370 | } | ||||
371 | } | ||||
372 | } | ||||
373 | elsif ( ref($data) eq 'HASH' ) { | ||||
374 | |||||
375 | # A hash object may be returned when a sub-object of a Foswiki::Meta | ||||
376 | # object has been matched. | ||||
377 | $result = $data->{ $node->{params}[0] }; | ||||
378 | } | ||||
379 | else { | ||||
380 | $result = $node->{params}[0]; | ||||
381 | } | ||||
382 | 8760 | 52.1ms | return $result; | ||
383 | } | ||||
384 | |||||
385 | # Get a referenced topic | ||||
386 | # See Foswiki::Store::QueryAlgorithms.pm for details | ||||
387 | sub getRefTopic { | ||||
388 | my ( $this, $relativeTo, $w, $t ) = @_; | ||||
389 | return Foswiki::Meta->load( $relativeTo->session, $w, $t ); | ||||
390 | } | ||||
391 | |||||
392 | 1 | 2µs | 1; | ||
393 | __END__ |