DBA Data[Home] [Help]

PACKAGE BODY: APPS.IBC_CONTENT_SEARCH_PVT

Source


1 PACKAGE BODY Ibc_Content_Search_Pvt AS
2 /* $Header: ibcvsrcb.pls 120.0 2005/09/01 21:40:35 srrangar noship $ */
3 TYPE WeakCurType IS REF CURSOR;
4   --
5   -- Private Utility function
6   -- remove ( and ) from p_string by replacing them with space characters
7   --
8   FUNCTION Remove_Parenthesis
9   ( p_string       IN VARCHAR2 )
10   RETURN VARCHAR2
11   IS
12     l_string VARCHAR2(32000) := p_string;
13   BEGIN
14     l_string := REPLACE(l_string, '(', ' ');
15     l_string := REPLACE(l_string, ')', ' ');
16     l_string := REPLACE(l_string, '[', ' ');
17     l_string := REPLACE(l_string, ']', ' ');
18     RETURN l_string;
19   END Remove_Parenthesis;
20 
21   --
22   -- Private Utility function
23   -- remove } and { from p_string by replacing them with space characters
24   --
25   FUNCTION Remove_Braces
26   ( p_string	IN VARCHAR2 )
27   RETURN VARCHAR2
28   IS
29     l_string VARCHAR2(32000) := p_string;
30   BEGIN
31     l_string := REPLACE(l_string, '}', ' ');
32     l_string := REPLACE(l_string, '{', ' ');
33     RETURN l_string;
34   END Remove_Braces;
35 
36   --
37   -- Private Utility function
38   -- replace white-space characters
39   --
40   FUNCTION Replace_Whitespace
41   ( p_string	IN VARCHAR2,
42     p_search_option IN NUMBER )
43   RETURN VARCHAR2
44   IS
45     lenb            INTEGER;
46     len             INTEGER;
47     l_criteria_word VARCHAR2(2000);
48     q_word          VARCHAR2(32000);
49     l_string        VARCHAR2(32000) := p_string;
50     first_word      BOOLEAN := TRUE;
51     l_operator      VARCHAR2(4);
52 
53   BEGIN
54 
55     -- First convert multi-byte space character to single byte space
56     -- so that later on, when we a parsing for the space character, it
57     -- will be found
58     lenb := LENGTHB(l_string);
59     len := LENGTH(l_string);
60     IF(lenb<>len) THEN
61       l_string := REPLACE(l_string, TO_MULTI_BYTE(' '), ' ');
62     END IF;
63     lenb := LENGTHB(l_string);
64     len := LENGTH(l_string);
65     -- Pad the criteria string with blanks so that
66     -- the parse algorithm will not miss the last word
67     l_string := RPAD(l_string, lenb+1);
68     l_string := LTRIM(l_string,' ');
69 
70     -- Initialize some variables
71     first_word := TRUE;
72     len := INSTR(l_string, ' ');  -- position of first space character
73 
74     -- Loop through the criteria string, parse to get a single criteria word
75     -- token at a time. Between each word, insert the proper Oracle Text
76     -- operator (e.g. AND, OR, ACCUM, etc.) depending on the search method
77     -- chosen.
78     WHILE (len > 0) LOOP
79       l_criteria_word :=
80         SUBSTR(l_string, 1, len-1); --from beg till char before space
81 
82       IF (first_word = TRUE)
83       THEN
84         IF (p_search_option = Ibc_Content_Search_Pvt.FUZZY) --FUZZY
85         THEN
86            q_word := '?'''||l_criteria_word||'''';
87          ELSE
88            q_word := ''''||l_criteria_word||'''';
89          END IF;
90       ELSE
91         IF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ALL)
92         THEN
93           l_operator := ' & ';
94         ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ANY)
95         THEN
96           l_operator := ' | ';
97         ELSIF (p_search_option = Ibc_Content_Search_Pvt.FUZZY)
98         THEN
99           l_operator := ' , ?';
100         ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ACCUM)
101         THEN
102           l_operator := ' , ';
103         ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_PHRASE)
104         THEN
105           l_operator := ' ';
106         ELSE -- if other cases
107           l_operator := ' , ';
108         END IF;
109         q_word := q_word||l_operator||''''||l_criteria_word||'''';
110       END IF;
111 
112       first_word := FALSE;
113 
114       -- Get the rest of the criteria string and trim off beginning whitespace
115       -- This will now be the beginning of the next criteria token
116       l_string := SUBSTR(l_string,len);
117       l_string := LTRIM(l_string, ' ');
118       -- Find the position of the next space. This will now be the end of the
119       -- next criteria token
120       len:= INSTR(l_string, ' '); -- find the position of the next space
121     END LOOP;
122     RETURN q_word;
123   END Replace_Whitespace;
124 
125 
126   --
127   -- Private Utility function
128   -- Handle special characters for Text query
129   --
130   FUNCTION Escape_Special_Char( p_string IN VARCHAR2 )
131     RETURN VARCHAR2
132   IS
133     l_string VARCHAR2(32000) := p_string;
134   BEGIN
135     -- Remove Grouping and Escaping characters
136     l_string := Remove_Parenthesis(l_string);
137     l_string := Remove_Braces(l_string);
138 
139     -- replace all the other special reserved characters
140     l_string := REPLACE(l_string, Fnd_Global.LOCAL_CHR(39),
141       Fnd_Global.LOCAL_CHR(39)||Fnd_Global.LOCAL_CHR(39)); -- quote ' to ''
142     l_string := REPLACE(l_string, '\', '\\');  -- back slash (escape char)
143     l_string := REPLACE(l_string, ',', '\,');  -- accumulate
144     l_string := REPLACE(l_string, '&', '\&');  -- and
145     l_string := REPLACE(l_string, '=', '\=');  -- equivalance
146     l_string := REPLACE(l_string, '?', '\?');  -- fussy
147     l_string := REPLACE(l_string, '-', '\-');  -- minus
148     l_string := REPLACE(l_string, ';', '\;');  -- near
149     l_string := REPLACE(l_string, '~', '\~');  -- not
150     l_string := REPLACE(l_string, '|', '\|');  -- or
151     l_string := REPLACE(l_string, '$', '\$');  -- stem
152     l_string := REPLACE(l_string, '!', '\!');  -- soundex
153     l_string := REPLACE(l_string, '>', '\>');  -- threshold
154     l_string := REPLACE(l_string, '*', '\*');  -- weight
155     l_string := REPLACE(l_string, '_', '\_');  -- single char wildcard
156 
157     --bug 3209009
158     -- to make sure we will not miss '% test and %%'
159     l_string := ' '||l_string||' ';
160     l_string := REPLACE(l_string, ' % ', ' \% ');
161     l_string := REPLACE(l_string, ' %% ', ' \%\% ');
162     l_string := trim(l_string);
163 
164     RETURN l_string;
165   END Escape_Special_Char;
166   --
167   -- Private Utility function
168   -- Add the next term to the query string according to search option
169   -- Parameters:
170   --  p_string VARCHAR2: the running keyword string
171   --  p_term VARCHAR2: the term to append
172   --  p_search_option NUMBER: search option, as defined in IBC_CONTENT_SEARCH_PVT
173   -- Returns:
174   --  Query string with the term appended using the appropriate search operator
175   -- Since 12.0
176   --
177   FUNCTION Append_Query_Term
178   ( p_string 	IN VARCHAR2,
179     p_term  	IN VARCHAR2,
180     p_search_option IN NUMBER )
181     RETURN VARCHAR2
182   IS
183     l_string VARCHAR2(32000) := p_string;
184     l_operator      VARCHAR2(4);
185   BEGIN
186     IF( trim(p_term) IS NULL )
187     THEN
188         RETURN p_string;
189     END IF;
190 
191     IF( trim(l_string) IS NULL ) -- first term
192     THEN
193       IF (p_search_option = Ibc_Content_Search_Pvt.FUZZY)
194       THEN
195         l_string := '?'''|| p_term ||'''';
196       ELSE
197         l_string :=  p_term;
198       END IF;
199     ELSE -- subsequent terms
200       IF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ALL)
201       THEN
202         l_operator := ' & ';
203       ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ANY)
204       THEN
205         l_operator := ' | ';
206       ELSIF (p_search_option = Ibc_Content_Search_Pvt.FUZZY)
207       THEN
208         l_operator := ' , ?';
209       ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_ACCUM)
210       THEN
211           l_operator := ' , ';
212       ELSIF (p_search_option = Ibc_Content_Search_Pvt.MATCH_PHRASE)
213       THEN
214         l_operator := ' ';
215       ELSE -- if other cases
216         l_operator := ' , ';
217       END IF;
218 
219       l_string := l_string || l_operator|| p_term ;
220     END IF;
221 
222     RETURN l_string;
223   END Append_Query_Term;
224 
225   --
226   -- Private Utility function
227   -- This method parses the keywords based on the search syntax rule.
228   -- We support the syntax of exact phrase in the keywords (" ").
229   --
230   -- Parameters:
231   --  p_string VARCHAR2: keywords to be processed
232   --  p_search_option NUMBER: Must be one of the search option
233   --       defined in CS_K NOWLEDGE_PUB.
234   -- Returns:
235   --  The processed keyword query
236   -- Since 12.0
237   --
238   FUNCTION Parse_Keywords
239   ( p_string	IN VARCHAR2,
240     p_search_option IN NUMBER )
241   RETURN VARCHAR2
242   IS
243     l_left_quote    INTEGER := 0; -- position of left quote
244     l_right_quote   INTEGER := 0; -- position of right quote
245     l_qnum          INTEGER := 0; -- number of double quotes found so far
246     l_phrase        VARCHAR2(32000); -- extracted phrase
247     l_unquoted      VARCHAR2(32000) := ''; -- all unquoted text
248     l_len           INTEGER;
249     TYPE String_List IS TABLE OF VARCHAR2(32000) INDEX BY PLS_INTEGER;
250     l_phrase_list  String_List;  -- list of extracted phrases
251     l_counter       INTEGER;
252     l_processed_keyword VARCHAR(32000) := ''; --final processed keyword string
253   BEGIN
254 
255     l_left_quote := INSTR(p_string, '"', 1, l_qnum + 1);
256 
257     IF(l_left_quote = 0) -- no quotes
258     THEN
259       l_unquoted := p_string;
260     END IF;
261 
262     WHILE (l_left_quote > 0) LOOP
263       --add unquoted portion to the unquoted string (exclude ")
264       --assert: left quote (current) > right quote (prev)
265       l_len := l_left_quote - l_right_quote - 1;
266       l_unquoted := l_unquoted || ' ' ||
267         SUBSTR(p_string, l_right_quote + 1, l_len);
268 
269       --is there a close quote?
270       l_right_quote := INSTR(p_string,'"', 1, l_qnum + 2);
271       IF(l_right_quote > 0) -- add the quote
272       THEN
273         l_len := l_right_quote - l_left_quote - 1;
274         l_phrase := SUBSTR(p_string, l_left_quote + 1, l_len);
275         IF( trim (l_phrase) IS NOT NULL)
276         THEN
277           --add the quote to the list
278           l_phrase_list(l_left_quote) := l_phrase;
279           --dbms_output.put_line('phrase:' || '[' || l_phrase || ']');
280         END IF;
281       ELSE -- add the remaining text (last quote was an open quote)
282         l_unquoted := l_unquoted || ' ' || SUBSTR(p_string, l_left_quote + 1);
283       END IF;
284 
285       -- now process the next phrase, try to find the open quote
286       l_qnum := l_qnum + 2;
287       l_left_quote := INSTR(p_string, '"', 1, l_qnum + 1);
288     END LOOP;
289 
290     -- add the remaining text (last quote was close quote)
291     IF(l_right_quote > 0)
292     THEN
293         l_unquoted := l_unquoted || ' ' || SUBSTR(p_string, l_right_quote + 1);
294     END IF;
295 
296    --add unquoted text first to final keyword string
297    IF(LENGTH( trim (l_unquoted) ) > 0)
298    THEN
299      l_processed_keyword := l_unquoted;
300      l_processed_keyword := Escape_Special_Char(l_processed_keyword);
301      l_processed_keyword :=
302        Replace_Whitespace(l_processed_keyword, p_search_option);
303    END IF;
304 
305    -- loop and add all the phrases
306    l_counter := l_phrase_list.FIRST;
307    WHILE l_counter IS NOT NULL
308    LOOP
309       --dbms_output.put_line('Phrase[' || l_counter || '] = ' || l_phrase_list(l_counter));
310       --process each phrase as an exact phrase
311       l_phrase := Escape_Special_Char( l_phrase_list(l_counter) );
312       l_phrase := Replace_Whitespace(l_phrase, Ibc_Content_Search_Pvt.MATCH_PHRASE);
313       l_phrase := '(' || l_phrase || ')';
314       l_processed_keyword :=
315         Append_Query_Term(l_processed_keyword, l_phrase, p_search_option);
316       l_counter := l_phrase_list.NEXT(l_counter);
317 
318    END LOOP;
319 
320    -- Note some calling procedures do not properly handle an empty query
321    -- For now, simply return ' ', which will match nothing
322    IF( trim (l_processed_keyword) IS NULL)
323    THEN
324      l_processed_keyword := ' '' '' ';
325    END IF;
326 
327      RETURN l_processed_keyword;
328   END Parse_Keywords;
329 
330   --
331   -- Private Utility function
332   -- This function build the theme query component of a search
333   -- This is essentially wrapping the keywords with a 'about()'
334   -- intermedia function call.
335   -- The string parameter passed into the intermedia 'about()'
336   -- function has a limit of 255 characters. This function gets
337   -- around that limit by breaking the query string up into < 255
338   -- character chunks, wrapping each chunk with a separate 'about()'
339   -- function and accumulating the theme search chunks together.
340   FUNCTION Build_Intermedia_Theme_Query( p_raw_query_keywords  IN VARCHAR2 )
341     RETURN VARCHAR2
342   IS
343     l_theme_querystring VARCHAR2(30000);
344     l_chunksize     INTEGER := 245;
345     l_pos_raw       INTEGER;
346     l_pos_endchunk  INTEGER;
347     l_len_raw       INTEGER;
348     l_chunk_count   INTEGER := 0;
349   BEGIN
350     l_len_raw := LENGTH(p_raw_query_keywords);
351     l_pos_raw := 1;
352 
353     WHILE( l_pos_raw < l_len_raw ) LOOP
354       l_chunk_count := l_chunk_count + 1;
355 
356       -- Set end position of next chunck
357       IF( l_pos_raw + l_chunksize - 1  > l_len_raw ) THEN
358         l_pos_endchunk := l_len_raw;
359       ELSE
360         l_pos_endchunk := l_pos_raw + l_chunksize - 1;
361         -- adjust the endchunk to the last word boundary
362         l_pos_endchunk := INSTR( p_raw_query_keywords, ' ',
363                                  -(l_len_raw-l_pos_endchunk+1) );
364       END IF;
365 
366       -- wrap next chunk with 'about()' and append to
367       -- the theme query string buffer with accumulate.
368       IF( l_chunk_count > 1 ) THEN
369         l_theme_querystring := l_theme_querystring || ',';
370       END IF;
371 
372       l_theme_querystring := l_theme_querystring || 'about(' ||
373         SUBSTR(p_raw_query_keywords,
374                l_pos_raw,
375                l_pos_endchunk - l_pos_raw + 1)||')';
376 
377       l_pos_raw := l_pos_endchunk + 1;
378     END LOOP;
379     RETURN l_theme_querystring;
380   END Build_Intermedia_Theme_Query;
381   --
382   -- Private Utility function
383   -- This is the main query-rewrite function. Given a raw
384   -- user-entered keyword string and the search method chosen,
385   -- this function will construct the appropriate Oracle Text
386   -- query string. This is independent of whether the search
387   -- is for solutions or statements or anything else.
388   -- NOTE: This function does NOT incorporate product, platform,
389   -- category, or other metadata information into the Text query.
390   -- Those predicates are left to the caller to append.
391   FUNCTION Build_Intermedia_Query
392   ( p_string IN VARCHAR2,
393     p_search_option IN NUMBER )
394   RETURN VARCHAR2
395   IS
396     l_about_query VARCHAR2(32000) := p_string;
397     l_keyword_query VARCHAR2(32000) := p_string;
398     l_iQuery_str VARCHAR2(32000); -- final intermedia query string
399     lenb INTEGER;
400     len INTEGER;
401   BEGIN
402 
403     -- If the Search option chosen is THEME Search or if there is
404     -- no search option chosen, then rewrite the raw text query
405     -- with the theme search query and concatenate it with a regular
406     -- non-theme based rewritten query
407     IF (p_search_option = Ibc_Content_Search_Pvt.THEME_BASED OR
408         p_search_option IS NULL) --DEFAULT
409     THEN
410       l_keyword_query :=
411         Build_Keyword_Query
412          ( p_string => l_keyword_query,
413            p_search_option=> NULL);
414       l_about_query :=
415         Build_Intermedia_Theme_Query( Escape_Special_Char(l_about_query) );
416       l_iQuery_str := '('||l_about_query||' OR '||l_keyword_query||')';
417     ELSE
418     -- Else just build the standard, non-theme based rewritten query
419       l_keyword_query :=
420         Build_Keyword_Query
421         ( p_string => l_keyword_query,
422           p_search_option => p_search_option );
423 
424       l_iQuery_str := '( ' || l_keyword_query || ' )';
425     END IF;
426 
427     -- Return the rewritten text query criteria
428     RETURN l_iQuery_str;
429 
430   END Build_Intermedia_Query;
431   --
432   -- Private Utility function
433   -- Convert Text query critiera string into keyword query
434   -- with special characters handled
435   -- Since 12.0, delegates to Parse_Keywords
436   --
437   FUNCTION Build_Keyword_Query(
438     p_string        IN VARCHAR2,
439     p_search_option IN NUMBER
440   ) RETURN VARCHAR2
441   IS
442     --l_string varchar2(32000) := p_string;
443   BEGIN
444     --l_string := Escape_Special_Char(l_string);
445     --return Replace_Whitespace(l_string, p_search_option);
446     RETURN parse_keywords(p_string, p_search_option);
447   END Build_Keyword_Query;
448 
449 -- Constructs the intermedia query that should be used in the
450 -- CONTAINS predicate for a Content search
451 --
452 FUNCTION Build_Simple_Text_Query
453   (
454     p_qry_string IN VARCHAR2,
455     p_search_option IN NUMBER
456   )
457   RETURN VARCHAR2
458 IS
459   l_query_str VARCHAR2(30000) := p_qry_string;
460 
461 BEGIN
462 
463   IF (p_search_option = Ibc_Content_Search_Pvt.INTERMEDIA_SYNTAX) -- Intermedia Syntax
464   THEN
465        RETURN l_query_str;
466   END IF;
467 
468   l_query_str := Build_Intermedia_Query( l_query_str, p_search_option);
469 
470   RETURN l_query_str;
471 END Build_Simple_Text_Query;
472 END Ibc_Content_Search_Pvt;