summaryrefslogtreecommitdiffstats
path: root/doc/src/sgml/html/textsearch-features.html
blob: 59a781256c2e5cd256f6424d504cec6c1ecd5e51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>12.4. Additional Features</title><link rel="stylesheet" type="text/css" href="stylesheet.css" /><link rev="made" href="pgsql-docs@lists.postgresql.org" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /><link rel="prev" href="textsearch-controls.html" title="12.3. Controlling Text Search" /><link rel="next" href="textsearch-parsers.html" title="12.5. Parsers" /></head><body id="docContent" class="container-fluid col-10"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="5" align="center">12.4. Additional Features</th></tr><tr><td width="10%" align="left"><a accesskey="p" href="textsearch-controls.html" title="12.3. Controlling Text Search">Prev</a> </td><td width="10%" align="left"><a accesskey="u" href="textsearch.html" title="Chapter 12. Full Text Search">Up</a></td><th width="60%" align="center">Chapter 12. Full Text Search</th><td width="10%" align="right"><a accesskey="h" href="index.html" title="PostgreSQL 15.7 Documentation">Home</a></td><td width="10%" align="right"> <a accesskey="n" href="textsearch-parsers.html" title="12.5. Parsers">Next</a></td></tr></table><hr /></div><div class="sect1" id="TEXTSEARCH-FEATURES"><div class="titlepage"><div><div><h2 class="title" style="clear: both">12.4. Additional Features</h2></div></div></div><div class="toc"><dl class="toc"><dt><span class="sect2"><a href="textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR">12.4.1. Manipulating Documents</a></span></dt><dt><span class="sect2"><a href="textsearch-features.html#TEXTSEARCH-MANIPULATE-TSQUERY">12.4.2. Manipulating Queries</a></span></dt><dt><span class="sect2"><a href="textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS">12.4.3. Triggers for Automatic Updates</a></span></dt><dt><span class="sect2"><a href="textsearch-features.html#TEXTSEARCH-STATISTICS">12.4.4. Gathering Document Statistics</a></span></dt></dl></div><p>
   This section describes additional functions and operators that are
   useful in connection with text search.
  </p><div class="sect2" id="TEXTSEARCH-MANIPULATE-TSVECTOR"><div class="titlepage"><div><div><h3 class="title">12.4.1. Manipulating Documents</h3></div></div></div><p>
    <a class="xref" href="textsearch-controls.html#TEXTSEARCH-PARSING-DOCUMENTS" title="12.3.1. Parsing Documents">Section 12.3.1</a> showed how raw textual
    documents can be converted into <code class="type">tsvector</code> values.
    <span class="productname">PostgreSQL</span> also provides functions and
    operators that can be used to manipulate documents that are already
    in <code class="type">tsvector</code> form.
   </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">
     <a id="id-1.5.11.7.3.3.1.1.1" class="indexterm"></a>

      <code class="literal"><code class="type">tsvector</code> || <code class="type">tsvector</code></code>
     </span></dt><dd><p>
       The <code class="type">tsvector</code> concatenation operator
       returns a vector which combines the lexemes and positional information
       of the two vectors given as arguments.  Positions and weight labels
       are retained during the concatenation.
       Positions appearing in the right-hand vector are offset by the largest
       position mentioned in the left-hand vector, so that the result is
       nearly equivalent to the result of performing <code class="function">to_tsvector</code>
       on the concatenation of the two original document strings.  (The
       equivalence is not exact, because any stop-words removed from the
       end of the left-hand argument will not affect the result, whereas
       they would have affected the positions of the lexemes in the
       right-hand argument if textual concatenation were used.)
      </p><p>
       One advantage of using concatenation in the vector form, rather than
       concatenating text before applying <code class="function">to_tsvector</code>, is that
       you can use different configurations to parse different sections
       of the document.  Also, because the <code class="function">setweight</code> function
       marks all lexemes of the given vector the same way, it is necessary
       to parse the text and do <code class="function">setweight</code> before concatenating
       if you want to label different parts of the document with different
       weights.
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.3.3.2.1.1" class="indexterm"></a>

      <code class="literal">setweight(<em class="replaceable"><code>vector</code></em> <code class="type">tsvector</code>, <em class="replaceable"><code>weight</code></em> <code class="type">"char"</code>) returns <code class="type">tsvector</code></code>
     </span></dt><dd><p>
       <code class="function">setweight</code> returns a copy of the input vector in which every
       position has been labeled with the given <em class="replaceable"><code>weight</code></em>, either
       <code class="literal">A</code>, <code class="literal">B</code>, <code class="literal">C</code>, or
       <code class="literal">D</code>.  (<code class="literal">D</code> is the default for new
       vectors and as such is not displayed on output.)  These labels are
       retained when vectors are concatenated, allowing words from different
       parts of a document to be weighted differently by ranking functions.
      </p><p>
       Note that weight labels apply to <span class="emphasis"><em>positions</em></span>, not
       <span class="emphasis"><em>lexemes</em></span>.  If the input vector has been stripped of
       positions then <code class="function">setweight</code> does nothing.
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.3.3.3.1.1" class="indexterm"></a>

      <code class="literal">length(<em class="replaceable"><code>vector</code></em> <code class="type">tsvector</code>) returns <code class="type">integer</code></code>
     </span></dt><dd><p>
       Returns the number of lexemes stored in the vector.
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.3.3.4.1.1" class="indexterm"></a>

      <code class="literal">strip(<em class="replaceable"><code>vector</code></em> <code class="type">tsvector</code>) returns <code class="type">tsvector</code></code>
     </span></dt><dd><p>
       Returns a vector that lists the same lexemes as the given vector, but
       lacks any position or weight information.  The result is usually much
       smaller than an unstripped vector, but it is also less useful.
       Relevance ranking does not work as well on stripped vectors as
       unstripped ones.  Also,
       the <code class="literal">&lt;-&gt;</code> (FOLLOWED BY) <code class="type">tsquery</code> operator
       will never match stripped input, since it cannot determine the
       distance between lexeme occurrences.
      </p></dd></dl></div><p>
    A full list of <code class="type">tsvector</code>-related functions is available
    in <a class="xref" href="functions-textsearch.html#TEXTSEARCH-FUNCTIONS-TABLE" title="Table 9.43. Text Search Functions">Table 9.43</a>.
   </p></div><div class="sect2" id="TEXTSEARCH-MANIPULATE-TSQUERY"><div class="titlepage"><div><div><h3 class="title">12.4.2. Manipulating Queries</h3></div></div></div><p>
    <a class="xref" href="textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES" title="12.3.2. Parsing Queries">Section 12.3.2</a> showed how raw textual
    queries can be converted into <code class="type">tsquery</code> values.
    <span class="productname">PostgreSQL</span> also provides functions and
    operators that can be used to manipulate queries that are already
    in <code class="type">tsquery</code> form.
   </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">
      <code class="literal"><code class="type">tsquery</code> &amp;&amp; <code class="type">tsquery</code></code>
     </span></dt><dd><p>
       Returns the AND-combination of the two given queries.
      </p></dd><dt><span class="term">
      <code class="literal"><code class="type">tsquery</code> || <code class="type">tsquery</code></code>
     </span></dt><dd><p>
       Returns the OR-combination of the two given queries.
      </p></dd><dt><span class="term">
      <code class="literal">!! <code class="type">tsquery</code></code>
     </span></dt><dd><p>
       Returns the negation (NOT) of the given query.
      </p></dd><dt><span class="term">
      <code class="literal"><code class="type">tsquery</code> &lt;-&gt; <code class="type">tsquery</code></code>
     </span></dt><dd><p>
       Returns a query that searches for a match to the first given query
       immediately followed by a match to the second given query, using
       the <code class="literal">&lt;-&gt;</code> (FOLLOWED BY)
       <code class="type">tsquery</code> operator.  For example:

