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
Table of Contents
The database search queries project was started to address the following needs:
deliver search queries inside MMBase in a uniform and device-independent manner
provide mechanisms to process these queries in a device-specific way that ensures optimal performance
The plan of action for the project defined these three stages, that have now been completed:
Introduction a SearchQuery
object,
capable of representing all kind of search queries supported by
standard relational databases.
Introduction of an extension mechanism to enable use of additional, vendor specific, database functionality.
Integration of the SearchQuery
into
existing MMBase code.
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.
Sourcecode of JUnit tests is available from \dbsq\test\src in CVS module speeltuin.
Sourcecode of the code samples is available from \dbsq\src in CVS module speeltuin.
Interfaces for query objects, query handlers and dependent objects.
Query objects represent search requests, query handlers process requests represented by query objects. (More on this further on.)
The query interfaces specify the interfaces exposed by query objects, in order for query handlers to retrieve the query data. This will enable query handlers to be implemented independent from query implementations.
Note that these interfaces do not specify how query objects are constructed, see next section.
Classes implementing the query interfaces described in the previous section. These also provide methods to construct queries.
Search queries are defined by these interfaces:
SearchQuery
: the query
Step
: a query step, generalizes the
notion of a table
StepField
: a step field, generalizes
the notion of a field from a table
RelationStep
: a Step
defining a relation between two steps.
SortOrder
: a sorting order
Constraint
: a constraint
CompositeConstraint
: a number of
constraints, combined using AND or OR.
Constraint
subinterfaces for specific
constraint types.
In all cases, the interface provides:
Accessor methods, to retrieve the query data.
equals()
and
hashCode()
methods, enabling queries and
parts of queries to be compared to each other. This is anticipates
future developments, like caching of queries.
A toString()
method, provided for
debugging purposes.
The implementation classes provide methods to construct query objects. See next section for examples.
It may seem that the interfaces have been provided in anticipation of a number of different implementations. This is in fact not the case, as I can't think of a reason why one (basic) implementation should not suffice. On the other hand, a number of different query handlers seems likely. The interfaces are provided as a stable and clearcut specification of the interaction between query and query handler, allowing future enhancements to the query implementation to evolve, without breaking the query handler implementation.
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.
Now that we can create query objects, we need a way to deliver these to the database layer.
For this purpose, the databaselayer should provide an object
implementing the SearchQueryHandler
interface,
that can process queries.
package org.mmbase.storage.search; public interface SearchQueryHandler { public List getNodes(SearchQuery query, MMObjectBuilder builder) throws SearchQueryException;
Method getNodes()
executes the search,
and returns the result as a list of nodes of the type corresponding to
the specified builder. I.e. either real nodes (MMObjectNode
instances), clusternodes (ClusterNode
instances)
or resultnodes (ResultNode
instances). More on
this in the next section.
In addition, a method is provided to test if the underlying storage supports a certain type of constraint:
public int getSupportLevel(Constraint constraint, SearchQuery query) throws SearchQueryException;
Four levels of support are defined:
public final static int SUPPORT_NONE = 0; public final static int SUPPORT_WEAK = 1; public final static int SUPPORT_NORMAL = 2; public final static int SUPPORT_OPTIMAL = 3;
A similar method is provided to test support for other features of a search query:
public int getSupportLevel(int feature, SearchQuery query) throws SearchQueryException;
This method takes as argument one of these constraints:
public final static int FEATURE_MAX_NUMBER = 1; public final static int FEATURE_OFFSET = 2;
Depending on the second argument that is provided (builder), the
handler's method getNodes(query,builder)
will return a list of nodes of one of the following types:
MMObjectNode
- when a
MMObjectBuilder
is specified, or a subclass
that is not a VirtualBuilder
.
For this to work as expected, the query is required to include all persistent fields of the nodetype that is retrieved.
Generally, this functionality is not meant to be accessed
directly, but instead through the MMObjectBuilder method
getNodes(query)
, which takes care of things
like nodecache and inheritance issues as well.
ClusterNode
- when a
ClusterBuilder
is specified.
For this to work properly, the aliases of the steps in the query should follow the conventions for tablenames in multilevel queries. (TODO: elaborate)
Generally, this functionality is not meant to be accessed
directly, but instead through the ClusterBuilder method
getClusterNodes(query)
.
ResultNode
- when a
ResultBuilder
is specified.
Resultnodes are able to represent the results of arbitrary
search queries without the restrictions of real or clusternodes. In
particular: field aliases can be used to rename the fields in the
result, results can be grouped/aggregated, and aggregate functions
like COUNT
,
MIN
and MAX
can be
used.
Generally, this functionality can be accessed directly, and is meant to provide an alternative to directly executing search queries on the database.
A basic implementation of the SearchQueryHandler
interface is provided by BasicQueryHandler,
that
can be used when the storage is provided by a SQL 92 compliant database.
A BasicQueryHandler
instance uses an
SqlHandler
object to create an SQL statement,
executes the resulting query and converts the queryresults back to
nodes.
The SqlHandler
interface isolates the
functionality concerned with translating query objects to actual,
possibly vendor-specific, SQL statements. The actual implementation
class used will depend on the underlying database, and can be extended
with additional functionality using additional handlers. See the API
javadoc for more info.
Example 1. Retrieving real nodes
As a demonstration of retrieving real nodes, this example lists all nodes of type "pools".
MMBase mmbase = MMBase.getMMBase(); 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);
Note the use of NodeSearchQuery
to
produce a query that retrieves real nodes.
This will creates a query equivalent to:
SELECT * FROM <basename>_pools pools
This code executes the query and prints the result - real nodes with these fields: number, otype, owner, name, description.
// Execute, get result as real nodes. List nodes1 = pools.getNodes(query1); Iterator iNodes1 = nodes1.iterator(); while (iNodes1.hasNext()) { MMObjectNode node = (MMObjectNode) iNodes1.next(); System.out.println("Real node: " + node); }
Example 2. Retrieving clusternodes
As a demonstration of retrieving clusternodes, this example lists number fields of related pools and images, following relations from pools to images.
// 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"));
This creates the following 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)
This code executes the query and prints the results - clusternodes with these fields: pools.number, insrel.number, images.number.
// Execute, result as clusternodes. ClusterBuilder clusterBuilder = mmbase.getClusterBuilder(); List nodes2 = clusterBuilder.getClusterNodes(query2); Iterator iNodes2 = nodes2.iterator(); while (iNodes2.hasNext()) { ClusterNode node = (ClusterNode) iNodes2.next(); System.out.println("Clusternode: " + node); }
Example 3. Retrieving clusternodes using table aliases
Alternatively we could use table aliases to use tablenames appended with a digit:
// 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"));
This creates the following 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)
Execution results in clusternodes with these fields: pools0.number, insrel.number, images3.number.
// Execute, result as clusternodes. List nodes3 = clusterBuilder.getClusterNodes(query3); Iterator iNodes3 = nodes3.iterator(); while (iNodes3.hasNext()) { ClusterNode node = (ClusterNode) iNodes3.next(); System.out.println("Clusternode: " + node); }
Example 4. Retrieving resultnodes
As a demonstration of retrieving resultnodes, this example lists arbitrary fields of nodes of type "pools", using field aliases.
// 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");
This creates the following query:
SELECT number AS field1, name AS field2, description AS field3 FROM <basename>_pools pools
This code executes the query and prints the results - resultnodes with these fields: field1, field2, field3
// 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); }
Example 5. Retrieving aggregated resultnodes
Finally, to demonstrate aggregating queries, this example lists the results of applying aggregating functions COUNT, MIN and MAX on the "number" field of all nodes of type "pools".
// 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("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");
This creates the following query:
SELECT COUNT(number) AS pool_count, MIN(number) AS min_number, MAX(number) AS max_number FROM <basename>_pools pools
This code executes the query and prints the result - a resultnode with these fields: pool_count, min_number, max_number
// 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); }
/* 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]