Oracle8 ConText Cartridge Workbench User's Guide Release 2.3 A57700-01 |
Library | Product | Contents | Index

# 4
CTXQUERY Forms Sample Application

This chapter presents the CTXQUERY sample application, developed with Oracle Forms 4.5, and illustrates one method for developing applications using ConText.

The topics covered in this chapter are:

- Overview of CTXQUERY
- Setting Up the Sample
- CTXQUERY Architecture

## Overview of CTXQUERY

The CTXQUERY sample application is a basic Oracle Forms 4.5 application which provides a graphical interface for performing text queries and DML operations with ConText. It supports all the query expression operators and can provide query term highlighting using asterisks in the specified documents.

Note: The CTXQUERY sample Forms application does not utilize any of the theme querying functionality provided by ConText. However, the sample application could be theme-query enabled for English-language documents simply by creating a theme indexing policy, indexing the column with the policy, and referencing the policy in the query block.

### Concepts

The ConText query concepts demonstrated in this example are:

- query expression syntax
- boolean and statistical (logical) operators
- expansion operators
- score thresholds
- result limits
- two-step queries
- sharing result tables
- term highlighting

### Layout

The form is laid out in four blocks on three canvases. There is a block and a canvas for each step of the query, hitlist, and view with one extra block for all the buttons. All canvases are displayed in a single window, CTXQUERY.

## Setting Up the Sample

This sections provides information about the setup tasks that must be performed before using the sample application:

- Demonstration Installation Scripts
- Oracle Forms Installation

### Demonstration Installation Scripts

Before the sample Forms application can be used, the demonstration installation scripts must be run. The demonstration installation scripts are provided with ConText and are installed automatically on the server machine during ConText installation; however, they must be run manually after ConText is installed.

The demonstration scripts perform the following tasks:

- create the articles sample table
- populate the articles table with text
- create a text index for the text column in the table

  Note: Because the CTXQUERY sample application requires setup before it can be used, an icon is not created for CTXQUERY during ConText Workbench installation. Once setup has been performed on the server machine, to start CTXQUERY, navigate to the directory where the ctxquery.fmb file is located (usually CTXW\DEMO in either C:\ORANT or C:\ORA95) and double-click on the file.

  See Also: For more information about the location of the demonstration scripts for setting up the sample application, see the Oracle8 Server installation documentation specific to your server operating system.

### Oracle Forms Installation

The sample application requires Oracle Forms (32-bit), version 4.5.7.4.0 or higher, to be installed.

Oracle Forms is not installed automatically during ConText Workbench installation and must be installed manually before using the sample application.

Note: If a version higher than 4.5.6.3.3 is installed on your machine, to use the sample application, the ctxquery.fmb file may have to be regenerated after installation.

## CTXQUERY Architecture

The CTXQUERY form is divided into eight distinct sections:

- Startup
- Enter the query screen
- Start the pre-query trigger
- Build the query expression
- Display an article in the view block
- Invoke the HIGHLIGHT procedure
- Perform DML Operations
- Cleanup

### Startup

On startup, the When-New-Form-Instance trigger initializes the form.

      When-New-Form-Instance
      set_window_property
        (FORMS_MDI_WINDOW, WINDOW_STATE, MAXIMIZE);
      set_window_property('CTXQUERY', WINDOW_STATE, MAXIMIZE);
      select query_id.nextval
        into :global.query_id
        from dual;

This maximizes the windows for display, then selects a query ID from a sequence.

This form is designed so that it can be used by multiple concurrent users, which means that the results tables are shared. When result tables are shared, a unique query ID separates each user's results from other users. In this form the unique ID is created as an increasing sequence of numbers with the last value stored in a global variable.

### Enter the query screen

The query screen is the first screen of the form displayed.

Each widget is part of the query block, which is a non-base-table block. There is one hidden field in this screen, query_string.

When the Query button is pressed, the When-Button-Pressed trigger goes to the hitlist block and executes a query:

    When-Button-Pressed on BUTTONS.QUERY

    go_block('hitlist');
    clear_block(no_validate);
    execute_query(all_records);

