SearchQuery introduction


Table of Contents

1. Background
2. Resources
3. Package descriptions
3.1. org.mmbase.storage.search
3.2. org.mmbase.storage.search.implementation
3.3. org.mmbase.storage.search.implementation.database
4. Query objects
4.1. Interfaces
4.2. Implementation
4.3. Code samples
5. Query handlers
5.1. SearchQueryHandler interface
5.2. Retrieving search results
5.3. BasicQueryHandler
5.4. Code samples
6. Sample program listings
6.1. QuerySampleCode
6.2. QueryHandlerSampleCode

The database search queries project was started to address the following needs:

The plan of action for the project defined these three stages, that have now been completed:

This article introduces design and implementation of the resulting SearchQuery API's.

If you're looking for the shortest path to code-level understanding, try skipping the next sections for the moment, and jump straight to the "code samples" sections right away.

Either way, once you understand the relevant code samples, the javadoc documentation provides the API details you'll need to go from there.

These code samples concentrate on how query objects represent search query requests analogous to SQL92 SELECT statements. To see how to retrieve actual nodes using query objects, refer to the code samples in the "Query handlers" sections.

In these examples, the represented queries are shown in SQL92 form, ignoring vendor specific issues like reserved words and deviating syntax. (Actual query handlers will take these into account as well.)

An example of creating a simple query with one step and one field:

        MMBase mmbase = MMBase.getMMBase();
        MMObjectBuilder images = mmbase.getBuilder("images");
        InsRel insrel = mmbase.getInsRel();
        MMObjectBuilder pools = mmbase.getBuilder("pools");

        // Create query.
        BasicSearchQuery query = new BasicSearchQuery();
        BasicStep step1 = query.addStep(images);
        FieldDefs imagesTitle = images.getField("title");
        BasicStepField field1 = query.addField(step1, imagesTitle);
 Result:
        SELECT title FROM <basename>_images images

Add a related step.

        InsRel insrel = mmbase.getInsRel();
        MMObjectBuilder pools = mmbase.getBuilder("pools");
        BasicRelationStep step2 = query.addRelationStep(insrel, pools);
        step2.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
 Result:
        SELECT images.title
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)

Add another field.

        BasicStep step3 = (BasicStep) step2.getNext();
        FieldDefs poolsName = pools.getField("name");
        BasicStepField field2 = query.addField(step3, poolsName);
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)

Add nodenumber constraint.

        step3.addNode(100);
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)

Add field value constraint.

        BasicFieldValueConstraint constraint
            = new BasicFieldValueConstraint(field1, "Logo");
        query.setConstraint(constraint);
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
        AND images.title='Logo'

Add sort order.

        BasicSortOrder sortOrder = query.addSortOrder(field1);
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
        AND images.title='Logo'
        ORDER BY title ASC

An example of creating an aggregating query counting a single field:

        query = new BasicSearchQuery(true);
        step1 = query.addStep(images);
        FieldDefs imagesNumber = images.getField("number");
        BasicAggregatedField field1a = query.addAggregatedField(
            step1, imagesNumber, AggregatedField.AGGREGATION_TYPE_COUNT);
        field1a.setAlias("number_count");
 Result:
        SELECT COUNT(number) AS number_count
        FROM <basename>_images images

Add grouping on a second field.

        BasicAggregatedField field2a = query.addAggregatedField(
            step1, imagesTitle, AggregatedField.AGGREGATION_TYPE_GROUP_BY);
 Result:
        SELECT COUNT(number) AS number_count,
            title
        FROM <basename>_images images
        GROUP BY title

The complete listing of the sample program can be found at the end of this article.






/*

This software is OSI Certified Open Source Software.
OSI Certified is a certification mark of the Open Source Initiative.

The license (Mozilla version 1.0) can be read at the MMBase site.
See http://www.MMBase.org/license

*/
package org.mmbase.storage.search.sample;

