.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Lucy::Docs::Tutorial::QueryObjects 3pm" .TH Lucy::Docs::Tutorial::QueryObjects 3pm "2015-03-06" "perl v5.20.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Lucy::Docs::Tutorial::QueryObjects \- Use Query objects instead of query strings. .SH "DESCRIPTION" .IX Header "DESCRIPTION" Until now, our search app has had only a single search box. In this tutorial chapter, we'll move towards an \*(L"advanced search\*(R" interface, by adding a \&\*(L"category\*(R" drop-down menu. Three new classes will be required: .IP "\(bu" 4 QueryParser \- Turn a query string into a Query object. .IP "\(bu" 4 TermQuery \- Query for a specific term within a specific field. .IP "\(bu" 4 ANDQuery \- \*(L"\s-1AND\*(R"\s0 together multiple Query objects to produce an intersected result set. .SS "Adaptations to indexer.pl" .IX Subsection "Adaptations to indexer.pl" Our new \*(L"category\*(R" field will be a StringType field rather than a FullTextType field, because we will only be looking for exact matches. It needs to be indexed, but since we won't display its value, it doesn't need to be stored. .PP .Vb 2 \& my $cat_type = Lucy::Plan::StringType\->new( stored => 0 ); \& $schema\->spec_field( name => \*(Aqcategory\*(Aq, type => $cat_type ); .Ve .PP There will be three possible values: \*(L"article\*(R", \*(L"amendment\*(R", and \*(L"preamble\*(R", which we'll hack out of the source file's name during our \f(CW\*(C`parse_file\*(C'\fR subroutine: .PP .Vb 11 \& my $category \& = $filename =~ /art/ ? \*(Aqarticle\*(Aq \& : $filename =~ /amend/ ? \*(Aqamendment\*(Aq \& : $filename =~ /preamble/ ? \*(Aqpreamble\*(Aq \& : die "Can\*(Aqt derive category for $filename"; \& return { \& title => $title, \& content => $bodytext, \& url => "/us_constitution/$filename", \& category => $category, \& }; .Ve .SS "Adaptations to search.cgi" .IX Subsection "Adaptations to search.cgi" The \*(L"category\*(R" constraint will be added to our search interface using an \s-1HTML \&\s0\*(L"select\*(R" element (this routine will need to be integrated into the \s-1HTML\s0 generation section of search.cgi): .PP .Vb 10 \& # Build up the HTML "select" object for the "category" field. \& sub generate_category_select { \& my $cat = shift; \& my $select = qq| \& |; \& if ($cat) { \& $select =~ s/"$cat"/"$cat" selected/; \& } \& return $select; \& } .Ve .PP We'll start off by loading our new modules and extracting our new \s-1CGI\s0 parameter. .PP .Vb 3 \& use Lucy::Search::QueryParser; \& use Lucy::Search::TermQuery; \& use Lucy::Search::ANDQuery; \& \& ... \& \& my $category = decode( "UTF\-8", $cgi\->param(\*(Aqcategory\*(Aq) || \*(Aq\*(Aq ); .Ve .PP QueryParser's constructor requires a \*(L"schema\*(R" argument. We can get that from our IndexSearcher: .PP .Vb 7 \& # Create an IndexSearcher and a QueryParser. \& my $searcher = Lucy::Search::IndexSearcher\->new( \& index => $path_to_index, \& ); \& my $qparser = Lucy::Search::QueryParser\->new( \& schema => $searcher\->get_schema, \& ); .Ve .PP Previously, we have been handing raw query strings to IndexSearcher. Behind the scenes, IndexSearcher has been using a QueryParser to turn those query strings into Query objects. Now, we will bring QueryParser into the foreground and parse the strings explicitly. .PP .Vb 1 \& my $query = $qparser\->parse($q); .Ve .PP If the user has specified a category, we'll use an ANDQuery to join our parsed query together with a TermQuery representing the category. .PP .Vb 9 \& if ($category) { \& my $category_query = Lucy::Search::TermQuery\->new( \& field => \*(Aqcategory\*(Aq, \& term => $category, \& ); \& $query = Lucy::Search::ANDQuery\->new( \& children => [ $query, $category_query ] \& ); \& } .Ve .PP Now when we execute the query... .PP .Vb 6 \& # Execute the Query and get a Hits object. \& my $hits = $searcher\->hits( \& query => $query, \& offset => $offset, \& num_wanted => $page_size, \& ); .Ve .PP \&... we'll get a result set which is the intersection of the parsed query and the category query. .SH "Congratulations!" .IX Header "Congratulations!" You've made it to the end of the tutorial. .SH "SEE ALSO" .IX Header "SEE ALSO" For additional thematic documentation, see the Apache Lucy Cookbook. .PP ANDQuery has a companion class, ORQuery, and a close relative, RequiredOptionalQuery.