The hitlist block is based on the article_hitlist view. This view joins the query result table query_temp and the base table articles into a hitlist which has both score and article information. Although this block contains items for all the fields in this view, most are hidden. The visible page displays score, author, section, and title.

### Start the pre-query trigger

Before the query on the hitlist block is executed, the pre-query trigger fires. This trigger executes the first step of a two-step query, then limits the relational fields to the query criteria:

    Pre-Query on HITLIST

    build_query_string;

    ctx_query.contains('DEMO_POLICY',
                       :query.query_string,
                       'QUERY_TEMP',
                       1,
                       :global.query_id,
                       0,1,null);
    --
    --limit the relational fields to the query criteria

    --

    :hitlist.conid := :global.query_id;
    :hitlist.author := :query.author;
    :hitlist.section := :query.section;
    copy(:query.pub_date,'hitlist.pub_date');

### Build the query expression

The build_query_string procedure constructs the query expression by concatenating the term, weight, and operator fields from the form and putting them into the query.query_string hidden field.

build_query_string is divided into sections A through E.

#### Section A

Section A of the code clears the query string of any previous contents.

#### Section B

Section B takes each term from the user input form and builds the query expression as follows:

- attaches expansion operators (fuzzy, stem, soundex) to the front of each specified query term
- encases user-specified query arguments in curly brackets

  Query arguments are enclosed in curly brackets so that any special characters within the argument (e.g., the "&" in Q&A) will not be misinterpreted as ConText operators

- adds weighting factor

  If the user has filled in a weighting factor in the appropriate field of the input screen, this value is appended to the search argument.

- appends any specified logical operators to the end of the query expression

  When the concatenation is finished, a sample term looks like this:

  Expansion | Search Criteria | Weight | Boolean
  --- | --- | --- | ---
  ! | {cat} | *2 | &

  Note: In CTXQUERY, the user can enter up to three separate query expression. These four steps are repeated to for each query expression.

#### Section C

Section C of the code strips off extraneous operators at the end of the final query string since the last term has no boolean operator.

#### Section D

Section D adds the score threshold value to the query expression.

#### Section E

Section E appends the result limit.

#### BUILD_QUERY_STRING Procedure - Sample Code

    PROCEDURE build_query_string IS
    BEGIN
    --
    --Section A
    --
        :query.query_string := null;
    --
    --Section B
    --
        if (:query.qterm1 is not null) then
          :query.query_string := :query.query_string ||
            :query.qexp1 ||
            '{' || :query.qterm1 || '}' ||
            '*' || to_char(nvl(:query.qwt1, 1)) ||
            substr(:query.qop1,2,1);
        end if;
    if (:query.qterm2 is not null) then
        :query.query_string := :query.query_string ||
          :query.qexp2 ||
          '{' || :query.qterm2 || '}' ||
          '*' || to_char(nvl(:query.qwt2, 1)) ||
          substr(:query.qop2,2,1);
    end if;
    if (:query.qterm3 is not null) then
        :query.query_string := :query.query_string ||
          :query.qexp3 ||
          '{' || :query.qterm3 || '}' ||
          '*' || to_char(nvl(:query.qwt3, 1));
    end if;
    --
    --Section C
    --
    :query.query_string :=
    rtrim(:query.query_string, '&|,;-');
    --
    --Section D
    --
        if (:query.qthresh is not null) then
            :query.query_string := '(' || :query.query_string ||
            ')>' || to_char(:query.qthresh);
        end if;
    --
    --Section E
    --
        if (:query.qlimit is not null) then
            :query.query_string := '(' || :query.query_string ||
            '):' || to_char(:query.qlimit);
        end if;
    END;

### Display an article in the view block

When an article in the hitlist is double-clicked, the When-Mouse-Double-Click trigger on hitlist.title displays the article in the VIEW block:

    When-Mouse-Double-Click on HITLIST.TITLE
    GO_BLOCK('VIEW');
    EXECUTE_QUERY;

In order to display the correct article, the Pre-Query trigger limits the view block to display the article highlighted in the hitlist block:

    Pre-Query on VIEW:
    :view.article_id := :hitlist.article_id;

The view block is based on articles, and displays the full text of the article.

### Invoke the HIGHLIGHT procedure

