View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.query;
25  
26  import java.util.List;
27  import net.jcip.annotations.ThreadSafe;
28  import org.modeshape.common.util.CheckArg;
29  import org.modeshape.graph.query.QueryResults.Statistics;
30  import org.modeshape.graph.query.model.Column;
31  import org.modeshape.graph.query.model.Constraint;
32  import org.modeshape.graph.query.model.QueryCommand;
33  import org.modeshape.graph.query.optimize.Optimizer;
34  import org.modeshape.graph.query.optimize.RuleBasedOptimizer;
35  import org.modeshape.graph.query.plan.CanonicalPlanner;
36  import org.modeshape.graph.query.plan.PlanHints;
37  import org.modeshape.graph.query.plan.PlanNode;
38  import org.modeshape.graph.query.plan.Planner;
39  import org.modeshape.graph.query.plan.PlanNode.Property;
40  import org.modeshape.graph.query.plan.PlanNode.Traversal;
41  import org.modeshape.graph.query.plan.PlanNode.Type;
42  import org.modeshape.graph.query.process.Processor;
43  import org.modeshape.graph.query.process.QueryResultColumns;
44  import org.modeshape.graph.query.validate.Schemata;
45  
46  /**
47   * A query engine that is able to execute formal queries expressed in the Graph API's {@link QueryCommand Abstract Query Model}.
48   */
49  @ThreadSafe
50  public class QueryEngine implements Queryable {
51  
52      private final Planner planner;
53      private final Optimizer optimizer;
54      private final Processor processor;
55  
56      /**
57       * Create a new query engine given the {@link Planner planner}, {@link Optimizer optimizer}, {@link Processor processor}, and
58       * {@link Schemata schemata}.
59       * 
60       * @param planner the planner that should be used to generate canonical query plans for the queries; may be null if the
61       *        {@link CanonicalPlanner} should be used
62       * @param optimizer the optimizer that should be used to optimize the canonical query plan; may be null if the
63       *        {@link RuleBasedOptimizer} should be used
64       * @param processor the processor implementation that should be used to process the planned query and return the results
65       * @throws IllegalArgumentException if the processor reference is null
66       */
67      public QueryEngine( Planner planner,
68                          Optimizer optimizer,
69                          Processor processor ) {
70          CheckArg.isNotNull(processor, "processor");
71          this.planner = planner != null ? planner : new CanonicalPlanner();
72          this.optimizer = optimizer != null ? optimizer : new RuleBasedOptimizer();
73          this.processor = processor;
74      }
75  
76      /**
77       * {@inheritDoc}
78       * 
79       * @see org.modeshape.graph.query.Queryable#execute(org.modeshape.graph.query.QueryContext,
80       *      org.modeshape.graph.query.model.QueryCommand)
81       */
82      public QueryResults execute( QueryContext context,
83                                   QueryCommand query ) {
84          CheckArg.isNotNull(context, "context");
85          CheckArg.isNotNull(query, "query");
86  
87          // Create the canonical plan ...
88          long start = System.nanoTime();
89          PlanNode plan = planner.createPlan(context, query);
90          long duration = System.nanoTime() - start;
91          Statistics stats = new Statistics(duration);
92  
93          QueryResultColumns resultColumns = QueryResultColumns.empty();
94          if (!context.getProblems().hasErrors()) {
95              // Optimize the plan ...
96              start = System.nanoTime();
97              PlanNode optimizedPlan = optimizer.optimize(context, plan);
98              duration = System.nanoTime() - start;
99              stats = stats.withOptimizationTime(duration);
100 
101             // Find the query result columns ...
102             start = System.nanoTime();
103             resultColumns = determineQueryResultColumns(optimizedPlan, context.getHints());
104             duration = System.nanoTime() - start;
105             stats = stats.withOptimizationTime(duration);
106 
107             if (!context.getProblems().hasErrors()) {
108                 // Execute the plan ...
109                 try {
110                     start = System.nanoTime();
111                     return processor.execute(context, query, stats, optimizedPlan);
112                 } finally {
113                     duration = System.nanoTime() - start;
114                     stats = stats.withOptimizationTime(duration);
115                 }
116             }
117         }
118         // There were problems somewhere ...
119         return new org.modeshape.graph.query.process.QueryResults(resultColumns, stats, context.getProblems());
120     }
121 
122     protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan,
123                                                               PlanHints hints ) {
124         // Look for which columns to include in the results; this will be defined by the highest PROJECT node ...
125         PlanNode project = optimizedPlan.findAtOrBelow(Traversal.LEVEL_ORDER, Type.PROJECT);
126         if (project != null) {
127             List<Column> columns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
128             // Determine whether to include the full-text search scores in the results ...
129             boolean includeFullTextSearchScores = hints.hasFullTextSearch;
130             if (!includeFullTextSearchScores) {
131                 for (PlanNode select : optimizedPlan.findAllAtOrBelow(Type.SELECT)) {
132                     Constraint constraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
133                     if (QueryResultColumns.includeFullTextScores(constraint)) {
134                         includeFullTextSearchScores = true;
135                         break;
136                     }
137                 }
138             }
139             return new QueryResultColumns(columns, includeFullTextSearchScores);
140         }
141         return QueryResultColumns.empty();
142     }
143 
144 }