Table of Contents
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.
Interfaces for query objects, query handlers and dependent objects.
Note that these interfaces do not specify how query objects are constructed, see next section.
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.
package org.mmbase.storage.search; public interface SearchQueryHandler { public List getNodes(SearchQuery query, MMObjectBuilder builder) throws SearchQueryException;
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;
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 is part of the MMBase documentation.
For questions and remarks about this documentation mail to: documentation@mmbase.org