[dna-commits] DNA SVN: r1319 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/query/process and 6 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Mon Nov 16 18:24:06 EST 2009


Author: rhauch
Date: 2009-11-16 18:24:06 -0500 (Mon, 16 Nov 2009)
New Revision: 1319

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
Removed:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
   trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
   trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
Log:
DNA-467 Additional refactoring to move toward a working search engine.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -26,6 +26,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.collection.Problems;
 import org.jboss.dna.common.collection.SimpleProblems;
 import org.jboss.dna.common.util.CheckArg;
@@ -35,64 +36,120 @@
 import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
- * 
+ * An immutable context in which queries are to be executed. Each query context defines the information that is available during
+ * query execution.
  */
-public final class QueryContext {
+ at Immutable
+public class QueryContext {
     private final ExecutionContext context;
     private final PlanHints hints;
     private final Schemata schemata;
     private final Problems problems;
     private final Map<String, Object> variables;
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @param variables the mapping of variables and values, or null if there are no such variables
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
+                         Schemata schemata,
                          PlanHints hints,
-                         Schemata schemata,
                          Problems problems,
                          Map<String, Object> variables ) {
         CheckArg.isNotNull(context, "context");
+        CheckArg.isNotNull(schemata, "schemata");
         this.context = context;
         this.hints = hints != null ? hints : new PlanHints();
         this.schemata = schemata;
         this.problems = problems != null ? problems : new SimpleProblems();
         this.variables = variables != null ? Collections.<String, Object>unmodifiableMap(new HashMap<String, Object>(variables)) : Collections.<String, Object>emptyMap();
+        assert this.context != null;
+        assert this.hints != null;
+        assert this.schemata != null;
+        assert this.problems != null;
+        assert this.variables != null;
     }
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
+                         Schemata schemata,
                          PlanHints hints,
-                         Schemata schemata,
                          Problems problems ) {
-        this(context, hints, schemata, problems, null);
+        this(context, schemata, hints, problems, null);
     }
 
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
     public QueryContext( ExecutionContext context,
-                         PlanHints hints,
+                         Schemata schemata,
+                         PlanHints hints ) {
+        this(context, schemata, hints, null, null);
+    }
+
+    /**
+     * Create a new context for query execution.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
+    public QueryContext( ExecutionContext context,
                          Schemata schemata ) {
-        this(context, hints, schemata, null, null);
+        this(context, schemata, null, null, null);
     }
 
     /**
-     * @return context
+     * Get the execution context available to this query context.
+     * 
+     * @return the execution context; never null
      */
     public final ExecutionContext getExecutionContext() {
         return context;
     }
 
     /**
-     * @return hints
+     * Get the plan hints.
+     * 
+     * @return the plan hints; never null
      */
     public final PlanHints getHints() {
         return hints;
     }
 
     /**
-     * @return problems
+     * Get the problem container used by this query context. Any problems that have been encountered will be accumlated in this
+     * container.
+     * 
+     * @return the problem container; never null
      */
     public final Problems getProblems() {
         return problems;
     }
 
     /**
-     * @return schemata
+     * Get the definition of the tables available within this query context.
+     * 
+     * @return the schemata; never null
      */
     public Schemata getSchemata() {
         return schemata;
@@ -101,10 +158,66 @@
     /**
      * Get the variables that are to be substituted into the {@link BindVariableName} used in the query.
      * 
-     * @return immutable map of variable values keyed by their name; never null
+     * @return immutable map of variable values keyed by their name; never null but possibly empty
      */
     public Map<String, Object> getVariables() {
         return variables;
     }
 
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied execution context.
+     * 
+     * @param context the execution context that should be used in the new query context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the execution context reference is null
+     */
+    public QueryContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied schemata.
+     * 
+     * @param schemata the schemata that should be used in the new context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the schemata reference is null
+     */
+    public QueryContext with( Schemata schemata ) {
+        CheckArg.isNotNull(schemata, "schemata");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied hints.
+     * 
+     * @param hints the hints that should be used in the new context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the hints reference is null
+     */
+    public QueryContext with( PlanHints hints ) {
+        CheckArg.isNotNull(hints, "hints");
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied problem container.
+     * 
+     * @param problems the problems that should be used in the new context; may be null if a new problem container should be used
+     * @return the new context; never null
+     */
+    public QueryContext with( Problems problems ) {
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied variables.
+     * 
+     * @param variables the variables that should be used in the new context; may be null if there are no such variables
+     * @return the new context; never null
+     */
+    public QueryContext with( Map<String, Object> variables ) {
+        return new QueryContext(context, schemata, hints, problems, variables);
+    }
+
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -27,18 +27,15 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.ThreadSafe;
 import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Constraint;
 import org.jboss.dna.graph.query.model.FullTextSearch;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.model.Visitors;
 import org.jboss.dna.graph.query.optimize.Optimizer;
 import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.Planner;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
@@ -54,17 +51,6 @@
 @ThreadSafe
 public class QueryEngine implements Queryable {
 
-    /**
-     * A {@link Schemata} implementation that always returns null, meaning the table does not exist.
-     */
-    private static final Schemata DEFAULT_SCHEMATA = new Schemata() {
-        public Table getTable( SelectorName name ) {
-            // This won't allow the query engine to do anything (or much of anything),
-            // but it is legal and will result in meaningful problems
-            return null;
-        }
-    };
-
     private final Planner planner;
     private final Optimizer optimizer;
     private final Processor processor;
@@ -92,42 +78,25 @@
     /**
      * {@inheritDoc}
      * 
-     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata)
+     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand)
      */
-    public QueryResults execute( ExecutionContext context,
-                                 QueryCommand query,
-                                 Schemata schemata ) {
-        return execute(context, query, schemata, new PlanHints());
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.Queryable#execute(org.jboss.dna.graph.ExecutionContext,
-     *      org.jboss.dna.graph.query.model.QueryCommand, org.jboss.dna.graph.query.validate.Schemata,
-     *      org.jboss.dna.graph.query.plan.PlanHints)
-     */
-    public QueryResults execute( ExecutionContext context,
-                                 QueryCommand query,
-                                 Schemata schemata,
-                                 PlanHints hints ) {
+    public QueryResults execute( QueryContext context,
+                                 QueryCommand query ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(query, "query");
-        if (schemata == null) schemata = DEFAULT_SCHEMATA;
-        QueryContext queryContext = new QueryContext(context, hints, schemata);
 
         // Create the canonical plan ...
         long start = System.nanoTime();
-        PlanNode plan = planner.createPlan(queryContext, query);
+        PlanNode plan = planner.createPlan(context, query);
         long duration = System.nanoTime() - start;
         Statistics stats = new Statistics(duration);
 
         QueryResultColumns resultColumns = QueryResultColumns.empty();
-        if (!queryContext.getProblems().hasErrors()) {
+        if (!context.getProblems().hasErrors()) {
             // Optimize the plan ...
             start = System.nanoTime();
-            PlanNode optimizedPlan = optimizer.optimize(queryContext, plan);
+            PlanNode optimizedPlan = optimizer.optimize(context, plan);
             duration = System.nanoTime() - start;
             stats = stats.withOptimizationTime(duration);
 
@@ -137,11 +106,11 @@
             duration = System.nanoTime() - start;
             stats = stats.withOptimizationTime(duration);
 
-            if (!queryContext.getProblems().hasErrors()) {
+            if (!context.getProblems().hasErrors()) {
                 // Execute the plan ...
                 try {
                     start = System.nanoTime();
-                    return processor.execute(queryContext, query, stats, optimizedPlan);
+                    return processor.execute(context, query, stats, optimizedPlan);
                 } finally {
                     duration = System.nanoTime() - start;
                     stats = stats.withOptimizationTime(duration);
@@ -149,7 +118,7 @@
             }
         }
         // There were problems somewhere ...
-        return new org.jboss.dna.graph.query.process.QueryResults(queryContext, query, resultColumns, stats);
+        return new org.jboss.dna.graph.query.process.QueryResults(context, query, resultColumns, stats);
     }
 
     protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan ) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/Queryable.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -23,10 +23,7 @@
  */
 package org.jboss.dna.graph.query;
 
-import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.validate.Schemata;
 
 /**
  * An interface defining the ability to submit a query and obtain results.
@@ -38,26 +35,9 @@
      * 
      * @param context the context in which the query should be executed
      * @param query the query that is to be executed
-     * @param schemata the schemata that should be used to validate the query
      * @return the query results; never null
      * @throws IllegalArgumentException if the context or query references are null
      */
-    QueryResults execute( ExecutionContext context,
-                          QueryCommand query,
-                          Schemata schemata );
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param context the context in which the query should be executed
-     * @param query the query that is to be executed
-     * @param schemata the schemata that should be used to validate the query
-     * @param hints the hints for the execution; may be null if there are no hints
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     */
-    QueryResults execute( ExecutionContext context,
-                          QueryCommand query,
-                          Schemata schemata,
-                          PlanHints hints );
+    QueryResults execute( QueryContext context,
+                          QueryCommand query );
 }

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,120 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.process;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.AllNodes;
+import org.jboss.dna.graph.query.model.And;
+import org.jboss.dna.graph.query.model.Column;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.Limit;
+import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * A reusable base class for {@link ProcessingComponent} implementations that does everything except obtain the correct
+ * {@link Location} objects for the query results.
+ */
+public abstract class AbstractAccessComponent extends ProcessingComponent {
+
+    protected final PlanNode accessNode;
+    protected final SelectorName sourceName;
+    protected final List<Column> projectedColumns;
+    protected final Constraint constraint;
+    protected final Limit limit;
+
+    protected AbstractAccessComponent( QueryContext context,
+                                       Columns columns,
+                                       PlanNode accessNode ) {
+        super(context, columns);
+        this.accessNode = accessNode;
+
+        // Find the table name; should be
+        PlanNode source = accessNode.findAtOrBelow(Type.SOURCE);
+        if (source != null) {
+            this.sourceName = source.getProperty(Property.SOURCE_NAME, SelectorName.class);
+            if (!AllNodes.ALL_NODES_NAME.equals(this.sourceName)) {
+                throw new IllegalArgumentException();
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        // Find the project ...
+        PlanNode project = accessNode.findAtOrBelow(Type.PROJECT);
+        if (project != null) {
+            List<Column> projectedColumns = project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+            if (projectedColumns != null) {
+                this.projectedColumns = projectedColumns;
+            } else {
+                // Get the columns from the source columns ...
+                List<Schemata.Column> schemataColumns = source.getPropertyAsList(Property.SOURCE_COLUMNS, Schemata.Column.class);
+                this.projectedColumns = new ArrayList<Column>(schemataColumns.size());
+                NameFactory nameFactory = context.getExecutionContext().getValueFactories().getNameFactory();
+                for (Schemata.Column schemataColumn : schemataColumns) {
+                    String columnName = schemataColumn.getName();
+                    // PropertyType type = schemataColumn.getPropertyType();
+                    Name propertyName = nameFactory.create(columnName);
+                    Column column = new Column(sourceName, propertyName, columnName);
+                    this.projectedColumns.add(column);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        // Add the criteria ...
+        Constraint constraint = null;
+        for (PlanNode select : accessNode.findAllAtOrBelow(Type.SELECT)) {
+            Constraint selectConstraint = select.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+            if (constraint != null) {
+                constraint = new And(constraint, selectConstraint);
+            } else {
+                constraint = selectConstraint;
+            }
+        }
+        this.constraint = constraint;
+
+        // Find the limit ...
+        Limit limit = Limit.NONE;
+        PlanNode limitNode = accessNode.findAtOrBelow(Type.LIMIT);
+        if (limitNode != null) {
+            Integer count = limitNode.getProperty(Property.LIMIT_COUNT, Integer.class);
+            if (count != null) limit = limit.withRowLimit(count.intValue());
+            Integer offset = limitNode.getProperty(Property.LIMIT_OFFSET, Integer.class);
+            if (offset != null) limit = limit.withOffset(offset.intValue());
+        }
+        this.limit = limit;
+    }
+
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/AbstractAccessComponent.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/ValueCache.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,44 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.graph.query.process;
-
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.query.QueryResults;
-
-/**
- * Interface representing a cache of property values used in a {@link QueryResults}.
- */
-public interface ValueCache {
-
-    /**
-     * Get the value of the named property on the node at the supplied location.
-     * 
-     * @param location the location of the node; may not be null
-     * @param name the property name
-     * @return the value of the property, or null if there is no such value
-     */
-    Object getProperty( Location location,
-                        Name name );
-}

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -45,7 +45,6 @@
 import org.jboss.dna.graph.query.parse.InvalidQueryException;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -394,7 +393,7 @@
                 for (SelectorName name : viewNames) {
                     QueryCommand command = definitions.get(name);
                     // Create the canonical plan for the definition ...
-                    QueryContext queryContext = new QueryContext(context, new PlanHints(), schemata);
+                    QueryContext queryContext = new QueryContext(context, schemata);
                     CanonicalPlanner planner = new CanonicalPlanner();
                     PlanNode plan = planner.createPlan(queryContext, command);
                     if (queryContext.getProblems().hasErrors()) continue;

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/AddAccessNodesTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.optimize.AddAccessNodes;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
 import org.jboss.dna.graph.query.validate.Schemata;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = AddAccessNodes.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ChooseJoinAlgorithmTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -35,10 +35,7 @@
 import org.jboss.dna.graph.query.model.DescendantNodeJoinCondition;
 import org.jboss.dna.graph.query.model.JoinCondition;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.ChooseJoinAlgorithm;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
 import org.jboss.dna.graph.query.plan.JoinAlgorithm;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -57,7 +54,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         bestRule = ChooseJoinAlgorithm.USE_BEST_JOIN_ALGORITHM;
         nestedRule = ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM;
     }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/PushSelectCriteriaTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,9 +32,6 @@
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.PushSelectCriteria;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -52,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = PushSelectCriteria.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -38,7 +38,6 @@
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.SelectorName;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -67,7 +66,7 @@
         builder.addView("v1", "SELECT c11, c12 FROM t1 WHERE c13 < CAST('3' AS LONG)");
         builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
         schemata = builder.build();
-        context = new QueryContext(execContext, new PlanHints(), schemata);
+        context = new QueryContext(execContext, schemata);
     }
 
     /**

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RightOuterToLeftOuterJoinsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,9 +32,6 @@
 import org.jboss.dna.graph.query.AbstractQueryTest;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.model.JoinType;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RightOuterToLeftOuterJoins;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanNode.Property;
 import org.jboss.dna.graph.query.plan.PlanNode.Type;
@@ -52,7 +49,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         rule = RightOuterToLeftOuterJoins.INSTANCE;
     }
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -86,7 +86,7 @@
         builder.addView("type2",
                         "SELECT all.a3, all.a4 FROM all WHERE all.primaryType IN ('t2','t0') AND all.mixins IN ('t4','t5')");
         Schemata schemata = builder.build();
-        context = new QueryContext(execContext, new PlanHints(), schemata);
+        context = new QueryContext(execContext, schemata);
 
         node = new PlanNode(Type.ACCESS);
 

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/plan/CanonicalPlannerTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -112,7 +112,7 @@
     public void shouldProducePlanForSelectStarFromTable() {
         schemata = schemataBuilder.addTable("__ALLNODES__", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.isEmpty(), is(true));
         assertProjectNode(plan, "column1", "column2", "column3");
@@ -128,7 +128,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().fromAllNodes().query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -137,7 +137,7 @@
     public void shouldProduceErrorWhenSelectingNonExistantColumnOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.select("column1", "column4").from("someTable").query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         assertThat(problems.hasErrors(), is(true));
     }
@@ -146,7 +146,7 @@
     public void shouldProducePlanWhenSelectingAllColumnsOnExistingTable() {
         schemata = schemataBuilder.addTable("someTable", "column1", "column2", "column3").build();
         query = builder.selectStar().from("someTable").query();
-        queryContext = new QueryContext(context, hints, schemata, problems);
+        queryContext = new QueryContext(context, schemata, hints, problems);
         plan = planner.createPlan(queryContext, query);
         System.out.println(plan);
         assertThat(problems.hasErrors(), is(false));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/DistinctComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.DistinctComponent;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/QueryResultColumnsTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -37,9 +37,6 @@
 import org.jboss.dna.graph.query.QueryResults.Statistics;
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.QueryResultColumns;
-import org.jboss.dna.graph.query.process.QueryResults;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -66,7 +63,7 @@
     @Before
     public void beforeEach() {
         MockitoAnnotations.initMocks(this);
-        context = new QueryContext(executionContext, new PlanHints(), schemata);
+        context = new QueryContext(executionContext, schemata);
         columnList = new ArrayList<Column>();
         columnList.add(new Column(selector("table1"), name("colA"), "colA"));
         columnList.add(new Column(selector("table1"), name("colB"), "colB"));

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortLocationsComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,9 +31,6 @@
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.SortLocationsComponent;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +47,7 @@
 
     @Before
     public void beforeEach() {
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), mock(Schemata.class));
+        context = new QueryContext(new ExecutionContext(), mock(Schemata.class));
         inputTuples = new ArrayList<Object[]>();
         // Define the columns for the results ...
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/process/SortValuesComponentTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -32,7 +32,6 @@
 import org.jboss.dna.graph.query.QueryContext;
 import org.jboss.dna.graph.query.QueryResults.Columns;
 import org.jboss.dna.graph.query.model.Ordering;
-import org.jboss.dna.graph.query.plan.PlanHints;
 import org.jboss.dna.graph.query.validate.Schemata;
 import org.junit.Before;
 import org.junit.Test;
@@ -56,7 +55,7 @@
         columns = resultColumns("Selector1", "ColA", "ColB", "ColC");
         schemata = schemataFor(columns, PropertyType.STRING, PropertyType.LONG, PropertyType.STRING);
         // Define the context ...
-        context = new QueryContext(new ExecutionContext(), new PlanHints(), schemata);
+        context = new QueryContext(new ExecutionContext(), schemata);
         inputTuples = new ArrayList<Object[]>();
         // And define the delegating component ...
         delegate = new ProcessingComponent(context, columns) {

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,399 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.search;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.util.Version;
+import org.jboss.dna.common.text.NoOpEncoder;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.OptimizerRule;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.search.IndexRules.Rule;
+
+/**
+ * A simple {@link IndexStrategy} implementation that relies upon two separate indexes: one for the node content and a second
+ * one for paths and UUIDs.
+ */
+ at ThreadSafe
+abstract class DualIndexStrategy implements IndexStrategy {
+
+    static class PathIndex {
+        public static final String PATH = "path";
+        public static final String UUID = "uuid";
+    }
+
+    static class ContentIndex {
+        public static final String UUID = PathIndex.UUID;
+        public static final String FULL_TEXT = "fts";
+    }
+
+    /**
+     * The number of results that should be returned when performing queries while deleting entire branches of content. The
+     * current value is {@value} .
+     */
+    protected static final int SIZE_OF_DELETE_BATCHES = 1000;
+
+    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+        }
+    };
+
+    /**
+     * Obtain an immutable {@link FieldSelector} instance that accesses the UUID field.
+     */
+    protected static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
+        private static final long serialVersionUID = 1L;
+
+        public FieldSelectorResult accept( String fieldName ) {
+            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
+        }
+    };
+
+    private final IndexRules rules;
+    private final Logger logger;
+    private final QueryEngine queryEngine;
+
+    /**
+     * Create a new indexing strategy instance.
+     * 
+     * @param rules the indexing rules that govern how properties are to be index; may not be null
+     */
+    protected DualIndexStrategy( IndexRules rules ) {
+        assert rules != null;
+        this.rules = rules;
+        this.logger = Logger.getLogger(getClass());
+        // Create the query engine ...
+        Planner planner = new CanonicalPlanner();
+        Optimizer optimizer = new RuleBasedOptimizer() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
+             *      org.jboss.dna.graph.query.plan.PlanHints)
+             */
+            @Override
+            protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
+                                              PlanHints hints ) {
+                super.populateRuleStack(ruleStack, hints);
+                // Add any custom rules here, either at the front of the stack or at the end
+            }
+        };
+        QueryProcessor processor = new QueryProcessor() {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
+             *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
+             *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+             */
+            @Override
+            protected ProcessingComponent createAccessComponent( QueryContext context,
+                                                                 PlanNode accessNode,
+                                                                 Columns resultColumns,
+                                                                 org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer ) {
+                return DualIndexStrategy.this.createAccessComponent((SearchContext)context, accessNode, resultColumns, analyzer);
+            }
+        };
+
+        this.queryEngine = new QueryEngine(planner, optimizer, processor);
+    }
+
+    protected abstract ProcessingComponent createAccessComponent( SearchContext context,
+                                                                  PlanNode accessNode,
+                                                                  Columns resultColumns,
+                                                                  org.jboss.dna.graph.query.process.SelectComponent.Analyzer analyzer );
+
+    /**
+     * Utility method to obtain a {@link DateFormat} instance that can be used safely within a single thread.
+     * 
+     * @return the date formatter; never null
+     */
+    protected final DateFormat dateFormatter() {
+        return dateFormatter.get();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#getNamespaceEncoder()
+     */
+    public TextEncoder getNamespaceEncoder() {
+        return new NoOpEncoder();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#getChangeCountForAutomaticOptimization()
+     */
+    public int getChangeCountForAutomaticOptimization() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#createAnalyzer()
+     */
+    public Analyzer createAnalyzer() {
+        return new StandardAnalyzer(Version.LUCENE_CURRENT);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
+     * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
+     * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
+     * documents in the content index that represent those same nodes.
+     * </p>
+     * <p>
+     * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
+     * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
+     * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
+     * query.
+     * </p>
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#deleteBelow(Path, IndexContext)
+     */
+    public int deleteBelow( Path path,
+                            IndexContext indexes ) throws IOException {
+        // Perform a query using the reader to find those nodes at/below the path ...
+        try {
+            IndexReader pathReader = indexes.getPathsReader();
+            IndexSearcher pathSearcher = new IndexSearcher(pathReader);
+            String pathStr = indexes.stringFactory().create(path) + "/";
+            PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
+            int numberDeleted = 0;
+            while (true) {
+                // Execute the query and get the results ...
+                TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
+                int numResultsInBatch = results.scoreDocs.length;
+                // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
+                IndexReader contentReader = indexes.getContentReader();
+                for (ScoreDoc result : results.scoreDocs) {
+                    int docId = result.doc;
+                    // Find the UUID of the node ...
+                    Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
+                    String uuid = doc.get(PathIndex.UUID);
+                    // Delete the document from the paths index ...
+                    pathReader.deleteDocument(docId);
+                    // Delete the corresponding document from the content index ...
+                    contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
+                }
+                numberDeleted += numResultsInBatch;
+                if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
+            }
+            indexes.commit();
+            return numberDeleted;
+        } catch (FileNotFoundException e) {
+            // There are no index files yet, so nothing to delete ...
+            return 0;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#index(Node, IndexContext)
+     */
+    public void index( Node node,
+                       IndexContext indexes ) throws IOException {
+        ValueFactory<String> strings = indexes.stringFactory();
+        Location location = node.getLocation();
+        UUID uuid = location.getUuid();
+        if (uuid == null) uuid = UUID.randomUUID();
+        Path path = location.getPath();
+        String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
+        String uuidStr = uuid.toString();
+
+        if (logger.isTraceEnabled()) {
+            logger.trace("indexing {0}", pathStr);
+        }
+
+        // Create a separate document for the path, which makes it easier to handle moves since the path can
+        // be changed without changing any other content fields ...
+        Document doc = new Document();
+        doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        indexes.getPathsWriter().addDocument(doc);
+
+        // Create the document for the content (properties) ...
+        doc = new Document();
+        doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
+        String stringValue = null;
+        StringBuilder fullTextSearchValue = null;
+        for (Property property : node.getProperties()) {
+            Name name = property.getName();
+            Rule rule = rules.getRule(name);
+            if (rule.isSkipped()) continue;
+            String nameString = strings.create(name);
+            if (rule.isDate()) {
+                DateTimeFactory dateFactory = indexes.dateFactory();
+                for (Object value : property) {
+                    if (value == null) continue;
+                    DateTime dateValue = dateFactory.create(value);
+                    stringValue = dateFormatter().format(dateValue.toDate());
+                    // Add a separate field for each property value ...
+                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+                    // Dates are not added to the full-text search field (since this wouldn't make sense)
+                }
+                continue;
+            }
+            for (Object value : property) {
+                if (value == null) continue;
+                if (value instanceof Binary) {
+                    // don't include binary values as individual fields but do include them in the full-text search ...
+                    // TODO : add to full-text search ...
+                    continue;
+                }
+                stringValue = strings.create(value);
+                // Add a separate field for each property value ...
+                doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
+                // And add to the full-text field ...
+                if (rule.isFullText()) {
+                    if (fullTextSearchValue == null) {
+                        fullTextSearchValue = new StringBuilder();
+                    } else {
+                        fullTextSearchValue.append(' ');
+                    }
+                    fullTextSearchValue.append(stringValue);
+                }
+            }
+        }
+        // Add the full-text-search field ...
+        if (fullTextSearchValue != null) {
+            doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
+        }
+        indexes.getContentWriter().addDocument(doc);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#search(String, int, int, IndexContext, List)
+     */
+    public void search( String fullTextString,
+                        int maxResults,
+                        int offset,
+                        IndexContext indexes,
+                        List<Location> results ) throws IOException, ParseException {
+        assert fullTextString != null;
+        assert fullTextString.length() > 0;
+        assert offset >= 0;
+        assert maxResults > 0;
+        assert indexes != null;
+        assert results != null;
+
+        // Parse the full-text search and search against the 'fts' field ...
+        QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
+        Query query = parser.parse(fullTextString);
+        TopDocs docs = indexes.getContentSearcher().search(query, maxResults + offset);
+
+        // Collect the results ...
+        IndexReader contentReader = indexes.getContentReader();
+        IndexReader pathReader = indexes.getPathsReader();
+        IndexSearcher pathSearcher = indexes.getPathsSearcher();
+        ScoreDoc[] scoreDocs = docs.scoreDocs;
+        int numberOfResults = scoreDocs.length;
+        if (numberOfResults > offset) {
+            // There are enough results to satisfy the offset ...
+            for (int i = offset, num = scoreDocs.length; i != num; ++i) {
+                ScoreDoc result = scoreDocs[i];
+                int docId = result.doc;
+                // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
+                Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
+                String uuid = doc.get(ContentIndex.UUID);
+                // Find the path for this node (is there a better way to do this than one search per UUID?) ...
+                TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
+                if (pathDocs.scoreDocs.length < 1) {
+                    // No path record found ...
+                    continue;
+                }
+                Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
+                Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
+                // Now add the location ...
+                results.add(Location.create(path, UUID.fromString(uuid)));
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#query(org.jboss.dna.search.SearchContext,
+     *      org.jboss.dna.graph.query.model.QueryCommand)
+     */
+    public QueryResults query( SearchContext context,
+                               QueryCommand query ) {
+        return this.queryEngine.execute(context, query);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/DualIndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -31,16 +31,17 @@
 import org.apache.lucene.index.IndexWriter.MaxFieldLength;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.store.Directory;
+import org.jboss.dna.common.util.CheckArg;
 import org.jboss.dna.graph.ExecutionContext;
 import org.jboss.dna.graph.property.DateTimeFactory;
 import org.jboss.dna.graph.property.PathFactory;
 import org.jboss.dna.graph.property.ValueFactory;
 
 /**
- * A set of index readers and writers.
+ * A context for working with the index readers and writers.
  */
 @NotThreadSafe
-public final class IndexContext {
+final class IndexContext {
 
     private final ExecutionContext context;
     private final Directory pathsIndexDirectory;
@@ -278,4 +279,16 @@
         if (runtimeError != null) throw runtimeError;
     }
 
+    /**
+     * Create a copy of this index context, except that it uses the supplied execution context.
+     * 
+     * @param context the new execution context that should be used in the copy
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the context is null
+     */
+    public IndexContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new IndexContext(context, pathsIndexDirectory, contentIndexDirectory, analyzer, overwrite, readOnly);
+    }
+
 }

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,628 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.search;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * The set of rules that dictate how properties should be indexed.
+ */
+ at Immutable
+class IndexRules {
+
+    public static final int INDEX = 2 << 0;
+    public static final int ANALYZE = 2 << 1;
+    public static final int STORE = 2 << 2;
+    public static final int STORE_COMPRESSED = 2 << 3;
+    public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
+    public static final int FULL_TEXT = 2 << 5;
+    public static final int TREAT_AS_DATE = 2 << 6;
+
+    /**
+     * A single rule that dictates how a single property should be indexed.
+     * 
+     * @see IndexRules#getRule(Name)
+     */
+    @Immutable
+    public static interface Rule {
+        boolean isIncluded();
+
+        boolean isSkipped();
+
+        boolean isAnalyzed();
+
+        boolean isAnalyzedWithoutNorms();
+
+        boolean isStored();
+
+        boolean isStoredCompressed();
+
+        boolean isFullText();
+
+        boolean isDate();
+
+        int getMask();
+
+        Field.Store getStoreOption();
+
+        Field.Index getIndexOption();
+    }
+
+    public static final Rule SKIP = new SkipRule();
+
+    @Immutable
+    protected static class SkipRule implements Rule {
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
+         */
+        public int getMask() {
+            return 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         */
+        public boolean isAnalyzed() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         */
+        public boolean isAnalyzedWithoutNorms() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
+         */
+        public boolean isFullText() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
+         */
+        public boolean isIncluded() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
+         */
+        public boolean isSkipped() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
+         */
+        public boolean isStored() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
+         */
+        public boolean isStoredCompressed() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
+         */
+        public boolean isDate() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
+         */
+        public Index getIndexOption() {
+            return Field.Index.NO;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
+         */
+        public Store getStoreOption() {
+            return Field.Store.NO;
+        }
+    }
+
+    @Immutable
+    public static final class GeneralRule implements Rule {
+        private final int value;
+        private final Field.Store store;
+        private final Field.Index index;
+
+        protected GeneralRule( int value ) {
+            this.value = value;
+            this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
+            this.store = isStored() ? Field.Store.YES : Field.Store.NO;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getMask()
+         */
+        public int getMask() {
+            return value;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzed()
+         */
+        public boolean isAnalyzed() {
+            return (value & ANALYZE) == ANALYZE;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isAnalyzedWithoutNorms()
+         */
+        public boolean isAnalyzedWithoutNorms() {
+            return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isFullText()
+         */
+        public boolean isFullText() {
+            return (value & FULL_TEXT) == FULL_TEXT;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isIncluded()
+         */
+        public boolean isIncluded() {
+            return true;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isSkipped()
+         */
+        public boolean isSkipped() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStored()
+         */
+        public boolean isStored() {
+            return (value & STORE) == STORE;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isStoredCompressed()
+         */
+        public boolean isStoredCompressed() {
+            return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#isDate()
+         */
+        public boolean isDate() {
+            return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
+        }
+
+        protected Rule with( int options ) {
+            return createRule(value | options);
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getIndexOption()
+         */
+        public Index getIndexOption() {
+            return index;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see org.jboss.dna.search.IndexRules.Rule#getStoreOption()
+         */
+        public Store getStoreOption() {
+            return store;
+        }
+    }
+
+    private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
+
+    protected static Rule createRule( int value ) {
+        if (value <= 0) {
+            return SKIP;
+        }
+        Integer key = new Integer(value);
+        Rule rule = CACHE.get(key);
+        if (rule == null) {
+            Rule newRule = new GeneralRule(value);
+            rule = CACHE.putIfAbsent(value, newRule);
+            if (rule == null) rule = newRule;
+        }
+        return rule;
+    }
+
+    private final Map<Name, Rule> rulesByName;
+    private final Rule defaultRule;
+
+    protected IndexRules( Map<Name, Rule> rulesByName,
+                          Rule defaultRule ) {
+        this.rulesByName = rulesByName;
+        this.defaultRule = defaultRule != null ? defaultRule : SKIP;
+        assert this.defaultRule != null;
+    }
+
+    /**
+     * Get the rule associated with the given property name.
+     * 
+     * @param name the property name, or null if the default rule is to be returned
+     * @return the rule; never null
+     */
+    public Rule getRule( Name name ) {
+        Rule result = rulesByName.get(name);
+        return result != null ? result : this.defaultRule;
+    }
+
+    /**
+     * Return a new builder that can be used to create {@link IndexRules} objects.
+     * 
+     * @return a builder; never null
+     */
+    public static Builder createBuilder() {
+        return new Builder(new HashMap<Name, Rule>());
+    }
+
+    /**
+     * Return a new builder that can be used to create {@link IndexRules} objects.
+     * 
+     * @param initialRules the rules that the builder should start with
+     * @return a builder; never null
+     * @throws IllegalArgumentException if the initial rules reference is null
+     */
+    public static Builder createBuilder( IndexRules initialRules ) {
+        CheckArg.isNotNull(initialRules, "initialRules");
+        return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
+    }
+
+    /**
+     * A builder of immutable {@link IndexRules} objects.
+     */
+    @NotThreadSafe
+    public static class Builder {
+        private final Map<Name, Rule> rulesByName;
+        private Rule defaultRule;
+
+        Builder( Map<Name, Rule> rulesByName ) {
+            assert rulesByName != null;
+            this.rulesByName = rulesByName;
+        }
+
+        /**
+         * Set the default rules.
+         * 
+         * @param rule the default rule to use
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder defaultTo( Rule rule ) {
+            CheckArg.isNotNull(rule, "rule");
+            defaultRule = rule;
+            return this;
+        }
+
+        /**
+         * Set the default rules.
+         * 
+         * @param ruleMask the bitmask of rule to use
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder defaultTo( int ruleMask ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (ruleMask == 0) {
+                defaultRule = SKIP;
+            } else {
+                // Make sure the index flag is set ...
+                ruleMask |= INDEX;
+                defaultRule = createRule(ruleMask);
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be skipped from indexing.
+         * 
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder skip( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    rulesByName.put(name, SKIP);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Set the properties with the supplied names to use the supplied rules.
+         * 
+         * @param ruleMask the bitmask of rules to use
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder set( int ruleMask,
+                            Name... namesToIndex ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (namesToIndex != null) {
+                if (ruleMask > 0) {
+                    skip(namesToIndex);
+                } else {
+                    // Make sure the index flag is set ...
+                    ruleMask |= INDEX;
+                    Rule rule = createRule(ruleMask);
+                    for (Name name : namesToIndex) {
+                        rulesByName.put(name, rule);
+                    }
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
+         * these properties.
+         * 
+         * @param ruleMask the bitmask of rules to add
+         * @param namesToIndex the names of the properties that are to be skipped
+         * @return this builder for convenience and method chaining; never null
+         * @throws IllegalArgumentException if the rule mask is negative
+         */
+        public Builder add( int ruleMask,
+                            Name... namesToIndex ) {
+            CheckArg.isNonNegative(ruleMask, "options");
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, ruleMask);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder index( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
+         * rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be analyzed
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyze( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, ANALYZE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
+         * for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be stored
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder store( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, STORE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder fullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, FULL_TEXT | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
+         * other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder treatAsDates( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, TREAT_AS_DATE | INDEX);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
+         * for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndStore( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | STORE);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
+         * does not remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
+         *        searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder analyzeAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | ANALYZE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
+         * remove any other rules for these properties.
+         * 
+         * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
+         * @return this builder for convenience and method chaining; never null
+         */
+        public Builder storeAndFullText( Name... namesToIndex ) {
+            if (namesToIndex != null) {
+                for (Name name : namesToIndex) {
+                    add(name, INDEX | STORE | FULL_TEXT);
+                }
+            }
+            return this;
+        }
+
+        protected void add( Name name,
+                            int option ) {
+            Rule rule = rulesByName.get(name);
+            if (rule != null) {
+                option |= rule.getMask();
+            }
+            rulesByName.put(name, createRule(option));
+        }
+
+        /**
+         * Build the indexing rules.
+         * 
+         * @return the immutable indexing rules.
+         */
+        public IndexRules build() {
+            return new IndexRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
+        }
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexRules.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Copied: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java (from rev 1318, trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java)
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,133 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.search;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.queryParser.ParseException;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * Interface defining the behaviors associated with indexing graph content.
+ */
+interface IndexStrategy {
+
+    /**
+     * Get the number of changes that are allowed before optimization is automatically run.
+     * 
+     * @return a positive number denoting the minimum number of changes between automatic optimization operations, or a
+     *         non-positive number if automatic optimization should never be run
+     */
+    int getChangeCountForAutomaticOptimization();
+
+    /**
+     * Get the {@link TextEncoder} that should be used to encode the namespace URIs.
+     * 
+     * @return the encoder; may not be null
+     */
+    TextEncoder getNamespaceEncoder();
+
+    /**
+     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
+     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
+     * {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param node the node to be indexed; never null
+     * @param indexes the set of index readers and writers; never null
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    void index( Node node,
+                IndexContext indexes ) throws IOException;
+
+    /**
+     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
+     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
+     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param changes the set of changes to the content
+     * @param indexes the set of index readers and writers; never null
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int apply( Iterable<ChangeRequest> changes,
+               IndexContext indexes ) throws IOException;
+
+    /**
+     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
+     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
+     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
+     * 
+     * @param path the path identifying the graph content that is to be removed; never null
+     * @param indexes the set of index readers and writers; never null
+     * @return the (approximate) number of nodes that were affected by the changes
+     * @throws IOException if there is a problem indexing or using the writers
+     */
+    int deleteBelow( Path path,
+                     IndexContext indexes ) throws IOException;
+
+    /**
+     * Create the analyzer that is used for reading and updating the indexes.
+     * 
+     * @return the analyzer; may not be null
+     */
+    Analyzer createAnalyzer();
+
+    /**
+     * Perform a full-text search given the supplied query.
+     * 
+     * @param fullTextString the full-text query; never null or blank
+     * @param maxResults the maximum number of results that are to be returned; always positive
+     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
+     * @param indexes the set of index readers and writers; never null
+     * @param results the list where the results should be accumulated; never null
+     * @throws IOException if there is a problem indexing or using the writers
+     * @throws ParseException if there is a problem parsing the query
+     */
+    void search( String fullTextString,
+                 int maxResults,
+                 int offset,
+                 IndexContext indexes,
+                 List<Location> results ) throws IOException, ParseException;
+
+    /**
+     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
+     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
+     * writers) that should be used.
+     * 
+     * @param context the context in which the query should be executed; never null
+     * @param query the query; never null
+     * @return the results of the query
+     */
+    QueryResults query( SearchContext context,
+                        QueryCommand query );
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingRules.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,628 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.search;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import net.jcip.annotations.Immutable;
-import net.jcip.annotations.NotThreadSafe;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.Field.Index;
-import org.apache.lucene.document.Field.Store;
-import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.property.Name;
-
-/**
- * The set of rules that dictate how properties should be indexed.
- */
- at Immutable
-public class IndexingRules {
-
-    public static final int INDEX = 2 << 0;
-    public static final int ANALYZE = 2 << 1;
-    public static final int STORE = 2 << 2;
-    public static final int STORE_COMPRESSED = 2 << 3;
-    public static final int ANALYZED_WITHOUT_NORMS = 2 << 4;
-    public static final int FULL_TEXT = 2 << 5;
-    public static final int TREAT_AS_DATE = 2 << 6;
-
-    /**
-     * A single rule that dictates how a single property should be indexed.
-     * 
-     * @see IndexingRules#getRule(Name)
-     */
-    @Immutable
-    public static interface Rule {
-        boolean isIncluded();
-
-        boolean isSkipped();
-
-        boolean isAnalyzed();
-
-        boolean isAnalyzedWithoutNorms();
-
-        boolean isStored();
-
-        boolean isStoredCompressed();
-
-        boolean isFullText();
-
-        boolean isDate();
-
-        int getMask();
-
-        Field.Store getStoreOption();
-
-        Field.Index getIndexOption();
-    }
-
-    public static final Rule SKIP = new SkipRule();
-
-    @Immutable
-    protected static class SkipRule implements Rule {
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
-         */
-        public int getMask() {
-            return 0;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
-         */
-        public boolean isAnalyzed() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
-         */
-        public boolean isAnalyzedWithoutNorms() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
-         */
-        public boolean isFullText() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
-         */
-        public boolean isIncluded() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
-         */
-        public boolean isDate() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
-         */
-        public Index getIndexOption() {
-            return Field.Index.NO;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
-         */
-        public Store getStoreOption() {
-            return Field.Store.NO;
-        }
-    }
-
-    @Immutable
-    public static final class GeneralRule implements Rule {
-        private final int value;
-        private final Field.Store store;
-        private final Field.Index index;
-
-        protected GeneralRule( int value ) {
-            this.value = value;
-            this.index = isAnalyzed() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
-            this.store = isStored() ? Field.Store.YES : Field.Store.NO;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getMask()
-         */
-        public int getMask() {
-            return value;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzed()
-         */
-        public boolean isAnalyzed() {
-            return (value & ANALYZE) == ANALYZE;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isAnalyzedWithoutNorms()
-         */
-        public boolean isAnalyzedWithoutNorms() {
-            return (value & ANALYZED_WITHOUT_NORMS) == ANALYZED_WITHOUT_NORMS;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isFullText()
-         */
-        public boolean isFullText() {
-            return (value & FULL_TEXT) == FULL_TEXT;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isIncluded()
-         */
-        public boolean isIncluded() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isSkipped()
-         */
-        public boolean isSkipped() {
-            return false;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStored()
-         */
-        public boolean isStored() {
-            return (value & STORE) == STORE;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isStoredCompressed()
-         */
-        public boolean isStoredCompressed() {
-            return (value & STORE_COMPRESSED) == STORE_COMPRESSED;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#isDate()
-         */
-        public boolean isDate() {
-            return (value & TREAT_AS_DATE) == TREAT_AS_DATE;
-        }
-
-        protected Rule with( int options ) {
-            return createRule(value | options);
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getIndexOption()
-         */
-        public Index getIndexOption() {
-            return index;
-        }
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.search.IndexingRules.Rule#getStoreOption()
-         */
-        public Store getStoreOption() {
-            return store;
-        }
-    }
-
-    private static final ConcurrentHashMap<Integer, Rule> CACHE = new ConcurrentHashMap<Integer, Rule>();
-
-    protected static Rule createRule( int value ) {
-        if (value <= 0) {
-            return SKIP;
-        }
-        Integer key = new Integer(value);
-        Rule rule = CACHE.get(key);
-        if (rule == null) {
-            Rule newRule = new GeneralRule(value);
-            rule = CACHE.putIfAbsent(value, newRule);
-            if (rule == null) rule = newRule;
-        }
-        return rule;
-    }
-
-    private final Map<Name, Rule> rulesByName;
-    private final Rule defaultRule;
-
-    protected IndexingRules( Map<Name, Rule> rulesByName,
-                             Rule defaultRule ) {
-        this.rulesByName = rulesByName;
-        this.defaultRule = defaultRule != null ? defaultRule : SKIP;
-        assert this.defaultRule != null;
-    }
-
-    /**
-     * Get the rule associated with the given property name.
-     * 
-     * @param name the property name, or null if the default rule is to be returned
-     * @return the rule; never null
-     */
-    public Rule getRule( Name name ) {
-        Rule result = rulesByName.get(name);
-        return result != null ? result : this.defaultRule;
-    }
-
-    /**
-     * Return a new builder that can be used to create {@link IndexingRules} objects.
-     * 
-     * @return a builder; never null
-     */
-    public static Builder createBuilder() {
-        return new Builder(new HashMap<Name, Rule>());
-    }
-
-    /**
-     * Return a new builder that can be used to create {@link IndexingRules} objects.
-     * 
-     * @param initialRules the rules that the builder should start with
-     * @return a builder; never null
-     * @throws IllegalArgumentException if the initial rules reference is null
-     */
-    public static Builder createBuilder( IndexingRules initialRules ) {
-        CheckArg.isNotNull(initialRules, "initialRules");
-        return new Builder(initialRules.rulesByName).defaultTo(initialRules.defaultRule);
-    }
-
-    /**
-     * A builder of immutable {@link IndexingRules} objects.
-     */
-    @NotThreadSafe
-    public static class Builder {
-        private final Map<Name, Rule> rulesByName;
-        private Rule defaultRule;
-
-        Builder( Map<Name, Rule> rulesByName ) {
-            assert rulesByName != null;
-            this.rulesByName = rulesByName;
-        }
-
-        /**
-         * Set the default rules.
-         * 
-         * @param rule the default rule to use
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder defaultTo( Rule rule ) {
-            CheckArg.isNotNull(rule, "rule");
-            defaultRule = rule;
-            return this;
-        }
-
-        /**
-         * Set the default rules.
-         * 
-         * @param ruleMask the bitmask of rule to use
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder defaultTo( int ruleMask ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (ruleMask == 0) {
-                defaultRule = SKIP;
-            } else {
-                // Make sure the index flag is set ...
-                ruleMask |= INDEX;
-                defaultRule = createRule(ruleMask);
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be skipped from indexing.
-         * 
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder skip( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    rulesByName.put(name, SKIP);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Set the properties with the supplied names to use the supplied rules.
-         * 
-         * @param ruleMask the bitmask of rules to use
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder set( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                if (ruleMask > 0) {
-                    skip(namesToIndex);
-                } else {
-                    // Make sure the index flag is set ...
-                    ruleMask |= INDEX;
-                    Rule rule = createRule(ruleMask);
-                    for (Name name : namesToIndex) {
-                        rulesByName.put(name, rule);
-                    }
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to use the supplied rule mask. This does not remove any other rules for
-         * these properties.
-         * 
-         * @param ruleMask the bitmask of rules to add
-         * @param namesToIndex the names of the properties that are to be skipped
-         * @return this builder for convenience and method chaining; never null
-         * @throws IllegalArgumentException if the rule mask is negative
-         */
-        public Builder add( int ruleMask,
-                            Name... namesToIndex ) {
-            CheckArg.isNonNegative(ruleMask, "options");
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ruleMask);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed. This does not remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder index( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be analyzed (and obviously indexed). This does not remove any other
-         * rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be analyzed
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyze( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, ANALYZE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be stored (and obviously indexed). This does not remove any other rules
-         * for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be stored
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder store( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, STORE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be included in full-text searches (and obviously indexed). This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder fullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, FULL_TEXT | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be treated as dates (and obviously indexed). This does not remove any
-         * other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder treatAsDates( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, TREAT_AS_DATE | INDEX);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed and stored. This does not remove any other rules
-         * for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and stored
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndStore( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed, stored and included in full-text searches. This
-         * does not remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed, stored and included in full-text
-         *        searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndStoreAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | STORE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, analyzed and included in full-text searches. This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, analyzed and included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder analyzeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | ANALYZE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Mark the properties with the supplied names to be indexed, stored and included in full-text searches. This does not
-         * remove any other rules for these properties.
-         * 
-         * @param namesToIndex the names of the properties that are to be indexed, stored and included in full-text searches
-         * @return this builder for convenience and method chaining; never null
-         */
-        public Builder storeAndFullText( Name... namesToIndex ) {
-            if (namesToIndex != null) {
-                for (Name name : namesToIndex) {
-                    add(name, INDEX | STORE | FULL_TEXT);
-                }
-            }
-            return this;
-        }
-
-        protected void add( Name name,
-                            int option ) {
-            Rule rule = rulesByName.get(name);
-            if (rule != null) {
-                option |= rule.getMask();
-            }
-            rulesByName.put(name, createRule(option));
-        }
-
-        /**
-         * Build the indexing rules.
-         * 
-         * @return the immutable indexing rules.
-         */
-        public IndexingRules build() {
-            return new IndexingRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
-        }
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/IndexingStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,126 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.search;
-
-import java.io.IOException;
-import java.util.List;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-
-/**
- * Interface defining the behaviors associated with indexing graph content.
- */
-public interface IndexingStrategy {
-
-    int getChangeCountForAutomaticOptimization();
-
-    TextEncoder getNamespaceEncoder();
-
-    /**
-     * Index the node given the index writers. Note that implementors should simply just use the writers to add documents to the
-     * index(es), and should never call any of the writer lifecycle methods (e.g., {@link IndexWriter#commit()},
-     * {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param node the node to be indexed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    void index( Node node,
-                IndexContext indexes ) throws IOException;
-
-    /**
-     * Update the indexes to reflect the supplied changes to the graph content. Note that implementors should simply just use the
-     * writers to add documents to the index(es), and should never call any of the writer lifecycle methods (e.g.,
-     * {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param changes the set of changes to the content
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int apply( Iterable<ChangeRequest> changes,
-               IndexContext indexes ) throws IOException;
-
-    /**
-     * Remove from the index(es) all of the information pertaining to the nodes at or below the supplied path. Note that
-     * implementors should simply just use the writers to add documents to the index(es), and should never call any of the writer
-     * lifecycle methods (e.g., {@link IndexWriter#commit()}, {@link IndexWriter#rollback()}, etc.).
-     * 
-     * @param path the path identifying the graph content that is to be removed; never null
-     * @param indexes the set of index readers and writers; never null
-     * @return the (approximate) number of nodes that were affected by the changes
-     * @throws IOException if there is a problem indexing or using the writers
-     */
-    int deleteBelow( Path path,
-                     IndexContext indexes ) throws IOException;
-
-    /**
-     * Create the analyzer that is used for reading and updating the indexes.
-     * 
-     * @return the analyzer; may not be null
-     */
-    Analyzer createAnalyzer();
-
-    /**
-     * Perform a full-text search given the supplied query.
-     * 
-     * @param fullTextString the full-text query; never null or blank
-     * @param maxResults the maximum number of results that are to be returned; always positive
-     * @param offset the number of initial results to skip, or 0 if the first results are to be returned
-     * @param indexes the set of index readers and writers; never null
-     * @param results the list where the results should be accumulated; never null
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    void performQuery( String fullTextString,
-                       int maxResults,
-                       int offset,
-                       IndexContext indexes,
-                       List<Location> results ) throws IOException, ParseException;
-
-    /**
-     * Perform a query of the content. The {@link QueryCommand query} is supplied in the form of the Abstract Query Model, with
-     * the {@link Schemata} that defines the tables and views that are available to the query, and the set of index readers (and
-     * writers) that should be used.
-     * 
-     * @param query the query; never null
-     * @param schemata the definition of the tables used in the query; never null
-     * @param indexes the set of index readers and writers; never null
-     * @return the results of the query
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    QueryResults performQuery( QueryCommand query,
-                               Schemata schemata,
-                               IndexContext indexes ) throws IOException, ParseException;
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.search;
+
+import java.util.List;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.process.AbstractAccessComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * An {@link IndexStrategy} implementation that stores all content within a set of two indexes: one for the node content and a
+ * second one for paths and UUIDs.
+ */
+ at ThreadSafe
+class KitchenSinkIndexStrategy extends DualIndexStrategy {
+
+    /**
+     * The default set of {@link IndexRules} used by {@link KitchenSinkIndexStrategy} instances when no rules are provided.
+     */
+    public static final IndexRules DEFAULT_RULES;
+
+    static {
+        IndexRules.Builder builder = IndexRules.createBuilder();
+        // Configure the default behavior ...
+        builder.defaultTo(IndexRules.INDEX | IndexRules.ANALYZE);
+        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
+        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
+        // Configure the properties that we'll treat as dates ...
+        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
+        DEFAULT_RULES = builder.build();
+    }
+
+    /**
+     * Create a new indexing strategy instance uses the {@link #DEFAULT_RULES default indexing rules}.
+     */
+    public KitchenSinkIndexStrategy() {
+        this(null);
+    }
+
+    /**
+     * Create a new indexing strategy instance.
+     * 
+     * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
+     *        rules} are to be used
+     */
+    public KitchenSinkIndexStrategy( IndexRules rules ) {
+        super(rules != null ? rules : DEFAULT_RULES);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.DualIndexStrategy#createAccessComponent(org.jboss.dna.search.SearchContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
+     *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+     */
+    @Override
+    protected ProcessingComponent createAccessComponent( final SearchContext context,
+                                                         PlanNode accessNode,
+                                                         Columns resultColumns,
+                                                         Analyzer analyzer ) {
+        // Create a processing component for this access query ...
+        return new AbstractAccessComponent(context, resultColumns, accessNode) {
+            /**
+             * {@inheritDoc}
+             * 
+             * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+             */
+            @Override
+            public List<Object[]> execute() {
+                return null;
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.search.IndexStrategy#apply(Iterable, IndexContext)
+     */
+    public int apply( Iterable<ChangeRequest> changes,
+                      IndexContext indexes ) /*throws IOException*/{
+        for (ChangeRequest change : changes) {
+            if (change != null) continue;
+        }
+        return 0;
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/KitchenSinkIndexStrategy.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryComponent.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,57 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.search;
-
-import java.util.List;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-
-/**
- * A {@link ProcessingComponent} implementation that is used by the {@link LuceneQueryEngine.LuceneProcessor} to perform atomic
- * queries against the Lucene indexes.
- */
-class LuceneQueryComponent extends ProcessingComponent {
-
-    private final PlanNode accessNode;
-
-    LuceneQueryComponent( QueryContext context,
-                          Columns columns,
-                          PlanNode accessNode ) {
-        super(context, columns);
-        this.accessNode = accessNode;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
-     */
-    @Override
-    public List<Object[]> execute() {
-        if (accessNode != null) return null;
-        return null;
-    }
-}

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/LuceneQueryEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,114 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.search;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import org.apache.lucene.queryParser.ParseException;
-import org.jboss.dna.graph.query.QueryContext;
-import org.jboss.dna.graph.query.QueryEngine;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.QueryResults.Columns;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.optimize.Optimizer;
-import org.jboss.dna.graph.query.optimize.OptimizerRule;
-import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
-import org.jboss.dna.graph.query.plan.CanonicalPlanner;
-import org.jboss.dna.graph.query.plan.PlanHints;
-import org.jboss.dna.graph.query.plan.PlanNode;
-import org.jboss.dna.graph.query.process.ProcessingComponent;
-import org.jboss.dna.graph.query.process.QueryProcessor;
-import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
-import org.jboss.dna.graph.query.validate.Schemata;
-
-/**
- * 
- */
-class LuceneQueryEngine {
-
-    private QueryEngine engine;
-
-    public LuceneQueryEngine() {
-        engine = new QueryEngine(new CanonicalPlanner(), new LuceneOptimizer(), new LuceneProcessor());
-    }
-
-    /**
-     * Execute the supplied query by planning, optimizing, and then processing it.
-     * 
-     * @param query the query that is to be executed
-     * @param schemata the schemata that defines the tables used in the query
-     * @param indexes the indexes that should be used to execute the query; never null
-     * @return the query results; never null
-     * @throws IllegalArgumentException if the context or query references are null
-     * @throws IOException if there is a problem indexing or using the writers
-     * @throws ParseException if there is a problem parsing the query
-     */
-    public QueryResults execute( QueryCommand query,
-                                 Schemata schemata,
-                                 IndexContext indexes ) throws IOException, ParseException {
-        return engine.execute(indexes.context(), query, schemata, new PlanHints());
-    }
-
-    /**
-     * An {@link Optimizer} implementation that specializes the {@link RuleBasedOptimizer} by using custom rules.
-     */
-    protected static class LuceneOptimizer extends RuleBasedOptimizer {
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.graph.query.optimize.RuleBasedOptimizer#populateRuleStack(java.util.LinkedList,
-         *      org.jboss.dna.graph.query.plan.PlanHints)
-         */
-        @Override
-        protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
-                                          PlanHints hints ) {
-            super.populateRuleStack(ruleStack, hints);
-            // Add any custom rules here, either at the front of the stack or at the end
-        }
-    }
-
-    /**
-     * A query processor that operates against Lucene indexes. All functionality is inherited from the {@link QueryProcessor},
-     * except for the creation of the {@link ProcessingComponent} that does the low-level atomic queries (against the Lucene
-     * indexes).
-     */
-    protected static class LuceneProcessor extends QueryProcessor {
-
-        /**
-         * {@inheritDoc}
-         * 
-         * @see org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.QueryContext,
-         *      org.jboss.dna.graph.query.plan.PlanNode, org.jboss.dna.graph.query.QueryResults.Columns,
-         *      org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
-         */
-        @Override
-        protected ProcessingComponent createAccessComponent( QueryContext context,
-                                                             PlanNode accessNode,
-                                                             Columns resultColumns,
-                                                             Analyzer analyzer ) {
-            return new LuceneQueryComponent(context, resultColumns, accessNode);
-        }
-    }
-}

Added: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	                        (rev 0)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -0,0 +1,161 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.search;
+
+import java.util.Map;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.validate.Schemata;
+
+/**
+ * 
+ */
+class SearchContext extends QueryContext {
+
+    private final IndexContext indexes;
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param indexes the indexes that should be used
+     * @param schemata the definition of the tables available to this query
+     */
+    public SearchContext( IndexContext indexes,
+                          Schemata schemata ) {
+        super(indexes.context(), schemata);
+        this.indexes = indexes;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param queryContext
+     * @param indexes
+     */
+    public SearchContext( QueryContext queryContext,
+                          IndexContext indexes ) {
+        super(queryContext.getExecutionContext(), queryContext.getSchemata(), queryContext.getHints(),
+              queryContext.getProblems(), queryContext.getVariables());
+        this.indexes = indexes;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Create a new context for searching and querying.
+     * 
+     * @param context the execution context
+     * @param schemata the schemata
+     * @param hints the hints, or null if there are no hints
+     * @param problems the problems container, or null if a new problems container should be created
+     * @param variables the mapping of variables and values, or null if there are no such variables
+     * @throws IllegalArgumentException if the context or schmata are null
+     */
+    public SearchContext( IndexContext context,
+                          Schemata schemata,
+                          PlanHints hints,
+                          Problems problems,
+                          Map<String, Object> variables ) {
+        super(context.context(), schemata, hints, problems, variables);
+        this.indexes = context;
+        assert this.indexes != null;
+    }
+
+    /**
+     * Get the {@link IndexContext} for this query context.
+     * 
+     * @return the index context; never null
+     */
+    public IndexContext getIndexes() {
+        return indexes;
+    }
+
+    /**
+     * Obtain a copy of this context, except that the copy uses the supplied index context.
+     * 
+     * @param context the index context that should be used in the new query context
+     * @return the new context; never null
+     * @throws IllegalArgumentException if the index context reference is null
+     */
+    public SearchContext with( IndexContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new SearchContext(context, getSchemata(), getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.ExecutionContext)
+     */
+    @Override
+    public SearchContext with( ExecutionContext context ) {
+        CheckArg.isNotNull(context, "context");
+        return new SearchContext(indexes.with(context), getSchemata(), getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.validate.Schemata)
+     */
+    @Override
+    public SearchContext with( Schemata schemata ) {
+        CheckArg.isNotNull(schemata, "schemata");
+        return new SearchContext(indexes, schemata, getHints(), getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.graph.query.plan.PlanHints)
+     */
+    @Override
+    public SearchContext with( PlanHints hints ) {
+        CheckArg.isNotNull(hints, "hints");
+        return new SearchContext(indexes, getSchemata(), hints, getProblems(), getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(org.jboss.dna.common.collection.Problems)
+     */
+    @Override
+    public SearchContext with( Problems problems ) {
+        return new SearchContext(indexes, getSchemata(), getHints(), problems, getVariables());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.QueryContext#with(java.util.Map)
+     */
+    @Override
+    public SearchContext with( Map<String, Object> variables ) {
+        return new SearchContext(indexes, getSchemata(), getHints(), getProblems(), variables);
+    }
+}


Property changes on: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchContext.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/SearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -61,7 +61,7 @@
     private final String sourceName;
     private final RepositoryConnectionFactory connectionFactory;
     private final DirectoryConfiguration directoryFactory;
-    private final IndexingStrategy indexingStrategy;
+    private final IndexStrategy indexStrategy;
     private final PathFactory pathFactory;
     @GuardedBy( "workspaceEngineLock" )
     private final Map<String, WorkspaceSearchEngine> workspaceEnginesByName;
@@ -76,15 +76,12 @@
      * @param sourceName the name of the {@link RepositorySource}
      * @param connectionFactory the connection factory
      * @param directoryFactory the factory for Lucene {@link Directory directories}
-     * @param indexingStrategy the indexing strategy that governs how properties are to be indexed; or null if the default
-     *        strategy should be used
      * @throws IllegalArgumentException if any of the parameters (other than indexing strategy) are null
      */
     public SearchEngine( ExecutionContext context,
                          String sourceName,
                          RepositoryConnectionFactory connectionFactory,
-                         DirectoryConfiguration directoryFactory,
-                         IndexingStrategy indexingStrategy ) {
+                         DirectoryConfiguration directoryFactory ) {
         CheckArg.isNotNull(context, "context");
         CheckArg.isNotNull(sourceName, "sourceName");
         CheckArg.isNotNull(connectionFactory, "connectionFactory");
@@ -95,7 +92,7 @@
         this.context = context;
         this.pathFactory = context.getValueFactories().getPathFactory();
         this.workspaceEnginesByName = new HashMap<String, WorkspaceSearchEngine>();
-        this.indexingStrategy = indexingStrategy != null ? indexingStrategy : new StoreLittleIndexingStrategy();
+        this.indexStrategy = new KitchenSinkIndexStrategy();
     }
 
     /**
@@ -153,7 +150,7 @@
                 engine = workspaceEnginesByName.get(workspaceName);
                 if (engine == null) {
                     // Create the engine and register it ...
-                    engine = new WorkspaceSearchEngine(context, directoryFactory, indexingStrategy, sourceName, workspaceName,
+                    engine = new WorkspaceSearchEngine(context, directoryFactory, indexStrategy, sourceName, workspaceName,
                                                        connectionFactory);
                     workspaceEnginesByName.put(workspaceName, engine);
                 }

Deleted: trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/StoreLittleIndexingStrategy.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -1,372 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership.  Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of 
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- * 
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.search;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.List;
-import java.util.UUID;
-import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldSelector;
-import org.apache.lucene.document.FieldSelectorResult;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.PrefixQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.util.Version;
-import org.jboss.dna.common.text.NoOpEncoder;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.util.Logger;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.DateTime;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.graph.property.ValueFactory;
-import org.jboss.dna.graph.query.QueryResults;
-import org.jboss.dna.graph.query.model.QueryCommand;
-import org.jboss.dna.graph.query.validate.Schemata;
-import org.jboss.dna.graph.request.ChangeRequest;
-import org.jboss.dna.search.IndexingRules.Rule;
-
-/**
- * A simple {@link IndexingStrategy} implementation that relies upon very few fields to be stored in the indexes.
- */
- at ThreadSafe
-class StoreLittleIndexingStrategy implements IndexingStrategy {
-
-    static class PathIndex {
-        public static final String PATH = "path";
-        public static final String UUID = "uuid";
-    }
-
-    static class ContentIndex {
-        public static final String UUID = PathIndex.UUID;
-        public static final String FULL_TEXT = "fts";
-    }
-
-    public static final int SIZE_OF_DELETE_BATCHES = 100;
-
-    private ThreadLocal<DateFormat> dateFormatter = new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            return new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
-        }
-    };
-
-    private static final FieldSelector UUID_FIELD_SELECTOR = new FieldSelector() {
-        private static final long serialVersionUID = 1L;
-
-        public FieldSelectorResult accept( String fieldName ) {
-            return PathIndex.UUID.equals(fieldName) ? FieldSelectorResult.LOAD_AND_BREAK : FieldSelectorResult.NO_LOAD;
-        }
-    };
-
-    /**
-     * The default set of {@link IndexingRules} used by {@link StoreLittleIndexingStrategy} instances when no rules are provided.
-     */
-    public static final IndexingRules DEFAULT_RULES;
-
-    static {
-        IndexingRules.Builder builder = IndexingRules.createBuilder();
-        // Configure the default behavior ...
-        builder.defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE);
-        // Configure the UUID properties to be just indexed (not stored, not analyzed, not included in full-text) ...
-        builder.index(JcrLexicon.UUID, DnaLexicon.UUID);
-        // Configure the properties that we'll treat as dates ...
-        builder.treatAsDates(JcrLexicon.CREATED, JcrLexicon.LAST_MODIFIED);
-        DEFAULT_RULES = builder.build();
-    }
-
-    private final IndexingRules rules;
-    private final Logger logger;
-    private final LuceneQueryEngine queryEngine;
-
-    /**
-     * Create a new indexing strategy instance that does not support queries.
-     */
-    public StoreLittleIndexingStrategy() {
-        this(null);
-    }
-
-    /**
-     * Create a new indexing strategy instance.
-     * 
-     * @param rules the indexing rules that govern how properties are to be index, or null if the {@link #DEFAULT_RULES default
-     *        rules} are to be used
-     */
-    public StoreLittleIndexingStrategy( IndexingRules rules ) {
-        this.rules = rules != null ? rules : DEFAULT_RULES;
-        this.logger = Logger.getLogger(getClass());
-        this.queryEngine = new LuceneQueryEngine();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#getNamespaceEncoder()
-     */
-    public TextEncoder getNamespaceEncoder() {
-        return new NoOpEncoder();
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#getChangeCountForAutomaticOptimization()
-     */
-    public int getChangeCountForAutomaticOptimization() {
-        return 0;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#createAnalyzer()
-     */
-    public Analyzer createAnalyzer() {
-        return new StandardAnalyzer(Version.LUCENE_CURRENT);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Because this strategy uses multiple indexes, and since there's no correlation between the documents in those indexes, we
-     * need to perform the delete in multiple steps. First, we need to perform a query to find out which nodes exist below a
-     * certain path. Then, we need to delete those nodes from the paths index. Finally, we need to delete the corresponding
-     * documents in the content index that represent those same nodes.
-     * </p>
-     * <p>
-     * Since we don't know how many documents there will be, we perform these steps in batches, where each batch limits the number
-     * of results to a maximum number. We repeat batches as long as we find more results. This approach has the advantage that
-     * we'll never bring in a large number of results, and it allows us to delete the documents from the content node using a
-     * query.
-     * </p>
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#deleteBelow(Path, IndexContext)
-     */
-    public int deleteBelow( Path path,
-                            IndexContext indexes ) throws IOException {
-        // Perform a query using the reader to find those nodes at/below the path ...
-        try {
-            IndexReader pathReader = indexes.getPathsReader();
-            IndexSearcher pathSearcher = new IndexSearcher(pathReader);
-            String pathStr = indexes.stringFactory().create(path) + "/";
-            PrefixQuery query = new PrefixQuery(new Term(PathIndex.PATH, pathStr));
-            int numberDeleted = 0;
-            while (true) {
-                // Execute the query and get the results ...
-                TopDocs results = pathSearcher.search(query, SIZE_OF_DELETE_BATCHES);
-                int numResultsInBatch = results.scoreDocs.length;
-                // Walk the results, delete the doc, and add to the query that we'll use against the content index ...
-                IndexReader contentReader = indexes.getContentReader();
-                for (ScoreDoc result : results.scoreDocs) {
-                    int docId = result.doc;
-                    // Find the UUID of the node ...
-                    Document doc = pathReader.document(docId, UUID_FIELD_SELECTOR);
-                    String uuid = doc.get(PathIndex.UUID);
-                    // Delete the document from the paths index ...
-                    pathReader.deleteDocument(docId);
-                    // Delete the corresponding document from the content index ...
-                    contentReader.deleteDocuments(new Term(ContentIndex.UUID, uuid));
-                }
-                numberDeleted += numResultsInBatch;
-                if (numResultsInBatch < SIZE_OF_DELETE_BATCHES) break;
-            }
-            indexes.commit();
-            return numberDeleted;
-        } catch (FileNotFoundException e) {
-            // There are no index files yet, so nothing to delete ...
-            return 0;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#index(Node, IndexContext)
-     */
-    public void index( Node node,
-                       IndexContext indexes ) throws IOException {
-        ValueFactory<String> strings = indexes.stringFactory();
-        Location location = node.getLocation();
-        UUID uuid = location.getUuid();
-        if (uuid == null) uuid = UUID.randomUUID();
-        Path path = location.getPath();
-        String pathStr = path.isRoot() ? "/" : strings.create(location.getPath()) + "/";
-        String uuidStr = uuid.toString();
-
-        if (logger.isTraceEnabled()) {
-            logger.trace("indexing {0}", pathStr);
-        }
-
-        // Create a separate document for the path, which makes it easier to handle moves since the path can
-        // be changed without changing any other content fields ...
-        Document doc = new Document();
-        doc.add(new Field(PathIndex.PATH, pathStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        doc.add(new Field(PathIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        indexes.getPathsWriter().addDocument(doc);
-
-        // Create the document for the content (properties) ...
-        doc = new Document();
-        doc.add(new Field(ContentIndex.UUID, uuidStr, Field.Store.YES, Field.Index.NOT_ANALYZED));
-        String stringValue = null;
-        StringBuilder fullTextSearchValue = null;
-        for (Property property : node.getProperties()) {
-            Name name = property.getName();
-            Rule rule = rules.getRule(name);
-            if (rule.isSkipped()) continue;
-            String nameString = strings.create(name);
-            if (rule.isDate()) {
-                DateTimeFactory dateFactory = indexes.dateFactory();
-                for (Object value : property) {
-                    if (value == null) continue;
-                    DateTime dateValue = dateFactory.create(value);
-                    stringValue = dateFormatter.get().format(dateValue.toDate());
-                    // Add a separate field for each property value ...
-                    doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                    // Dates are not added to the full-text search field (since this wouldn't make sense)
-                }
-                continue;
-            }
-            for (Object value : property) {
-                if (value == null) continue;
-                if (value instanceof Binary) {
-                    // don't include binary values as individual fields but do include them in the full-text search ...
-                    // TODO : add to full-text search ...
-                    continue;
-                }
-                stringValue = strings.create(value);
-                // Add a separate field for each property value ...
-                doc.add(new Field(nameString, stringValue, rule.getStoreOption(), rule.getIndexOption()));
-                // And add to the full-text field ...
-                if (rule.isFullText()) {
-                    if (fullTextSearchValue == null) {
-                        fullTextSearchValue = new StringBuilder();
-                    } else {
-                        fullTextSearchValue.append(' ');
-                    }
-                    fullTextSearchValue.append(stringValue);
-                }
-            }
-        }
-        // Add the full-text-search field ...
-        if (fullTextSearchValue != null) {
-            doc.add(new Field(ContentIndex.FULL_TEXT, fullTextSearchValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
-        }
-        indexes.getContentWriter().addDocument(doc);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#performQuery(String, int, int, IndexContext, List)
-     */
-    public void performQuery( String fullTextString,
-                              int maxResults,
-                              int offset,
-                              IndexContext indexes,
-                              List<Location> results ) throws IOException, ParseException {
-        assert fullTextString != null;
-        assert fullTextString.length() > 0;
-        assert offset >= 0;
-        assert maxResults > 0;
-        assert indexes != null;
-        assert results != null;
-
-        // Parse the full-text search and search against the 'fts' field ...
-        QueryParser parser = new QueryParser(ContentIndex.FULL_TEXT, createAnalyzer());
-        Query query = parser.parse(fullTextString);
-        TopDocs docs = indexes.getContentSearcher().search(query, maxResults + offset);
-
-        // Collect the results ...
-        IndexReader contentReader = indexes.getContentReader();
-        IndexReader pathReader = indexes.getPathsReader();
-        IndexSearcher pathSearcher = indexes.getPathsSearcher();
-        ScoreDoc[] scoreDocs = docs.scoreDocs;
-        int numberOfResults = scoreDocs.length;
-        if (numberOfResults > offset) {
-            // There are enough results to satisfy the offset ...
-            for (int i = offset, num = scoreDocs.length; i != num; ++i) {
-                ScoreDoc result = scoreDocs[i];
-                int docId = result.doc;
-                // Find the UUID of the node (this UUID might be artificial, so we have to find the path) ...
-                Document doc = contentReader.document(docId, UUID_FIELD_SELECTOR);
-                String uuid = doc.get(ContentIndex.UUID);
-                // Find the path for this node (is there a better way to do this than one search per UUID?) ...
-                TopDocs pathDocs = pathSearcher.search(new TermQuery(new Term(PathIndex.UUID, uuid)), 1);
-                if (pathDocs.scoreDocs.length < 1) {
-                    // No path record found ...
-                    continue;
-                }
-                Document pathDoc = pathReader.document(pathDocs.scoreDocs[0].doc);
-                Path path = indexes.pathFactory().create(pathDoc.get(PathIndex.PATH));
-                // Now add the location ...
-                results.add(Location.create(path, UUID.fromString(uuid)));
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#performQuery(org.jboss.dna.graph.query.model.QueryCommand,
-     *      org.jboss.dna.graph.query.validate.Schemata, org.jboss.dna.search.IndexContext)
-     */
-    public QueryResults performQuery( QueryCommand query,
-                                      Schemata schemata,
-                                      IndexContext indexes ) throws IOException, ParseException {
-        return this.queryEngine.execute(query, schemata, indexes);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.search.IndexingStrategy#apply(Iterable, IndexContext)
-     */
-    public int apply( Iterable<ChangeRequest> changes,
-                      IndexContext indexes ) /*throws IOException*/{
-        for (ChangeRequest change : changes) {
-            if (change != null) continue;
-        }
-        return 0;
-    }
-}

Modified: trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java
===================================================================
--- trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/main/java/org/jboss/dna/search/WorkspaceSearchEngine.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -55,7 +55,7 @@
  * A search engine dedicated to a single workspace.
  */
 @ThreadSafe
-public class WorkspaceSearchEngine {
+class WorkspaceSearchEngine {
 
     protected static final String PATHS_INDEX_NAME = "paths";
     protected static final String CONTENT_INDEX_NAME = "content";
@@ -67,7 +67,7 @@
     private final String sourceName;
     private final String workspaceName;
     private final RepositoryConnectionFactory connectionFactory;
-    private final IndexingStrategy indexingStrategy;
+    private final IndexStrategy indexingStrategy;
     protected final AtomicInteger modifiedNodesSinceLastOptimize = new AtomicInteger(0);
 
     /**
@@ -86,7 +86,7 @@
      */
     protected WorkspaceSearchEngine( ExecutionContext context,
                                      DirectoryConfiguration directoryFactory,
-                                     IndexingStrategy indexingStrategy,
+                                     IndexStrategy indexingStrategy,
                                      String sourceName,
                                      String workspaceName,
                                      RepositoryConnectionFactory connectionFactory ) throws SearchEngineException {
@@ -168,7 +168,7 @@
         return context.getValueFactories().getStringFactory().create(path);
     }
 
-    final IndexingStrategy strategy() {
+    final IndexStrategy strategy() {
         return indexingStrategy;
     }
 
@@ -523,7 +523,7 @@
         final List<Location> results = new ArrayList<Location>(maxResults);
         return new Search() {
             public void execute( IndexContext indexes ) throws IOException, ParseException {
-                strategy().performQuery(fullTextSearch, maxResults, offset, indexes, results);
+                strategy().search(fullTextSearch, maxResults, offset, indexes, results);
             }
 
             public String messageFor( Throwable error ) {
@@ -550,9 +550,11 @@
                                   final Schemata schemata ) {
         return new Query() {
             private QueryResults results = null;
+            private SearchContext context = null;
 
-            public void execute( IndexContext indexes ) throws IOException, ParseException {
-                results = strategy().performQuery(query, schemata, indexes);
+            public void execute( IndexContext indexes ) {
+                context = new SearchContext(indexes, schemata);
+                results = strategy().query(context, query);
             }
 
             public String messageFor( Throwable error ) {

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/IndexingRulesTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -25,7 +25,7 @@
 
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
-import org.jboss.dna.search.IndexingRules.Builder;
+import org.jboss.dna.search.IndexRules.Builder;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -35,23 +35,23 @@
 public class IndexingRulesTest {
 
     private Builder builder;
-    private IndexingRules rules;
+    private IndexRules rules;
 
     @Before
     public void beforeEach() {
-        builder = IndexingRules.createBuilder();
+        builder = IndexRules.createBuilder();
         rules = builder.build();
     }
 
     @Test
     public void shouldBuildValidRulesFromBuilderThatIsNotInvoked() {
-        builder = IndexingRules.createBuilder();
+        builder = IndexRules.createBuilder();
         rules = builder.build();
     }
 
     @Test
     public void shouldBuildValidRulesFromBuilderAfterJustSettingDefaultRules() {
-        builder.defaultTo(IndexingRules.FULL_TEXT);
+        builder.defaultTo(IndexRules.FULL_TEXT);
         rules = builder.build();
         assertThat(rules.getRule(null).isFullText(), is(true));
     }

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/SearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -48,7 +48,6 @@
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
     private DirectoryConfiguration directoryFactory;
-    private IndexingStrategy indexingStrategy;
     private Graph content;
 
     @Before
@@ -81,15 +80,9 @@
             }
         };
 
-        // Set up the indexing strategy ...
-        IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
-                                           .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
-                                           .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(rules);
-
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();
-        engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory, indexingStrategy);
+        engine = new SearchEngine(context, sourceName, connectionFactory, directoryFactory);
     }
 
     protected Path path( String string ) {

Modified: trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java
===================================================================
--- trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:23:02 UTC (rev 1318)
+++ trunk/dna-search/src/test/java/org/jboss/dna/search/WorkspaceSearchEngineTest.java	2009-11-16 23:24:06 UTC (rev 1319)
@@ -47,7 +47,7 @@
     private InMemoryRepositorySource source;
     private RepositoryConnectionFactory connectionFactory;
     private DirectoryConfiguration directoryFactory;
-    private IndexingStrategy indexingStrategy;
+    private IndexStrategy indexingStrategy;
     private Graph content;
 
     @Before
@@ -74,10 +74,10 @@
         };
 
         // Set up the indexing strategy ...
-        IndexingRules rules = IndexingRules.createBuilder(StoreLittleIndexingStrategy.DEFAULT_RULES)
-                                           .defaultTo(IndexingRules.INDEX | IndexingRules.ANALYZE | IndexingRules.FULL_TEXT)
+        IndexRules rules = IndexRules.createBuilder(KitchenSinkIndexStrategy.DEFAULT_RULES)
+                                           .defaultTo(IndexRules.INDEX | IndexRules.ANALYZE | IndexRules.FULL_TEXT)
                                            .build();
-        indexingStrategy = new StoreLittleIndexingStrategy(rules);
+        indexingStrategy = new KitchenSinkIndexStrategy(rules);
 
         // Now set up the search engine ...
         directoryFactory = DirectoryConfigurations.inMemory();



More information about the dna-commits mailing list