source: server/trunk/web/lib/Sophie/Controller/Search.pm @ 130

Last change on this file since 130 was 130, checked in by nanardon, 13 years ago
  • allow using glob like search on dirname part
File size: 16.6 KB
Line 
1package Sophie::Controller::Search;
2use Moose;
3use namespace::autoclean;
4use Sophie;
5
6BEGIN {extends 'Catalyst::Controller'; }
7
8=head1 NAME
9
10Sophie::Controller::Search - Catalyst Controller
11
12=head1 DESCRIPTION
13
14Catalyst Controller.
15
16=head1 METHODS
17
18=cut
19
20
21=head2 index
22
23=cut
24
25sub index :Path :Args(0) {
26    my ($self, $c) = @_;
27
28    if ($c->req->param('page')) {
29        $c->req->params->{search} = $c->session->{search};
30        $c->req->params->{type} = $c->session->{type};
31        $c->req->params->{deptype} = $c->session->{deptype};
32    } else {
33        $c->session->{search} = $c->req->params->{search};
34        $c->session->{type} = $c->req->params->{type};
35        $c->session->{deptype} = $c->req->params->{deptype};
36    }
37
38    my $searchspec = {
39        page => $c->req->param('page') || undef,
40    };
41
42    for ($c->req->param('type')) {
43        /^byname$/ and do {
44            $c->forward('byname', [ $searchspec, $c->req->param('search') ||
45                    undef ]);
46            last;
47        };
48        /^bydep$/ and do {
49            $c->forward('bydep', [ $searchspec, $c->req->param('deptype'), grep { $_ } split (/\s+/,
50                        $c->req->param('search') || '') ]);
51            last;
52        };
53    }
54
55}
56
57sub results :Local {
58    my ( $self, $c ) = @_;
59
60    if ($c->req->param('page')) {
61        $c->req->params->{search} ||= $c->session->{search};
62    }
63
64    if ($c->req->param('search')) {
65        $c->session->{search} = $c->req->param('search');
66        $c->forward('quick', [
67                {
68                    page => $c->req->param('page') || undef,
69                    src => 0,
70                } , grep { $_ } split(/\s/, $c->req->param('search')) ]);
71
72    }
73}
74
75sub adv_search :Local {
76    my ( $self, $c ) = @_;
77}
78
79sub distrib_search : Private {
80    my ( $self, $c, $searchspec, $asfilter ) = @_;
81
82    # if asfilter is set, return undef if nothing would have been filter
83    if (my $rs = $c->forward('/distrib/distrib_rs', [ $searchspec, $asfilter ]))
84    {
85        return $rs
86            ->search_related('MediasPaths')
87            ->search_related('Paths')
88            ->search_related('Rpmfiles');
89        } else {
90            return;
91        }
92}
93
94sub format_search : Private {
95    my ( $self, $c, $searchspec ) = @_;
96    $searchspec ||= {};
97
98    my $rs = $c->stash->{rs}->search(
99        {},
100        {
101            page => $searchspec->{page} || 1,
102            rows => $searchspec->{rows} || 10,
103        },
104    );
105
106    $c->stash->{rs} = $rs;
107    $c->stash->{column} ||= 'pkgid';
108    my @results;
109    if (ref $c->stash->{column}) {
110        while (my $i = $rs->next) {
111            push(@results, {
112                map { $_ => $i->get_column($_) } @{$c->stash->{column}} 
113            });
114        }
115    } else {
116        @results = $rs->get_column($c->stash->{column})->all;
117    }
118    $c->stash->{xmlrpc} = {};
119    if (!$searchspec->{nopager}) {
120        my $pager = $c->stash->{rs}->pager;
121        $c->stash->{pager} = $pager;
122        $c->stash->{xmlrpc} = {
123                pages => $pager->last_page,
124                current_page => $pager->current_page,
125                total_entries => $pager->total_entries,
126                entries_per_page => $pager->entries_per_page,
127        };
128    }
129    $c->stash->{xmlrpc}{results} = \@results;
130    return $c->stash->{xmlrpc};
131}
132
133=head2 search.rpms.bydate (SEARCHSPEC, TIMESTAMP)
134
135Return a list of rpms files added since TIMESTAMP.
136TIMESTAMP must the number of second since 1970-01-01 (eq UNIX epoch).
137
138SEARCHSPEC is a struct with following key/value:
139
140=over 4
141
142=item distribution
143
144Limit search to this distribution
145
146=item release
147
148Limit search to this release
149
150=item arch
151
152Limit search to distribution of this arch
153
154=item src
155
156If set to true, limit search to source package, If set to false, limit search to
157binary package.
158
159=item name
160
161Limit search to rpm having this name
162
163=item rows
164
165Set maximum of results, the default is 10000.
166
167=back
168
169Each elements of the output is a struct:
170
171=over 4
172
173=item filename
174
175the rpm filename
176
177=item pkgid
178
179the identifier of the package
180
181=item distribution
182
183the distribution containing this package
184
185=item release
186
187the release containing this package
188
189=item arch
190
191the arch containing this package
192
193=item media
194
195the media containing this package
196
197=back
198
199=cut
200
201sub bydate : XMLRPCPath('/search/rpms/bydate') {
202    my ( $self, $c, $searchspec, $date ) = @_;
203    $searchspec ||= {};
204
205    return $c->stash->{xmlrpc} = [
206        map {
207            { 
208                filename => $_->get_column('filename'),
209                pkgid    => $_->get_column('pkgid'), 
210                distribution => $_->get_column('name'),
211                release => $_->get_column('version'),
212                arch => $_->get_column('arch'),
213                media => $_->get_column('label'),
214            }
215        }
216        $c->forward('/distrib/distrib_rs', [ $searchspec ])
217        ->search_related('MediasPaths')
218        ->search_related('Paths')
219        ->search_related('Rpmfiles',
220            {
221                -nest => \[
222                    "Rpmfiles.added > '1970-01-01'::date + ?::interval",
223                    [ plain_text => "$date seconds" ],   
224                ],
225                pkgid => {
226                    IN => $c->model('Base::Rpms')->search(
227                        {
228                            (exists($searchspec->{name})
229                                ? (name => $searchspec->{name})
230                                : ()
231                            ),
232                            (exists($searchspec->{src})
233                                ? (issrc => $searchspec->{src} ? 1 : 0)
234                                : ()
235                            ),
236                        }
237                    )->get_column('pkgid')->as_query,
238                }
239            },
240            {
241                select => [qw(filename pkgid name version arch label) ],
242                rows => $searchspec->{rows} || 10000,
243                order_by => [ 'Rpmfiles.added desc' ],
244            },
245        )->all ];
246}
247
248sub bypkgid : XMLRPCPath('/search/rpm/bypkgid') {
249    my ( $self, $c, $searchspec, $pkgid ) = @_;
250    $searchspec ||= {};
251
252    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
253
254    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
255        {
256            -and => [ 
257                (exists($searchspec->{src})
258                    ? { issrc => $searchspec->{src} ? 1 : 0 }
259                    : ()),
260                { pkgid => $pkgid },
261                $distrs
262                    ? { pkgid => { IN => $distrs->get_column('pkgid')->as_query, } }
263                    : ()
264            ]     
265        },
266    );
267
268    $c->forward('format_search', $searchspec);
269}
270
271=head2 search.rpm.byname (SEARCHSPEC, NAME, [SENSE, EVR])
272
273Search package by its NAME. SENSE and EVR are optional version filter where
274SENSE is dependency sign (C<E<gt>>, C<=>, ...) and EVR the search version as
275either C<VERSION>, C<VERSION-RELEASE> or C<EPOCH:VERSION-RELEASE>.
276
277SEARCHSPEC is a struct with search options.
278
279=cut
280
281sub byname : XMLRPCPath('/search/rpm/byname') {
282    my ( $self, $c, $searchspec, $name, $sense, $evr ) = @_;
283    $searchspec ||= {};
284
285    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
286
287    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
288        {
289            -and => [ 
290                (exists($searchspec->{src})
291                    ? { issrc => $searchspec->{src} ? 1 : 0 }
292                    : ()),
293                { name => $name },
294                ( $evr
295                    ? { -nest => \[ 
296                        "rpmdepmatch(rpmsenseflag('='), evr, rpmsenseflag(?), ?)",
297                        [ plain_text => $sense],
298                        [ plain_text => $evr ],
299                    ] }
300                    : ()),
301                ($distrs
302                    ? { pkgid => { IN => $distrs->get_column('pkgid')->as_query, }, }
303                    : ()),
304            ]     
305        },
306        {
307                order_by => [ 'name', 'evr using >>', 'issrc' ],
308        }
309    );
310    $c->forward('format_search', $searchspec);
311
312}
313
314sub bytag : XMLRPCPath('/search/rpm/bytag') {
315    my ( $self, $c, $searchspec, $tag, $tagvalue ) = @_;
316    $searchspec ||= {};
317
318    my $tagrs = $c->model('Base')->resultset('Tags')
319        ->search({ tagname => lc($tag), value => $tagvalue})
320        ->get_column('pkgid');
321    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
322    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
323        {
324            -and => [ 
325                (exists($searchspec->{src})
326                    ? { issrc => $searchspec->{src} ? 1 : 0 }
327                    : ()),
328                { pkgid => 
329                    { IN => $tagrs->as_query, },
330                },
331                $distrs
332                    ? { pkgid => { IN => $distrs->get_column('pkgid')->as_query, }, }
333                    : (),
334            ]     
335        },
336    );
337    $c->forward('format_search', $searchspec);
338
339}
340
341sub deps_rs : Private {
342    my ($self, $c, $searchspec, $deptype, $depname, $depsense, $depevr ) = @_;
343
344    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
345
346    return $c->model('Base::Deps')->search(
347        {
348            -and => [
349            { deptype => $deptype },
350            { depname => $depname },
351            ($depsense
352                ? ({-nest => \[
353                    'rpmdepmatch(flags, evr, rpmsenseflag(?), ?)',
354                    [ plain_text => $depsense],
355                    [ plain_text => $depevr ]
356                ]})
357            : ()),
358            ($distrs 
359                ? ({ pkgid => { IN => $distrs->get_column('pkgid')->as_query,
360                        },})
361                : ()),
362            (exists($searchspec->{src})
363                ? { pkgid => { IN => $c->model('Base::Rpms')->search(
364                            { issrc => $searchspec->{src} ? 1 : 0 }
365                        )->get_column('pkgid')->as_query, }, }
366                : ()),
367            ($searchspec->{pkgid}
368                ? { pkgid => $searchspec->{pkgid} }
369                : ()),
370            ]
371        },
372        {
373            '+select' => [ { rpmsenseflag => 'flags' }, 'depname', ],
374            '+as'     => [ qw(sense name) ],
375
376        }
377    );
378}
379
380sub file_rs : Private {
381    my ( $self, $c, $searchspec, $file) = @_;
382    my ($dirname, $basename) = $file =~ m:^(.*/)?([^/]+)$:;
383    $dirname =~ m:^[/]: or $dirname = undef;
384    if (!$dirname) {
385        if ($file =~ /(\*|\?)/) {
386            $file =~ tr/*?/%_/;
387        } else {
388            $file = '%' . $file;
389        }
390    }
391    $searchspec ||= {};
392
393    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
394
395    return $c->model('Base::Files')->search(
396        {
397            -and => [
398                ($dirname
399                    ? (dirname => $dirname)
400                    : ()),
401                { 'dirname || basename' => { LIKE => $file } },
402                basename => $basename,
403                ($searchspec->{content} ? { has_content => 1 } : ()),
404                ($distrs 
405                    ? (pkgid => { IN => $distrs->get_column('pkgid')->as_query, },)
406                    : ()),
407                ($searchspec->{pkgid}
408                    ? { pkgid => $searchspec->{pkgid} }
409                    : ()),
410            ],
411        },
412        {
413            '+select' => [
414                'contents is NOT NULL as has_content',
415                { rpmfilesmode => 'mode' },
416            ],
417            '+as' => [ qw(has_content perm), ],
418        }
419    );
420}
421
422sub bydep : XMLRPCPath('/search/rpm/bydep') {
423    my ( $self, $c, $searchspec, $deptype, $depname, $depsense, $depevr ) = @_;
424    $searchspec ||= {};
425
426    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
427
428    my $deprs = $c->forward(
429        'deps_rs', [ 
430            $searchspec, $deptype, $depname,
431            $depsense, $depevr 
432        ],
433    )->get_column('pkgid');
434    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
435        {
436            pkgid => 
437                { IN => $deprs->as_query, },
438        },
439    );
440    $c->forward('format_search', $searchspec);
441}
442
443sub byfile : XMLRPCPath('/search/rpm/byfile') {
444    my ( $self, $c, $searchspec, $file) = @_;
445    $searchspec ||= {};
446    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
447
448    my $filers = $c->forward('file_rs', [ $searchspec, $file ])
449        ->get_column('pkgid');
450    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
451        {
452            $distrs
453                ? { pkgid => { IN => $distrs->get_column('pkgid')->as_query, }, }
454                : (),
455        },
456    );
457    $c->forward('format_search', $searchspec);
458}
459
460sub fuzzy : XMLRPCPath('/search/rpm/fuzzy') {
461    my ($self, $c, $searchspec, $name) = @_;
462    $searchspec ||= {};
463
464    my $deprs = $c->model('Base')->resultset('Deps')->search(
465        { deptype => 'P', depname => { '~*' => $name } }
466    )->get_column('pkgid');
467    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
468
469    $c->stash->{rs} = 
470
471        $c->model('Base')->resultset('Rpms')->search(
472        {
473            -and => [
474                (exists($searchspec->{src})
475                    ? { issrc => $searchspec->{src} ? 1 : 0 }
476                    : ()),
477                { -or => [
478                    { name => 
479                        { '~*' => $name, },
480                    },
481#                    { pkgid =>
482#                        { IN => $deprs->as_query, },
483#                    },
484                     ]
485                },
486                $distrs
487                    ? { pkgid => { IN => $distrs->get_column('pkgid')->as_query, }, }
488                    : (),
489            ]     
490        },
491    );
492   
493    $c->forward('format_search', $searchspec);
494}
495
496sub quick : XMLRPCPath('/search/rpm/quick') {
497    my ($self, $c, $searchspec, @keywords) = @_;
498    $searchspec ||= {};
499    my $tsquery = join(' & ', map { $_ =~ s/ /\\ /g; $_ } @keywords);
500   
501    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
502
503    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
504            {
505                -or => [
506#                    { -nest => \[
507#                        "to_tsvector('english', description) @@ to_tsquery(?)",
508#                        [ plain_text => $tsquery],
509#                    ], },
510                    {
511                    name => { '~*' => [ @keywords ] },
512                    },
513                ],
514            (exists($searchspec->{src})
515                ? (issrc => $searchspec->{src} ? 1 : 0)
516                : ()),
517            ($distrs 
518                ? (pkgid => { IN => $distrs->get_column('pkgid')->as_query, },)
519                : ()),
520        },
521    );
522    $c->forward('format_search', $searchspec);
523}
524
525sub description : XMLRPCPath('/search/rpm/description') {
526    my ($self, $c, $searchspec, @keywords) = @_;
527    $searchspec ||= {};
528    my $tsquery = join(' & ', map { $_ =~ s/ /\\ /g; $_ } @keywords);
529    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
530    $c->stash->{rs} = $c->model('Base')->resultset('Rpms')->search(
531        {
532            -nest => \[
533                    "to_tsvector('english', description) @@ to_tsquery(?)",
534                    [ plain_text => $tsquery],
535                ],
536                (exists($searchspec->{src})
537                    ? (issrc => $searchspec->{src} ? 1 : 0)
538                    : ()),
539                ($distrs 
540                    ? (pkgid => { IN => $distrs->get_column('pkgid')->as_query, },)
541                    : ()),
542        },
543        {
544            select => [ 
545                "ts_rank_cd(to_tsvector('english', description),to_tsquery(?)) as rank",
546                'pkgid'
547            ],
548            bind => [ $tsquery ], 
549            order_by => [ 'rank desc', 'name', 'evr using >>', 'issrc' ],
550        },
551    )->as_subselect_rs;
552    $c->forward('format_search', $searchspec);
553}
554
555sub file_search : XMLRPCPath('/search/file/byname') {
556    my ( $self, $c, $searchspec, $file) = @_;
557    $searchspec ||= {};
558
559    $c->stash->{rs} = $c->forward('file_rs', [ $searchspec, $file ]);
560   
561    my @col = qw(dirname basename md5 size pkgid count);
562    $c->stash->{column} = [ @col, qw(has_content perm user group) ];
563   
564    $c->forward('format_search', $searchspec);
565}
566
567sub dep_search : XMLRPCPath('/search/dep/match') {
568    my ($self, $c, $searchspec, $deptype, $depname, $depsense, $depevr) = @_;
569
570    my $distrs = $c->forward('distrib_search', [ $searchspec, 1 ]);
571    $c->stash->{rs} = $c->forward(
572        'deps_rs', [ 
573            $searchspec, $deptype, $depname,
574            $depsense, $depevr 
575        ],
576    );
577
578    $c->stash->{column} = [ qw(name sense evr flags pkgid) ];
579    $c->forward('format_search', $searchspec);
580}
581
582=head1 AUTHOR
583
584Olivier Thauvin
585
586=head1 LICENSE
587
588This library is free software. You can redistribute it and/or modify
589it under the same terms as Perl itself.
590
591=cut
592
593__PACKAGE__->meta->make_immutable;
594
5951;
Note: See TracBrowser for help on using the repository browser.