import org.mmbase.module.core.*;
import org.mmbase.module.corebuilders.*;
import org.mmbase.storage.search.*;
import org.mmbase.storage.search.implementation.*;
import org.mmbase.storage.search.implementation.database.*;

/**
 * Sample code demonstrating basic usage of the <code>SearchQuery</code>
 * interfaces.
 * <p>
 * Requires the following builders to be active: <code>builders</code> and
 * <code>pools</code>.
 *
 * @author  Rob van Maris
 * @version $Revision: 1.11 $
 * @since MMBase-1.7
 */
public class QuerySampleCode {

    /**
     * Demo application, writes resulting SQL strings to System.out.
     * <br>
     * Requires one commandline argument: the path to the MMBase config directory.
     * @param args Commandline arguments.
     */
    public static void main(String[] args) throws Exception {
        if (args.length < 1) {
            System.out.println(
            "Requires one commandline argument: the path to the MMBase config directory.");
            System.exit(1);
        }
        MMBaseContext.init(args[0], true);

        // Sql handler that generates SQL strings.
        SqlHandler sqlHandler = new BasicSqlHandler(new java.util.HashMap());

        MMBase mmbase = MMBase.getMMBase();
        MMObjectBuilder images = mmbase.getBuilder("images");

        // Create query.
        BasicSearchQuery query = new BasicSearchQuery();
        BasicStep step1 = query.addStep(images);
        FieldDefs imagesTitle = images.getField("title");
        BasicStepField field1 = query.addField(step1, imagesTitle);

/*
 Result:
        SELECT title FROM <basename>_images images
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add a related step.
        InsRel insrel = mmbase.getInsRel();
        MMObjectBuilder pools = mmbase.getBuilder("pools");
        BasicRelationStep step2 = query.addRelationStep(insrel, pools);
        step2.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);

/*
 Result:
        SELECT images.title
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add another field.
        BasicStep step3 = (BasicStep) step2.getNext();
        FieldDefs poolsName = pools.getField("name");
        BasicStepField field2 = query.addField(step3, poolsName);

/*
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add nodenumber constraint.
        step3.addNode(100);

/*
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add field value constraint.
        BasicFieldValueConstraint constraint
            = new BasicFieldValueConstraint(field1, "Logo");
        query.setConstraint(constraint);

/*
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
        AND images.title='Logo'
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add sort order.
        BasicSortOrder sortOrder = query.addSortOrder(field1);

/*
 Result:
        SELECT images.title,
            pools.name
        FROM <basename>_images images,
            <basename>_insrel insrel,
            <basename>_pools pools
        WHERE pools.number IN (100)
        AND (images.number=insrel.snumber
        AND pools.number=insrel.dnumber)
        AND images.title='Logo'
        ORDER BY title ASC
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // An example of creating an aggregating query counting a single field:
        query = new BasicSearchQuery(true);
        step1 = query.addStep(images);
        FieldDefs imagesNumber = images.getField("number");
        BasicAggregatedField field1a = query.addAggregatedField(
            step1, imagesNumber, AggregatedField.AGGREGATION_TYPE_COUNT);
        field1a.setAlias("number_count");

/*
 Result:
        SELECT COUNT(number) AS number_count
        FROM <basename>_images images
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        // Add grouping on a second field.
        BasicAggregatedField field2a = query.addAggregatedField(
            step1, imagesTitle, AggregatedField.AGGREGATION_TYPE_GROUP_BY);

/*
 Result:
        SELECT COUNT(number) AS number_count,
            title
        FROM <basename>_images images
        GROUP BY title
 */
        System.out.println("Result:\n" + sqlHandler.toSql(query, sqlHandler));

        System.exit(0);
    }

}
package org.mmbase.storage.search.sample;

import java.util.*;
import org.mmbase.module.core.*;
import org.mmbase.module.corebuilders.*;
import org.mmbase.storage.search.*;
import org.mmbase.storage.search.implementation.*;
import org.mmbase.storage.search.implementation.database.*;