</p><pre class="screen">
SELECT to_tsquery('fat') &lt;-&gt; to_tsquery('cat | rat');
          ?column?
----------------------------
 'fat' &lt;-&gt; ( 'cat' | 'rat' )
</pre><p>
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.4.3.5.1.1" class="indexterm"></a>

      <code class="literal">tsquery_phrase(<em class="replaceable"><code>query1</code></em> <code class="type">tsquery</code>, <em class="replaceable"><code>query2</code></em> <code class="type">tsquery</code> [, <em class="replaceable"><code>distance</code></em> <code class="type">integer</code> ]) returns <code class="type">tsquery</code></code>
     </span></dt><dd><p>
       Returns a query that searches for a match to the first given query
       followed by a match to the second given query at a distance of exactly
       <em class="replaceable"><code>distance</code></em> lexemes, using
       the <code class="literal">&lt;<em class="replaceable"><code>N</code></em>&gt;</code>
       <code class="type">tsquery</code> operator.  For example:

</p><pre class="screen">
SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10);
  tsquery_phrase
------------------
 'fat' &lt;10&gt; 'cat'
</pre><p>
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.4.3.6.1.1" class="indexterm"></a>

      <code class="literal">numnode(<em class="replaceable"><code>query</code></em> <code class="type">tsquery</code>) returns <code class="type">integer</code></code>
     </span></dt><dd><p>
       Returns the number of nodes (lexemes plus operators) in a
       <code class="type">tsquery</code>. This function is useful
       to determine if the <em class="replaceable"><code>query</code></em> is meaningful
       (returns &gt; 0), or contains only stop words (returns 0).
       Examples:

</p><pre class="screen">
SELECT numnode(plainto_tsquery('the any'));
NOTICE:  query contains only stopword(s) or doesn't contain lexeme(s), ignored
 numnode
---------
       0

SELECT numnode('foo &amp; bar'::tsquery);
 numnode
---------
       3
</pre><p>
      </p></dd><dt><span class="term">
     <a id="id-1.5.11.7.4.3.7.1.1" class="indexterm"></a>

      <code class="literal">querytree(<em class="replaceable"><code>query</code></em> <code class="type">tsquery</code>) returns <code class="type">text</code></code>
     </span></dt><dd><p>
       Returns the portion of a <code class="type">tsquery</code> that can be used for
       searching an index.  This function is useful for detecting
       unindexable queries, for example those containing only stop words
       or only negated terms.  For example:

</p><pre class="screen">
SELECT querytree(to_tsquery('defined'));
 querytree
-----------
 'defin'

SELECT querytree(to_tsquery('!defined'));
 querytree
-----------
 T
</pre><p>
      </p></dd></dl></div><div class="sect3" id="TEXTSEARCH-QUERY-REWRITING"><div class="titlepage"><div><div><h4 class="title">12.4.2.1. Query Rewriting</h4></div></div></div><a id="id-1.5.11.7.4.4.2" class="indexterm"></a><p>
     The <code class="function">ts_rewrite</code> family of functions search a
     given <code class="type">tsquery</code> for occurrences of a target
     subquery, and replace each occurrence with a
     substitute subquery.  In essence this operation is a
     <code class="type">tsquery</code>-specific version of substring replacement.
     A target and substitute combination can be
     thought of as a <em class="firstterm">query rewrite rule</em>.  A collection
     of such rewrite rules can be a powerful search aid.
     For example, you can expand the search using synonyms
     (e.g., <code class="literal">new york</code>, <code class="literal">big apple</code>, <code class="literal">nyc</code>,
     <code class="literal">gotham</code>) or narrow the search to direct the user to some hot
     topic.  There is some overlap in functionality between this feature
     and thesaurus dictionaries (<a class="xref" href="textsearch-dictionaries.html#TEXTSEARCH-THESAURUS" title="12.6.4. Thesaurus Dictionary">Section 12.6.4</a>).
     However, you can modify a set of rewrite rules on-the-fly without
     reindexing, whereas updating a thesaurus requires reindexing to be
     effective.
    </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">
       <code class="literal">ts_rewrite (<em class="replaceable"><code>query</code></em> <code class="type">tsquery</code>, <em class="replaceable"><code>target</code></em> <code class="type">tsquery</code>, <em class="replaceable"><code>substitute</code></em> <code class="type">tsquery</code>) returns <code class="type">tsquery</code></code>
      </span></dt><dd><p>
        This form of <code class="function">ts_rewrite</code> simply applies a single
        rewrite rule: <em class="replaceable"><code>target</code></em>
        is replaced by <em class="replaceable"><code>substitute</code></em>
        wherever it appears in <em class="replaceable"><code>query</code></em>.  For example:

</p><pre class="screen">
SELECT ts_rewrite('a &amp; b'::tsquery, 'a'::tsquery, 'c'::tsquery);
 ts_rewrite
------------
 'b' &amp; 'c'
</pre><p>
       </p></dd><dt><span class="term">
       <code class="literal">ts_rewrite (<em class="replaceable"><code>query</code></em> <code class="type">tsquery</code>, <em class="replaceable"><code>select</code></em> <code class="type">text</code>) returns <code class="type">tsquery</code></code>
      </span></dt><dd><p>
        This form of <code class="function">ts_rewrite</code> accepts a starting
        <em class="replaceable"><code>query</code></em> and an SQL <em class="replaceable"><code>select</code></em> command, which
        is given as a text string.  The <em class="replaceable"><code>select</code></em> must yield two
        columns of <code class="type">tsquery</code> type.  For each row of the
        <em class="replaceable"><code>select</code></em> result, occurrences of the first column value
        (the target) are replaced by the second column value (the substitute)
        within the current <em class="replaceable"><code>query</code></em> value.  For example:

</p><pre class="screen">
CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery);
INSERT INTO aliases VALUES('a', 'c');

SELECT ts_rewrite('a &amp; b'::tsquery, 'SELECT t,s FROM aliases');
 ts_rewrite
------------
 'b' &amp; 'c'
</pre><p>
       </p><p>
        Note that when multiple rewrite rules are applied in this way,
        the order of application can be important; so in practice you will
        want the source query to <code class="literal">ORDER BY</code> some ordering key.
       </p></dd></dl></div><p>
     Let's consider a real-life astronomical example. We'll expand query
     <code class="literal">supernovae</code> using table-driven rewriting rules:

</p><pre class="screen">
CREATE TABLE aliases (t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn'));

SELECT ts_rewrite(to_tsquery('supernovae &amp; crab'), 'SELECT * FROM aliases');
           ts_rewrite
---------------------------------
 'crab' &amp; ( 'supernova' | 'sn' )
</pre><p>

     We can change the rewriting rules just by updating the table:

</p><pre class="screen">
UPDATE aliases
SET s = to_tsquery('supernovae|sn &amp; !nebulae')
WHERE t = to_tsquery('supernovae');

SELECT ts_rewrite(to_tsquery('supernovae &amp; crab'), 'SELECT * FROM aliases');
                 ts_rewrite
---------------------------------------------
 'crab' &amp; ( 'supernova' | 'sn' &amp; !'nebula' )
</pre><p>
    </p><p>
     Rewriting can be slow when there are many rewriting rules, since it
     checks every rule for a possible match. To filter out obvious non-candidate
     rules we can use the containment operators for the <code class="type">tsquery</code>
     type. In the example below, we select only those rules which might match
     the original query:

</p><pre class="screen">
SELECT ts_rewrite('a &amp; b'::tsquery,
                  'SELECT t,s FROM aliases WHERE ''a &amp; b''::tsquery @&gt; t');
 ts_rewrite
------------
 'b' &amp; 'c'
</pre><p>
    </p></div></div><div class="sect2" id="TEXTSEARCH-UPDATE-TRIGGERS"><div class="titlepage"><div><div><h3 class="title">12.4.3. Triggers for Automatic Updates</h3></div></div></div><a id="id-1.5.11.7.5.2" class="indexterm"></a><div class="note"><h3 class="title">Note</h3><p>
     The method described in this section has been obsoleted by the use of
     stored generated columns, as described in <a class="xref" href="textsearch-tables.html#TEXTSEARCH-TABLES-INDEX" title="12.2.2. Creating Indexes">Section 12.2.2</a>.
    </p></div><p>
    When using a separate column to store the <code class="type">tsvector</code> representation
    of your documents, it is necessary to create a trigger to update the
    <code class="type">tsvector</code> column when the document content columns change.
    Two built-in trigger functions are available for this, or you can write
    your own.
   </p><pre class="synopsis">
tsvector_update_trigger(<em class="replaceable"><code>tsvector_column_name</code></em>,​ <em class="replaceable"><code>config_name</code></em>, <em class="replaceable"><code>text_column_name</code></em> [<span class="optional">, ... </span>])
tsvector_update_trigger_column(<em class="replaceable"><code>tsvector_column_name</code></em>,​ <em class="replaceable"><code>config_column_name</code></em>, <em class="replaceable"><code>text_column_name</code></em> [<span class="optional">, ... </span>])
</pre><p>
    These trigger functions automatically compute a <code class="type">tsvector</code>
    column from one or more textual columns, under the control of
    parameters specified in the <code class="command">CREATE TRIGGER</code> command.
    An example of their use is:

</p><pre class="screen">
CREATE TABLE messages (
    title       text,
    body        text,
    tsv         tsvector
);

CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON messages FOR EACH ROW EXECUTE FUNCTION
tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);

INSERT INTO messages VALUES('title here', 'the body text is here');

SELECT * FROM messages;
   title    |         body          |            tsv
------------+-----------------------+----------------------------
 title here | the body text is here | 'bodi':4 'text':5 'titl':1

SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title &amp; body');
   title    |         body
------------+-----------------------
 title here | the body text is here
