1Lucy::Docs::Tutorial::QUuseerryOCbojnetcrtisbLTuuutcteyod:r:iPDaeolrc(ls3:)D:oTcuutmoernitaalt:i:oQnueryObjectsTutorial(3)
2
3
4
6 Lucy::Docs::Tutorial::QueryObjectsTutorial - Use Query objects instead
7 of query strings.
8
10 Until now, our search app has had only a single search box. In this
11 tutorial chapter, we’ll move towards an “advanced search” interface, by
12 adding a “category” drop-down menu. Three new classes will be
13 required:
14
15 • QueryParser - Turn a query string into a Query object.
16
17 • TermQuery - Query for a specific term within a specific field.
18
19 • ANDQuery - “AND” together multiple Query objects to produce an
20 intersected result set.
21
22 Adaptations to indexer.pl
23 Our new “category” field will be a StringType field rather than a
24 FullTextType field, because we will only be looking for exact matches.
25 It needs to be indexed, but since we won’t display its value, it
26 doesn’t need to be stored.
27
28 my $cat_type = Lucy::Plan::StringType->new( stored => 0 );
29 $schema->spec_field( name => 'category', type => $cat_type );
30
31 There will be three possible values: “article”, “amendment”, and
32 “preamble”, which we’ll hack out of the source file’s name during our
33 "parse_file" subroutine:
34
35 my $category
36 = $filename =~ /art/ ? 'article'
37 : $filename =~ /amend/ ? 'amendment'
38 : $filename =~ /preamble/ ? 'preamble'
39 : die "Can't derive category for $filename";
40 return {
41 title => $title,
42 content => $bodytext,
43 url => "/us_constitution/$filename",
44 category => $category,
45 };
46
47 Adaptations to search.cgi
48 The “category” constraint will be added to our search interface using
49 an HTML “select” element (this routine will need to be integrated into
50 the HTML generation section of search.cgi):
51
52 # Build up the HTML "select" object for the "category" field.
53 sub generate_category_select {
54 my $cat = shift;
55 my $select = qq|
56 <select name="category">
57 <option value="">All Sections</option>
58 <option value="article">Articles</option>
59 <option value="amendment">Amendments</option>
60 </select>|;
61 if ($cat) {
62 $select =~ s/"$cat"/"$cat" selected/;
63 }
64 return $select;
65 }
66
67 We’ll start off by loading our new modules and extracting our new CGI
68 parameter.
69
70 use Lucy::Search::QueryParser;
71 use Lucy::Search::TermQuery;
72 use Lucy::Search::ANDQuery;
73
74 ...
75
76 my $category = decode( "UTF-8", $cgi->param('category') || '' );
77
78 QueryParser’s constructor requires a “schema” argument. We can get
79 that from our IndexSearcher:
80
81 # Create an IndexSearcher and a QueryParser.
82 my $searcher = Lucy::Search::IndexSearcher->new(
83 index => $path_to_index,
84 );
85 my $qparser = Lucy::Search::QueryParser->new(
86 schema => $searcher->get_schema,
87 );
88
89 Previously, we have been handing raw query strings to IndexSearcher.
90 Behind the scenes, IndexSearcher has been using a QueryParser to turn
91 those query strings into Query objects. Now, we will bring QueryParser
92 into the foreground and parse the strings explicitly.
93
94 my $query = $qparser->parse($q);
95
96 If the user has specified a category, we’ll use an ANDQuery to join our
97 parsed query together with a TermQuery representing the category.
98
99 if ($category) {
100 my $category_query = Lucy::Search::TermQuery->new(
101 field => 'category',
102 term => $category,
103 );
104 $query = Lucy::Search::ANDQuery->new(
105 children => [ $query, $category_query ]
106 );
107 }
108
109 Now when we execute the query…
110
111 # Execute the Query and get a Hits object.
112 my $hits = $searcher->hits(
113 query => $query,
114 offset => $offset,
115 num_wanted => $page_size,
116 );
117
118 … we’ll get a result set which is the intersection of the parsed query
119 and the category query.
120
121 Using TermQuery with full text fields
122 When querying full text fields, the easiest way is to create query
123 objects using QueryParser. But sometimes you want to create TermQuery
124 for a single term in a FullTextType field directly. In this case, we
125 have to run the search term through the field’s analyzer to make sure
126 it gets normalized in the same way as the field’s content.
127
128 sub make_term_query {
129 my ($field, $term) = @_;
130
131 my $token;
132 my $type = $schema->fetch_type($field);
133
134 if ( $type->isa('Lucy::Plan::FullTextType') ) {
135 # Run the term through the full text analysis chain.
136 my $analyzer = $type->get_analyzer;
137 my $tokens = $analyzer->split($term);
138
139 if ( @$tokens != 1 ) {
140 # If the term expands to more than one token, or no
141 # tokens at all, it will never match a token in the
142 # full text field.
143 return Lucy::Search::NoMatchQuery->new;
144 }
145
146 $token = $tokens->[0];
147 }
148 else {
149 # Exact match for other types.
150 $token = $term;
151 }
152
153 return Lucy::Search::TermQuery->new(
154 field => $field,
155 term => $token,
156 );
157 }
158
159 Congratulations!
160 You’ve made it to the end of the tutorial.
161
162 See Also
163 For additional thematic documentation, see the Apache Lucy Cookbook.
164
165 ANDQuery has a companion class, ORQuery, and a close relative,
166 RequiredOptionalQuery.
167
168
169
170perl v5.36.0 L2u0c2y3:-:0D1o-c2s0::Tutorial::QueryObjectsTutorial(3)