/**
 * Sample code demonstrating basic usage of the
 * {@link org.mmbase.storage.search.SearchQueryHandler SearchQueryHandler}
 * interface.
 * <p>
 * Requires the following builders to be active: <code>builders</code> and
 * <code>pools</code>.
 *
 * @author  Rob van Maris
 * @version $Revision: 1.11 $
 * @since MMBase-1.7
 */
public class QueryHandlerSampleCode {

    /**
     * Demo application.
     * <br>
     * Requires one commandline argument: the path to the MMBase config directory.
     * @param args Commandline arguments.
     */
    public static void main(String args[]) throws Exception {
        if (args.length < 1) {
            System.out.println(
            "Requires one commandline argument: the path to the MMBase config directory.");
            System.exit(1);
        }
        MMBaseContext.init(args[0], true);
        MMBase mmbase = MMBase.getMMBase();

        // Sql handler that generates SQL strings.
        SqlHandler sqlHandler = new BasicSqlHandler(new java.util.HashMap());

        /* Several ways to get a queryhandler instance:
         1) Create a basic queryhandler, that supports (just) the
            ANSI SQL92 standard:
        SearchQueryHandler handler = new BasicQueryHandler(sqlHandler);
         2) Get the database support class, that is configured for the
            specific database used:
        SearchQueryHandler handler = mmbase.getDatabase();
         */

        // Queryhandler provided by databaselayer:
        SearchQueryHandler handler = mmbase.getDatabase();

        MMObjectBuilder pools = mmbase.getBuilder("pools");
        MMObjectBuilder images = mmbase.getBuilder("images");
        InsRel insrel = mmbase.getInsRel();

        // EXAMPLE 1: query retrieving real nodes (of type pools).
        NodeSearchQuery query1 = new NodeSearchQuery(pools);

/*
 Query equivalent to:
        SELECT * FROM <basename>_pools pools
 Returns:
    real nodes with these fields: number, otype, owner, name, description
 */
        System.out.println("Query: " + sqlHandler.toSql(query1, sqlHandler));

        // Execute, get result as real nodes.
        List nodes1 = handler.getNodes(query1, pools);
        Iterator iNodes1 = nodes1.iterator();
        while (iNodes1.hasNext()) {
            MMObjectNode node = (MMObjectNode) iNodes1.next();
            System.out.println("Real node: " + node);
        }

        // EXAMPLE 2: query retrieving clusternodes.
        BasicSearchQuery query2 = new BasicSearchQuery();
        BasicStep step2a = query2.addStep(pools);
        BasicRelationStep step2b = query2.addRelationStep(insrel, images);
        step2b.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
        Step step2c = step2b.getNext();
        // Add at least the number fields of all steps.
        query2.addField(step2a, pools.getField("number"));
        query2.addField(step2b, insrel.getField("number"));
        query2.addField(step2c, images.getField("number"));

/*
 Query:
        SELECT
            pools.number,
            insrel.number,
            images.number
        FROM
            <basename>_pools pools,
            <basename>_insrel insrel,
            <basename>_images images
        WHERE (pools.number = insrel.snumber
        AND images.number = insrel.dnumber)
 */
        System.out.println("Query: " + sqlHandler.toSql(query2, sqlHandler));

        // Execute, result as clusternodes.
        List nodes2 = handler.getNodes(query2, new ClusterBuilder(mmbase));
        Iterator iNodes2 = nodes2.iterator();
        while (iNodes2.hasNext()) {
            ClusterNode node = (ClusterNode) iNodes2.next();
            System.out.println("Clusternode: " + node);
        }

/*
 Returns:
        clusternodes with these fields: pools.number, insrel.number, images.number
 */

        // EXAMPLE 3: query retrieving clusternodes using table aliases.
        BasicSearchQuery query3 = new BasicSearchQuery();
        BasicStep step3a = query3.addStep(pools);
        BasicRelationStep step3b = query3.addRelationStep(insrel, images);
        step3b.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
        BasicStep step3c = (BasicStep) step3b.getNext();
        // Set table aliases.
        step3a.setAlias("pools0");
        step3c.setAlias("images3");
        // Add at least the number fields of all steps.
        query3.addField(step3a, pools.getField("number"));
        query3.addField(step3b, insrel.getField("number"));
        query3.addField(step3c, images.getField("number"));

/*
 Query:
        SELECT
            pools0.number,
            insrel.number,
            images3.number
        FROM
            <basename>_pools pools0,
            <basename>_insrel insrel,
            <basename>_images images3
        WHERE (pools0.number = insrel.snumber
        AND images3.number = insrel.dnumber)
 */
        System.out.println("Query: " + sqlHandler.toSql(query3, sqlHandler));

        // Execute, result as clusternodes.
        List nodes3 = handler.getNodes(query3, new ClusterBuilder(mmbase));
        Iterator iNodes3 = nodes3.iterator();
        while (iNodes3.hasNext()) {
            ClusterNode node = (ClusterNode) iNodes3.next();
            System.out.println("Clusternode: " + node);
        }

/*
 Returns:
        clusternodes with these fields: pools0.number, insrel.number, images3.number
 */

        // EXAMPLE 4: query retrieving resultnodes.
        BasicSearchQuery query4 = new BasicSearchQuery();
        BasicStep step4a = query4.addStep(pools);
        // Add some fields with field aliases.
        query4.addField(step4a, pools.getField("number")).setAlias("field1");
        query4.addField(step4a, pools.getField("name")).setAlias("field2");
        query4.addField(step4a, pools.getField("description")).setAlias("field3");

/*
 Query:
        SELECT
            number AS field1,
            name AS field2,
            description AS field3
        FROM
            <basename>_pools pools
 */
        System.out.println("Query: " + sqlHandler.toSql(query4, sqlHandler));

        // Execute, result as resultnodes.
        List nodes4 = handler.getNodes(query4, new ResultBuilder(mmbase, query4));
        Iterator iNodes4 = nodes4.iterator();
        while (iNodes4.hasNext()) {
            ResultNode node = (ResultNode) iNodes4.next();
            System.out.println("Resultnode: " + node);
        }

/*
 Returns:
        resultnodes with these fields: field1, field2, field3
 */

        // EXAMPLE 5: query retrieving aggregated resultnodes.
        BasicSearchQuery query5 = new BasicSearchQuery(true);
        BasicStep step5a = query5.addStep(pools);
        // Add some aggregated fields with field aliases.
        query5.addAggregatedField(step5a, pools.getField("number"),
            AggregatedField.AGGREGATION_TYPE_COUNT).setAlias("pool_count");
        query5.addAggregatedField(step5a, pools.getField("number"),
            AggregatedField.AGGREGATION_TYPE_MIN).setAlias("min_number");
        query5.addAggregatedField(step5a, pools.getField("number"),
            AggregatedField.AGGREGATION_TYPE_MAX).setAlias("max_number");

/*
 Query:
        SELECT
            COUNT(number) AS pool_count,
            MIN(number) AS min_number,
            MAX(number) AS max_number
        FROM
            <basename>_pools pools
 */
        System.out.println("Query: " + sqlHandler.toSql(query5, sqlHandler));

        // Execute, result as resultnodes.
        List nodes5 = handler.getNodes(query5, new ResultBuilder(mmbase, query5));
        Iterator iNodes5 = nodes5.iterator();
        while (iNodes5.hasNext()) {
            ResultNode node = (ResultNode) iNodes5.next();
            System.out.println("Resultnode: " + node);
        }

/*
 Returns:
        a resultnode with these fields: pool_count, min_number, max_number
 */

        System.exit(0);
    }
}

This is part of the MMBase documentation.

For questions and remarks about this documentation mail to: [email protected]