When the highlight button on the view block is pressed, the When-Button-Pressed trigger on buttons.highlight invokes the CTX_QUERY.HIGHLIGHT procedure.

The highlight procedure is divided into sections A and B.

#### Section A

Section A call the HIGHLIGHT procedure and performs the following tasks:

- fetches the document
- parses the query
- identifies the matching terms
- inserts results into the highlight_temp table.

The highlight result table (highlight_temp) has the following structure:

Columns | TYPE
--- | ---
ID | NUMBER
OFFSET | NUMBER
LENGTH | NUMBER
STRENGTH | NUMBER

This procedure highlights matching query terms in the text window by surrounding them with <<< and >>>.

It is not possible to do an INSTR for the search terms because of the expansion operators; for example, go=going=gone in a stem expansion. Instead, use the highlight procedure in Section A to generate the highlights table.

This table holds the offset and the length of each word to be highlighted.

#### Section B

With the highlights generated, the cursor loop in Section B works through each offset, length pair backwards from last offset to first.

The asterisks insert won't change the other offsets. For each offset, length pair, the asterisks are inserted by reassigning the full text to: everything before the term (line 1), then the asterisks, then the term and some more asterisks (line 2) then everything after the term (line 3). Doing this repeatedly highlights all the terms in the document.

#### HIGHLIGHT Procedure -- Sample Code

    PROCEDURE highlight IS
      cursor highcur is select offset, length
        from highlight_temp
        where id = :global.query_id
        order by offset desc;
    --
    --  Section A
    --
    BEGIN
      ctx_query.highlight(
        'DEMO_POLICY',              -- policy name
        to_char(:view.article_id),  -- textkey
        :query.query_string,        -- query string
        :global.query_id,           -- query_id
        null,
        null,
        'HIGHLIGHT_TEMP',           -- highlight table
        null,
        null,
        null,
        null);
    --
    --  Section B
    --
      for hc in highcur loop
    1.    :view.text := substr(:view.text,1,hc.offset - 1) ||
    2.    '***'||substr(:view.text,hc.offset,hc.length)||'***'||
    3.    substr(:view.text,hc.offset+hc.length);
      end loop;
    END;

### Perform DML Operations

When a document is changed it needs to be re-indexed and new linguistic information needs to be extracted. This can be performed in a table trigger or in the application.

The view block contains pre-update and pre-index triggers that perform the necessary re-indexing when there is a change to article data. Both triggers invoke the reindex_article procedure.

The reindex_article procedure performs the following tasks:

- submits the document for text re-index (Section A)
- clears existing linguistic information (Section B)
- extracts new linguistic information (Section C)
- submits the linguistic requests (Section D)

  Note: There is no COMMIT; it is performed by the COMMIT of the change to the VIEW block. In SUBMIT, you must explicitly ask for it not to COMMIT (the FALSE second parameter).

  See Also: For more information about DML and re-indexing, see Oracle8 ConText Cartridge Administrator's Guide.

#### REINDEX_ARTICLE Procedure -- Sample Code

    PROCEDURE reindex_article IS
      handle number;
    begin
    #
    # Section A
    #
    ctx_dml.reindex('demo_policy', TO_CHAR(:view.article_id));
    #
    # Section B
    #
    delete from article_themes
    where pk = :view.article_id;

    delete from article_gists
    where pk = :view.article_id;
    #
    # Section C
    #
    ctx_ling.REQUEST_themes('demo_policy',
              TO_CHAR(:view.article_id),
                'article_themes');
    ctx_ling.REQUEST_gist('demo_policy',
              TO_CHAR(:view.article_id),
                'article_gists');
    #
    Section D
    #
      handle := ctx_ling.submit(0,FALSE,0);
    end;

### Cleanup

Before the session is ended, clean up any leftover rows in the hitlist result table (query_temp) and the highlight result table (highlight_temp) tables for the query ID:

    Post-Form
    delete from query_temp
    where conid = to_number(:global.query_id);
    delete from highlight_temp
    where id = to_number(:global.query_id);
    standard.commit;

The standard.commit statement executes an Oracle COMMIT without doing a Forms commit_form.

Prev | Next
| Copyright © 1997 Oracle Corporation. All Rights Reserved. |
Library | Product | Contents | Index