</pre><p>

    Having created this trigger, any change in <code class="structfield">title</code> or
    <code class="structfield">body</code> will automatically be reflected into
    <code class="structfield">tsv</code>, without the application having to worry about it.
   </p><p>
    The first trigger argument must be the name of the <code class="type">tsvector</code>
    column to be updated.  The second argument specifies the text search
    configuration to be used to perform the conversion.  For
    <code class="function">tsvector_update_trigger</code>, the configuration name is simply
    given as the second trigger argument.  It must be schema-qualified as
    shown above, so that the trigger behavior will not change with changes
    in <code class="varname">search_path</code>.  For
    <code class="function">tsvector_update_trigger_column</code>, the second trigger argument
    is the name of another table column, which must be of type
    <code class="type">regconfig</code>.  This allows a per-row selection of configuration
    to be made.  The remaining argument(s) are the names of textual columns
    (of type <code class="type">text</code>, <code class="type">varchar</code>, or <code class="type">char</code>).  These
    will be included in the document in the order given.  NULL values will
    be skipped (but the other columns will still be indexed).
   </p><p>
    A limitation of these built-in triggers is that they treat all the
    input columns alike.  To process columns differently — for
    example, to weight title differently from body — it is necessary
    to write a custom trigger.  Here is an example using
    <span class="application">PL/pgSQL</span> as the trigger language:

</p><pre class="programlisting">
CREATE FUNCTION messages_trigger() RETURNS trigger AS $$
begin
  new.tsv :=
     setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') ||
     setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D');
  return new;
end
$$ LANGUAGE plpgsql;

CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
    ON messages FOR EACH ROW EXECUTE FUNCTION messages_trigger();
</pre><p>
   </p><p>
    Keep in mind that it is important to specify the configuration name
    explicitly when creating <code class="type">tsvector</code> values inside triggers,
    so that the column's contents will not be affected by changes to
    <code class="varname">default_text_search_config</code>.  Failure to do this is likely to
    lead to problems such as search results changing after a dump and restore.
   </p></div><div class="sect2" id="TEXTSEARCH-STATISTICS"><div class="titlepage"><div><div><h3 class="title">12.4.4. Gathering Document Statistics</h3></div></div></div><a id="id-1.5.11.7.6.2" class="indexterm"></a><p>
    The function <code class="function">ts_stat</code> is useful for checking your
    configuration and for finding stop-word candidates.
   </p><pre class="synopsis">
ts_stat(<em class="replaceable"><code>sqlquery</code></em> <code class="type">text</code>, [<span class="optional"> <em class="replaceable"><code>weights</code></em> <code class="type">text</code>, </span>]
        OUT <em class="replaceable"><code>word</code></em> <code class="type">text</code>, OUT <em class="replaceable"><code>ndoc</code></em> <code class="type">integer</code>,
        OUT <em class="replaceable"><code>nentry</code></em> <code class="type">integer</code>) returns <code class="type">setof record</code>
</pre><p>
    <em class="replaceable"><code>sqlquery</code></em> is a text value containing an SQL
    query which must return a single <code class="type">tsvector</code> column.
    <code class="function">ts_stat</code> executes the query and returns statistics about
    each distinct lexeme (word) contained in the <code class="type">tsvector</code>
    data.  The columns returned are

    </p><div class="itemizedlist"><ul class="itemizedlist compact" style="list-style-type: bullet; "><li class="listitem" style="list-style-type: disc"><p>
       <em class="replaceable"><code>word</code></em> <code class="type">text</code> — the value of a lexeme
      </p></li><li class="listitem" style="list-style-type: disc"><p>
       <em class="replaceable"><code>ndoc</code></em> <code class="type">integer</code> — number of documents
       (<code class="type">tsvector</code>s) the word occurred in
      </p></li><li class="listitem" style="list-style-type: disc"><p>
       <em class="replaceable"><code>nentry</code></em> <code class="type">integer</code> — total number of
       occurrences of the word
      </p></li></ul></div><p>

    If <em class="replaceable"><code>weights</code></em> is supplied, only occurrences
    having one of those weights are counted.
   </p><p>
    For example, to find the ten most frequent words in a document collection:

</p><pre class="programlisting">
SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
</pre><p>

    The same, but counting only word occurrences with weight <code class="literal">A</code>
    or <code class="literal">B</code>:

</p><pre class="programlisting">
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
</pre><p>
   </p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="textsearch-controls.html" title="12.3. Controlling Text Search">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="textsearch.html" title="Chapter 12. Full Text Search">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="textsearch-parsers.html" title="12.5. Parsers">Next</a></td></tr><tr><td width="40%" align="left" valign="top">12.3. Controlling Text Search </td><td width="20%" align="center"><a accesskey="h" href="index.html" title="PostgreSQL 15.7 Documentation">Home</a></td><td width="40%" align="right" valign="top"> 12.5. Parsers</td></tr></table></div></body></html>