You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2013/03/06 06:12:23 UTC
svn commit: r1453162 [1/2] - in /lucene/dev/branches/branch_4x: ./
dev-tools/ dev-tools/maven/ dev-tools/maven/solr/
dev-tools/maven/solr/core/src/java/ lucene/ lucene/analysis/
lucene/analysis/common/src/java/org/apache/lucene/analysis/util/ solr/ sol...
Author: sarowe
Date: Wed Mar 6 05:12:22 2013
New Revision: 1453162
URL: http://svn.apache.org/r1453162
Log:
SOLR-4503: Add REST API methods to get schema information: fields, dynamicFields, fieldTypes, and copyFields. Restlet 2.1.1 is integrated and is used to service these requests.
Also fixes bugs in dynamic copyField logic described in SOLR-3798.
Also fixes a bug with proxied SolrCloud requests (SOLR-4210) when using the GET method.
(merged trunk r1453161)
Added:
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/rest/
- copied from r1453161, lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
- copied unchanged from r1453161, lucene/dev/trunk/solr/core/src/java/org/apache/solr/servlet/ResponseUtils.java
lucene/dev/branches/branch_4x/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
- copied unchanged from r1453161, lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/rest/
- copied from r1453161, lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/
lucene/dev/branches/branch_4x/solr/licenses/org.restlet-2.1.1.jar.sha1
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet-2.1.1.jar.sha1
lucene/dev/branches/branch_4x/solr/licenses/org.restlet-LICENSE-ASL.txt
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet-LICENSE-ASL.txt
lucene/dev/branches/branch_4x/solr/licenses/org.restlet-NOTICE.txt
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet-NOTICE.txt
lucene/dev/branches/branch_4x/solr/licenses/org.restlet.ext.servlet-2.1.1.jar.sha1
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet.ext.servlet-2.1.1.jar.sha1
lucene/dev/branches/branch_4x/solr/licenses/org.restlet.ext.servlet-LICENSE-ASL.txt
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet.ext.servlet-LICENSE-ASL.txt
lucene/dev/branches/branch_4x/solr/licenses/org.restlet.ext.servlet-NOTICE.txt
- copied unchanged from r1453161, lucene/dev/trunk/solr/licenses/org.restlet.ext.servlet-NOTICE.txt
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
- copied unchanged from r1453161, lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/util/RESTfulServerProvider.java
- copied unchanged from r1453161, lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RESTfulServerProvider.java
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java
- copied unchanged from r1453161, lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java
- copied unchanged from r1453161, lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java
Modified:
lucene/dev/branches/branch_4x/ (props changed)
lucene/dev/branches/branch_4x/dev-tools/ (props changed)
lucene/dev/branches/branch_4x/dev-tools/maven/pom.xml.template
lucene/dev/branches/branch_4x/dev-tools/maven/solr/core/src/java/pom.xml.template
lucene/dev/branches/branch_4x/dev-tools/maven/solr/pom.xml.template
lucene/dev/branches/branch_4x/lucene/ (props changed)
lucene/dev/branches/branch_4x/lucene/analysis/ (props changed)
lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java
lucene/dev/branches/branch_4x/lucene/ivy-settings.xml (contents, props changed)
lucene/dev/branches/branch_4x/solr/ (props changed)
lucene/dev/branches/branch_4x/solr/CHANGES.txt (contents, props changed)
lucene/dev/branches/branch_4x/solr/NOTICE.txt (contents, props changed)
lucene/dev/branches/branch_4x/solr/core/ (props changed)
lucene/dev/branches/branch_4x/solr/core/ivy.xml
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/core/SolrCore.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/AbstractSubTypeFieldType.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/CurrencyField.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldType.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/SchemaField.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/TextField.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/update/processor/LogUpdateProcessorFactory.java
lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java
lucene/dev/branches/branch_4x/solr/licenses/ (props changed)
lucene/dev/branches/branch_4x/solr/test-framework/ (props changed)
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
lucene/dev/branches/branch_4x/solr/webapp/ (props changed)
lucene/dev/branches/branch_4x/solr/webapp/web/WEB-INF/web.xml
Modified: lucene/dev/branches/branch_4x/dev-tools/maven/pom.xml.template
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/dev-tools/maven/pom.xml.template?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/dev-tools/maven/pom.xml.template (original)
+++ lucene/dev/branches/branch_4x/dev-tools/maven/pom.xml.template Wed Mar 6 05:12:22 2013
@@ -49,6 +49,7 @@
<tika.version>1.3</tika.version>
<httpcomponents.version>4.2.3</httpcomponents.version>
<commons-io.version>2.1</commons-io.version>
+ <restlet.version>2.1.1</restlet.version>
<!-- RandomizedTesting library system properties -->
<tests.iters>1</tests.iters>
@@ -391,6 +392,16 @@
<version>${jetty.version}</version>
</dependency>
<dependency>
+ <groupId>org.restlet.jee</groupId>
+ <artifactId>org.restlet</artifactId>
+ <version>${restlet.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jee</groupId>
+ <artifactId>org.restlet.ext.servlet</artifactId>
+ <version>${restlet.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
Modified: lucene/dev/branches/branch_4x/dev-tools/maven/solr/core/src/java/pom.xml.template
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/dev-tools/maven/solr/core/src/java/pom.xml.template?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/dev-tools/maven/solr/core/src/java/pom.xml.template (original)
+++ lucene/dev/branches/branch_4x/dev-tools/maven/solr/core/src/java/pom.xml.template Wed Mar 6 05:12:22 2013
@@ -136,6 +136,16 @@
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
+ <groupId>org.restlet.jee</groupId>
+ <artifactId>org.restlet</artifactId>
+ <version>${restlet.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jee</groupId>
+ <artifactId>org.restlet.ext.servlet</artifactId>
+ <version>${restlet.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
Modified: lucene/dev/branches/branch_4x/dev-tools/maven/solr/pom.xml.template
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/dev-tools/maven/solr/pom.xml.template?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/dev-tools/maven/solr/pom.xml.template (original)
+++ lucene/dev/branches/branch_4x/dev-tools/maven/solr/pom.xml.template Wed Mar 6 05:12:22 2013
@@ -76,6 +76,13 @@
</mailingList>
</mailingLists>
<inceptionYear>2006</inceptionYear>
+ <repositories>
+ <repository>
+ <id>maven-restlet</id>
+ <name>Public online Restlet repository</name>
+ <url>http://maven.restlet.org</url>
+ </repository>
+ </repositories>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
Modified: lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java (original)
+++ lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java Wed Mar 6 05:12:22 2013
@@ -29,6 +29,7 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@@ -49,6 +50,9 @@ import java.util.regex.PatternSyntaxExce
*/
public abstract class AbstractAnalysisFactory {
+ /** The original args, before init() processes them */
+ private Map<String,String> originalArgs;
+
/** The init args */
protected Map<String,String> args;
@@ -59,12 +63,17 @@ public abstract class AbstractAnalysisFa
* Initialize this factory via a set of key-value pairs.
*/
public void init(Map<String,String> args) {
- this.args = args;
+ originalArgs = Collections.unmodifiableMap(args);
+ this.args = new HashMap<String,String>(args);
}
public Map<String,String> getArgs() {
return args;
}
+
+ public Map<String,String> getOriginalArgs() {
+ return originalArgs;
+ }
/** this method can be called in the {@link org.apache.lucene.analysis.util.TokenizerFactory#create(java.io.Reader)}
* or {@link org.apache.lucene.analysis.util.TokenFilterFactory#create(org.apache.lucene.analysis.TokenStream)} methods,
Modified: lucene/dev/branches/branch_4x/lucene/ivy-settings.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/lucene/ivy-settings.xml?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/lucene/ivy-settings.xml (original)
+++ lucene/dev/branches/branch_4x/lucene/ivy-settings.xml Wed Mar 6 05:12:22 2013
@@ -30,6 +30,7 @@
<resolvers>
<ibiblio name="sonatype-releases" root="http://oss.sonatype.org/content/repositories/releases" m2compatible="true" />
+ <ibiblio name="maven.restlet.org" root="http://maven.restlet.org" m2compatible="true" />
<!-- you might need to tweak this from china so it works -->
<ibiblio name="working-chinese-mirror" root="http://mirror.netcologne.de/maven2" m2compatible="true" />
@@ -48,6 +49,7 @@
<!-- <resolver ref="local-maven-2" /> -->
<resolver ref="main"/>
<resolver ref="sonatype-releases" />
+ <resolver ref="maven.restlet.org" />
<resolver ref="working-chinese-mirror" />
</chain>
</resolvers>
Modified: lucene/dev/branches/branch_4x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/CHANGES.txt?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_4x/solr/CHANGES.txt Wed Mar 6 05:12:22 2013
@@ -87,6 +87,10 @@ New Features
"natural" value, converted to an optionally specified currency to
override the default for the field type.
(hossman)
+
+* SOLR-4503: Add REST API methods, via Restlet integration, for reading schema
+ elements, at /schema/fields/, /schema/dynamicfields/, /schema/fieldtypes/,
+ and /schema/copyfields/. (Steve Rowe)
Bug Fixes
----------------------
@@ -191,6 +195,10 @@ Bug Fixes
* SOLR-4518: Improved CurrencyField error messages when attempting to
use a Currency that is not supported by the current JVM. (hossman)
+
+* SOLR-3798: Fix copyField implementation in IndexSchema to handle
+ dynamic field references that aren't string-equal to the name of
+ the referenced dynamic field. (Steve Rowe)
Optimizations
----------------------
Modified: lucene/dev/branches/branch_4x/solr/NOTICE.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/NOTICE.txt?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/NOTICE.txt (original)
+++ lucene/dev/branches/branch_4x/solr/NOTICE.txt Wed Mar 6 05:12:22 2013
@@ -503,3 +503,15 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTI
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=========================================================================
+== Restlet Notice ==
+=========================================================================
+
+Copyright (C) 2005-2013 Restlet S.A.S.
+
+Restlet is a registered trademark of Restlet S.A.S.
+
+This product contains software developed by the Restlet project.
+
+See http://www.restlet.org/
Modified: lucene/dev/branches/branch_4x/solr/core/ivy.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/ivy.xml?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/ivy.xml (original)
+++ lucene/dev/branches/branch_4x/solr/core/ivy.xml Wed Mar 6 05:12:22 2013
@@ -30,6 +30,8 @@
<dependency org="org.objenesis" name="objenesis" rev="1.2" transitive="false"/>
<dependency org="com.spatial4j" name="spatial4j" rev="0.3" transitive="false"/>
<dependency org="javax.servlet" name="javax.servlet-api" rev="3.0.1" transitive="false"/>
- <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>
+ <dependency org="org.restlet.jee" name="org.restlet" rev="2.1.1" transitive="false"/>
+ <dependency org="org.restlet.jee" name="org.restlet.ext.servlet" rev="2.1.1" transitive="false"/>
+ <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>
</dependencies>
</ivy-module>
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java Wed Mar 6 05:12:22 2013
@@ -18,9 +18,13 @@
package org.apache.solr.client.solrj.embedded;
import java.io.IOException;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Random;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicLong;
import java.net.URL;
@@ -39,8 +43,10 @@ import javax.servlet.http.HttpServletRes
import org.apache.solr.servlet.SolrDispatchFilter;
import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslConnector;
import org.eclipse.jetty.server.ssl.SslSocketConnector;
@@ -49,6 +55,7 @@ import org.eclipse.jetty.server.handler.
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
@@ -87,6 +94,9 @@ public class JettySolrRunner {
private String coreNodeName;
+ /** Maps servlet holders (i.e. factories: class + init params) to path specs */
+ private SortedMap<ServletHolder,String> extraServlets = new TreeMap<ServletHolder,String>();
+
public static class DebugFilter implements Filter {
public int requestsToKeep = 10;
private AtomicLong nRequests = new AtomicLong();
@@ -151,6 +161,19 @@ public class JettySolrRunner {
this.schemaFilename = schemaFileName;
}
+ /**
+ * Constructor taking an ordered list of additional (servlet holder -> path spec) mappings
+ * to add to the servlet context
+ */
+ public JettySolrRunner(String solrHome, String context, int port,
+ String solrConfigFilename, String schemaFileName, boolean stopAtShutdown,
+ SortedMap<ServletHolder,String> extraServlets) {
+ if (null != extraServlets) { this.extraServlets.putAll(extraServlets); }
+ this.init(solrHome, context, port, stopAtShutdown);
+ this.solrConfigFilename = solrConfigFilename;
+ this.schemaFilename = schemaFileName;
+ }
+
private void init(String solrHome, String context, int port, boolean stopAtShutdown) {
this.context = context;
server = new Server(port);
@@ -285,6 +308,10 @@ public class JettySolrRunner {
// FilterHolder fh = new FilterHolder(filter);
debugFilter = root.addFilter(DebugFilter.class, "*", EnumSet.of(DispatcherType.REQUEST) );
dispatchFilter = root.addFilter(SolrDispatchFilter.class, "*", EnumSet.of(DispatcherType.REQUEST) );
+ for (ServletHolder servletHolder : extraServlets.keySet()) {
+ String pathSpec = extraServlets.get(servletHolder);
+ root.addServlet(servletHolder, pathSpec);
+ }
if (solrConfigFilename != null) System.clearProperty("solrconfig");
if (schemaFilename != null) System.clearProperty("schema");
System.clearProperty("solr.solr.home");
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/core/SolrCore.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/core/SolrCore.java Wed Mar 6 05:12:22 2013
@@ -1778,7 +1778,6 @@ public final class SolrCore implements S
}
}
-
public void execute(SolrRequestHandler handler, SolrQueryRequest req, SolrQueryResponse rsp) {
if (handler==null) {
String msg = "Null Request Handler '" +
@@ -1788,45 +1787,40 @@ public final class SolrCore implements S
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
}
- // setup response header and handle request
- final NamedList<Object> responseHeader = new SimpleOrderedMap<Object>();
- rsp.add("responseHeader", responseHeader);
- // toLog is a local ref to the same NamedList used by the request
- NamedList<Object> toLog = rsp.getToLog();
- // for back compat, we set these now just in case other code
- // are expecting them during handleRequest
-
- toLog.add("webapp", req.getContext().get("webapp"));
- toLog.add("path", req.getContext().get("path"));
- toLog.add("params", "{" + req.getParamString() + "}");
+ preDecorateResponse(req, rsp);
// TODO: this doesn't seem to be working correctly and causes problems with the example server and distrib (for example /spell)
// if (req.getParams().getBool(ShardParams.IS_SHARD,false) && !(handler instanceof SearchHandler))
// throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,"isShard is only acceptable with search handlers");
handler.handleRequest(req,rsp);
- setResponseHeaderValues(handler,req,rsp);
- if (log.isInfoEnabled() && toLog.size() > 0) {
- StringBuilder sb = new StringBuilder(logid);
- for (int i=0; i<toLog.size(); i++) {
- String name = toLog.getName(i);
- Object val = toLog.getVal(i);
- if (name != null) {
- sb.append(name).append('=');
- }
- sb.append(val).append(' ');
- }
+ postDecorateResponse(handler, req, rsp);
- log.info(sb.toString());
+ if (log.isInfoEnabled() && rsp.getToLog().size() > 0) {
+ log.info(rsp.getToLogAsString(logid));
}
-
}
+ public static void preDecorateResponse(SolrQueryRequest req, SolrQueryResponse rsp) {
+ // setup response header
+ final NamedList<Object> responseHeader = new SimpleOrderedMap<Object>();
+ rsp.add("responseHeader", responseHeader);
-
- public static void setResponseHeaderValues(SolrRequestHandler handler, SolrQueryRequest req, SolrQueryResponse rsp) {
+ // toLog is a local ref to the same NamedList used by the response
+ NamedList<Object> toLog = rsp.getToLog();
+
+ // for back compat, we set these now just in case other code
+ // are expecting them during handleRequest
+ toLog.add("webapp", req.getContext().get("webapp"));
+ toLog.add("path", req.getContext().get("path"));
+ toLog.add("params", "{" + req.getParamString() + "}");
+ }
+
+ /** Put status, QTime, and possibly request handler and params, in the response header */
+ public static void postDecorateResponse
+ (SolrRequestHandler handler, SolrQueryRequest req, SolrQueryResponse rsp) {
// TODO should check that responseHeader has not been replaced by handler
NamedList<Object> responseHeader = rsp.getResponseHeader();
final int qtime=(int)(rsp.getEndTime() - req.getStartTime());
@@ -1847,7 +1841,7 @@ public final class SolrCore implements S
}
SolrParams params = req.getParams();
- if( params.getBool(CommonParams.HEADER_ECHO_HANDLER, false) ) {
+ if( null != handler && params.getBool(CommonParams.HEADER_ECHO_HANDLER, false) ) {
responseHeader.add("handler", handler.getName() );
}
@@ -1867,7 +1861,6 @@ public final class SolrCore implements S
}
}
-
final public static void log(Throwable e) {
SolrException.log(log,null,e);
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java Wed Mar 6 05:12:22 2013
@@ -215,6 +215,20 @@ public class SolrQueryResponse {
public NamedList<Object> getToLog() {
return toLog;
}
+
+ /** Returns a string of the form "logid name1=value1 name2=value2 ..." */
+ public String getToLogAsString(String logid) {
+ StringBuilder sb = new StringBuilder(logid);
+ for (int i=0; i<toLog.size(); i++) {
+ String name = toLog.getName(i);
+ Object val = toLog.getVal(i);
+ if (name != null) {
+ sb.append(name).append('=');
+ }
+ sb.append(val).append(' ');
+ }
+ return sb.toString();
+ }
/**
* Enables or disables the emission of HTTP caching headers for this response.
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/AbstractSubTypeFieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/AbstractSubTypeFieldType.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/AbstractSubTypeFieldType.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/AbstractSubTypeFieldType.java Wed Mar 6 05:12:22 2013
@@ -89,7 +89,7 @@ public abstract class AbstractSubTypeFie
int p = SchemaField.calcProps(name, type, props);
SchemaField proto = SchemaField.create(name,
type, p, null);
- schema.registerDynamicField(proto);
+ schema.registerDynamicFields(proto);
return proto;
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/CurrencyField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/CurrencyField.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/CurrencyField.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/CurrencyField.java Wed Mar 6 05:12:22 2013
@@ -207,7 +207,7 @@ public class CurrencyField extends Field
props.put("multiValued", "false");
props.put("omitNorms", "true");
int p = SchemaField.calcProps(name, type, props);
- schema.registerDynamicField(SchemaField.create(name, type, p, null));
+ schema.registerDynamicFields(SchemaField.create(name, type, p, null));
}
/**
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldType.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldType.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldType.java Wed Mar 6 05:12:22 2013
@@ -19,15 +19,22 @@ package org.apache.solr.schema;
import java.io.IOException;
import java.io.Reader;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
+import org.apache.lucene.analysis.util.CharFilterFactory;
+import org.apache.lucene.analysis.util.TokenFilterFactory;
+import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.FieldInfo.IndexOptions;
import org.apache.lucene.index.IndexableField;
@@ -48,8 +55,12 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRef;
import org.apache.lucene.util.UnicodeUtil;
import org.apache.solr.analysis.SolrAnalyzer;
+import org.apache.solr.analysis.TokenizerChain;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.common.util.StrUtils;
import org.apache.solr.response.TextResponseWriter;
import org.apache.solr.search.QParser;
import org.apache.solr.search.Sorting;
@@ -81,6 +92,8 @@ public abstract class FieldType extends
/** properties explicitly set to false */
protected int falseProperties;
protected int properties;
+ private boolean isExplicitQueryAnalyzer;
+ private boolean isExplicitAnalyzer;
/** Returns true if fields of this type should be tokenized */
@@ -137,7 +150,7 @@ public abstract class FieldType extends
args.remove("compressThreshold");
}
- this.args=args;
+ this.args = Collections.unmodifiableMap(args);
Map<String,String> initArgs = new HashMap<String,String>(args);
trueProperties = FieldProperties.parseProperties(initArgs,true);
@@ -150,25 +163,25 @@ public abstract class FieldType extends
init(schema, initArgs);
- String positionInc = initArgs.get("positionIncrementGap");
+ String positionInc = initArgs.get(POSITION_INCREMENT_GAP);
if (positionInc != null) {
Analyzer analyzer = getAnalyzer();
if (analyzer instanceof SolrAnalyzer) {
((SolrAnalyzer)analyzer).setPositionIncrementGap(Integer.parseInt(positionInc));
} else {
- throw new RuntimeException("Can't set positionIncrementGap on custom analyzer " + analyzer.getClass());
+ throw new RuntimeException("Can't set " + POSITION_INCREMENT_GAP + " on custom analyzer " + analyzer.getClass());
}
analyzer = getQueryAnalyzer();
if (analyzer instanceof SolrAnalyzer) {
((SolrAnalyzer)analyzer).setPositionIncrementGap(Integer.parseInt(positionInc));
} else {
- throw new RuntimeException("Can't set positionIncrementGap on custom analyzer " + analyzer.getClass());
+ throw new RuntimeException("Can't set " + POSITION_INCREMENT_GAP + " on custom analyzer " + analyzer.getClass());
}
- initArgs.remove("positionIncrementGap");
+ initArgs.remove(POSITION_INCREMENT_GAP);
}
- this.postingsFormat = initArgs.remove("postingsFormat");
- this.docValuesFormat = initArgs.remove("docValuesFormat");
+ this.postingsFormat = initArgs.remove(POSTINGS_FORMAT);
+ this.docValuesFormat = initArgs.remove(DOC_VALUES_FORMAT);
if (initArgs.size() > 0) {
throw new RuntimeException("schema fieldtype " + typeName
@@ -384,7 +397,23 @@ public abstract class FieldType extends
UnicodeUtil.UTF16toUTF8(internal, 0, internal.length(), result);
}
- /**
+ public void setIsExplicitQueryAnalyzer(boolean isExplicitQueryAnalyzer) {
+ this.isExplicitQueryAnalyzer = isExplicitQueryAnalyzer;
+ }
+
+ public boolean isExplicitQueryAnalyzer() {
+ return isExplicitQueryAnalyzer;
+ }
+
+ public void setIsExplicitAnalyzer(boolean explicitAnalyzer) {
+ isExplicitAnalyzer = explicitAnalyzer;
+ }
+
+ public boolean isExplicitAnalyzer() {
+ return isExplicitAnalyzer;
+ }
+
+ /**
* Default analyzer for types that only produce 1 verbatim token...
* A maximum size of chars to be read must be specified
*/
@@ -499,8 +528,11 @@ public abstract class FieldType extends
}
/** @lucene.internal */
+ protected SimilarityFactory similarityFactory;
+
+ /** @lucene.internal */
protected Similarity similarity;
-
+
/**
* Gets the Similarity used when scoring fields of this type
*
@@ -515,6 +547,21 @@ public abstract class FieldType extends
return similarity;
}
+ /**
+ * Gets the factory for the Similarity used when scoring fields of this type
+ *
+ * <p>
+ * The default implementation returns null, which means this type
+ * has no custom similarity factory associated with it.
+ * </p>
+ *
+ * @lucene.internal
+ */
+ public SimilarityFactory getSimilarityFactory() {
+ return similarityFactory;
+ }
+
+
/** Return the numeric type of this field, or null if this field is not a
* numeric field. */
public org.apache.lucene.document.FieldType.NumericType getNumericType() {
@@ -525,8 +572,9 @@ public abstract class FieldType extends
* Sets the Similarity used when scoring fields of this type
* @lucene.internal
*/
- public void setSimilarity(Similarity similarity) {
- this.similarity = similarity;
+ public void setSimilarity(SimilarityFactory similarityFactory) {
+ this.similarityFactory = similarityFactory;
+ this.similarity = similarityFactory.getSimilarity();
}
/**
@@ -674,4 +722,188 @@ public abstract class FieldType extends
}
}
+ private static final String TYPE_NAME = "name";
+ private static final String CLASS_NAME = "class";
+ private static final String ANALYZER = "analyzer";
+ private static final String INDEX_ANALYZER = "indexAnalyzer";
+ private static final String QUERY_ANALYZER = "queryAnalyzer";
+ private static final String MULTI_TERM_ANALYZER = "multiTermAnalyzer";
+ private static final String SIMILARITY = "similarity";
+ private static final String POSTINGS_FORMAT = "postingsFormat";
+ private static final String DOC_VALUES_FORMAT = "docValuesFormat";
+ private static final String AUTO_GENERATE_PHRASE_QUERIES = "autoGeneratePhraseQueries";
+ private static final String ARGS = "args";
+ private static final String CHAR_FILTERS = "charFilters";
+ private static final String TOKENIZER = "tokenizer";
+ private static final String FILTERS = "filters";
+ private static final String POSITION_INCREMENT_GAP = "positionIncrementGap";
+
+ /**
+ * Get a map of property name -> value for this field type.
+ * @param showDefaults if true, include default properties.
+ */
+ public SimpleOrderedMap<Object> getNamedPropertyValues(boolean showDefaults) {
+ SimpleOrderedMap<Object> namedPropertyValues = new SimpleOrderedMap<Object>();
+ namedPropertyValues.add(TYPE_NAME, getTypeName());
+ namedPropertyValues.add(CLASS_NAME, normalizeSPIname(getClass().getName()));
+ if (showDefaults) {
+ Map<String,String> fieldTypeArgs = getNonFieldPropertyArgs();
+ if (null != fieldTypeArgs) {
+ for (String key : fieldTypeArgs.keySet()) {
+ namedPropertyValues.add(key, fieldTypeArgs.get(key));
+ }
+ }
+ if (this instanceof TextField) {
+ namedPropertyValues.add(AUTO_GENERATE_PHRASE_QUERIES, ((TextField) this).getAutoGeneratePhraseQueries());
+ }
+ namedPropertyValues.add(getPropertyName(INDEXED), hasProperty(INDEXED));
+ namedPropertyValues.add(getPropertyName(STORED), hasProperty(STORED));
+ namedPropertyValues.add(getPropertyName(DOC_VALUES), hasProperty(DOC_VALUES));
+ namedPropertyValues.add(getPropertyName(STORE_TERMVECTORS), hasProperty(STORE_TERMVECTORS));
+ namedPropertyValues.add(getPropertyName(STORE_TERMPOSITIONS), hasProperty(STORE_TERMPOSITIONS));
+ namedPropertyValues.add(getPropertyName(STORE_TERMOFFSETS), hasProperty(STORE_TERMOFFSETS));
+ namedPropertyValues.add(getPropertyName(OMIT_NORMS), hasProperty(OMIT_NORMS));
+ namedPropertyValues.add(getPropertyName(OMIT_TF_POSITIONS), hasProperty(OMIT_TF_POSITIONS));
+ namedPropertyValues.add(getPropertyName(OMIT_POSITIONS), hasProperty(OMIT_POSITIONS));
+ namedPropertyValues.add(getPropertyName(STORE_OFFSETS), hasProperty(STORE_OFFSETS));
+ namedPropertyValues.add(getPropertyName(MULTIVALUED), hasProperty(MULTIVALUED));
+ if (hasProperty(SORT_MISSING_FIRST)) {
+ namedPropertyValues.add(getPropertyName(SORT_MISSING_FIRST), true);
+ } else if (hasProperty(SORT_MISSING_LAST)) {
+ namedPropertyValues.add(getPropertyName(SORT_MISSING_LAST), true);
+ }
+ namedPropertyValues.add(getPropertyName(TOKENIZED), isTokenized());
+ // The BINARY property is always false
+ // namedPropertyValues.add(getPropertyName(BINARY), hasProperty(BINARY));
+ } else { // Don't show defaults
+ Set<String> fieldProperties = new HashSet<String>();
+ for (String propertyName : FieldProperties.propertyNames) {
+ fieldProperties.add(propertyName);
+ }
+ for (String key : args.keySet()) {
+ if (fieldProperties.contains(key)) {
+ namedPropertyValues.add(key, StrUtils.parseBool(args.get(key)));
+ } else {
+ namedPropertyValues.add(key, args.get(key));
+ }
+ }
+ }
+
+ if (isExplicitAnalyzer()) {
+ String analyzerProperty = isExplicitQueryAnalyzer() ? INDEX_ANALYZER : ANALYZER;
+ namedPropertyValues.add(analyzerProperty, getAnalyzerProperties(getAnalyzer()));
+ }
+ if (isExplicitQueryAnalyzer()) {
+ String analyzerProperty = isExplicitAnalyzer() ? QUERY_ANALYZER : ANALYZER;
+ namedPropertyValues.add(analyzerProperty, getAnalyzerProperties(getQueryAnalyzer()));
+ }
+ if (this instanceof TextField) {
+ if (((TextField)this).isExplicitMultiTermAnalyzer()) {
+ namedPropertyValues.add(MULTI_TERM_ANALYZER, getAnalyzerProperties(((TextField) this).getMultiTermAnalyzer()));
+ }
+ }
+ if (null != getSimilarity()) {
+ namedPropertyValues.add(SIMILARITY, getSimilarityProperties());
+ }
+ if (null != getPostingsFormat()) {
+ namedPropertyValues.add(POSTINGS_FORMAT, getPostingsFormat());
+ }
+ if (null != getDocValuesFormat()) {
+ namedPropertyValues.add(DOC_VALUES_FORMAT, getDocValuesFormat());
+ }
+ return namedPropertyValues;
+ }
+
+ /** Returns args to this field type that aren't standard field properties */
+ protected Map<String,String> getNonFieldPropertyArgs() {
+ Map<String,String> initArgs = new HashMap<String,String>(args);
+ for (String prop : FieldProperties.propertyNames) {
+ initArgs.remove(prop);
+ }
+ return initArgs;
+ }
+
+ /**
+ * Returns a description of the given analyzer, by either reporting the Analyzer name
+ * if it's not a TokenizerChain, or if it is, querying each analysis factory for its
+ * name and args.
+ */
+ protected static SimpleOrderedMap<Object> getAnalyzerProperties(Analyzer analyzer) {
+ SimpleOrderedMap<Object> analyzerProps = new SimpleOrderedMap<Object>();
+ analyzerProps.add(CLASS_NAME, normalizeSPIname(analyzer.getClass().getName()));
+
+ if (analyzer instanceof TokenizerChain) {
+ Map<String,String> factoryArgs;
+ TokenizerChain tokenizerChain = (TokenizerChain)analyzer;
+ CharFilterFactory[] charFilterFactories = tokenizerChain.getCharFilterFactories();
+ if (null != charFilterFactories && charFilterFactories.length > 0) {
+ List<SimpleOrderedMap<Object>> charFilterProps = new ArrayList<SimpleOrderedMap<Object>>();
+ for (CharFilterFactory charFilterFactory : charFilterFactories) {
+ SimpleOrderedMap<Object> props = new SimpleOrderedMap<Object>();
+ props.add(CLASS_NAME, normalizeSPIname(charFilterFactory.getClass().getName()));
+ factoryArgs = charFilterFactory.getOriginalArgs();
+ if (null != factoryArgs) {
+ for (String key : factoryArgs.keySet()) {
+ props.add(key, factoryArgs.get(key));
+ }
+ }
+ charFilterProps.add(props);
+ }
+ analyzerProps.add(CHAR_FILTERS, charFilterProps);
+ }
+
+ SimpleOrderedMap<Object> tokenizerProps = new SimpleOrderedMap<Object>();
+ TokenizerFactory tokenizerFactory = tokenizerChain.getTokenizerFactory();
+ tokenizerProps.add(CLASS_NAME, normalizeSPIname(tokenizerFactory.getClass().getName()));
+ factoryArgs = tokenizerFactory.getOriginalArgs();
+ if (null != factoryArgs) {
+ for (String key : factoryArgs.keySet()) {
+ tokenizerProps.add(key, factoryArgs.get(key));
+ }
+ }
+ analyzerProps.add(TOKENIZER, tokenizerProps);
+
+ TokenFilterFactory[] filterFactories = tokenizerChain.getTokenFilterFactories();
+ if (null != filterFactories && filterFactories.length > 0) {
+ List<SimpleOrderedMap<Object>> filterProps = new ArrayList<SimpleOrderedMap<Object>>();
+ for (TokenFilterFactory filterFactory : filterFactories) {
+ SimpleOrderedMap<Object> props = new SimpleOrderedMap<Object>();
+ props.add(CLASS_NAME, normalizeSPIname(filterFactory.getClass().getName()));
+ factoryArgs = filterFactory.getOriginalArgs();
+ if (null != factoryArgs) {
+ for (String key : factoryArgs.keySet()) {
+ props.add(key, factoryArgs.get(key));
+ }
+ }
+ filterProps.add(props);
+ }
+ analyzerProps.add(FILTERS, filterProps);
+ }
+ }
+ return analyzerProps;
+ }
+
+ private static String normalizeSPIname(String fullyQualifiedName) {
+ if (fullyQualifiedName.startsWith("org.apache.lucene.") || fullyQualifiedName.startsWith("org.apache.solr.")) {
+ return "solr" + fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf('.'));
+ }
+ return fullyQualifiedName;
+ }
+
+ /** Returns a description of this field's similarity, if any */
+ protected SimpleOrderedMap<Object> getSimilarityProperties() {
+ SimpleOrderedMap<Object> props = new SimpleOrderedMap<Object>();
+ if (similarity != null) {
+ props.add(CLASS_NAME, normalizeSPIname(similarity.getClass().getName()));
+ SolrParams factoryParams = similarityFactory.getParams();
+ if (null != factoryParams) {
+ Iterator<String> iter = factoryParams.getParameterNamesIterator();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ props.add(key, factoryParams.get(key));
+ }
+ }
+ }
+ return props;
+ }
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/FieldTypePluginLoader.java Wed Mar 6 05:12:22 2013
@@ -98,20 +98,35 @@ public final class FieldTypePluginLoader
expression = "./similarity";
anode = (Node)xpath.evaluate(expression, node, XPathConstants.NODE);
SimilarityFactory simFactory = IndexSchema.readSimilarity(loader, anode);
+ if (null != simFactory) {
+ ft.setSimilarity(simFactory);
+ }
- if (queryAnalyzer==null) queryAnalyzer=analyzer;
- if (analyzer==null) analyzer=queryAnalyzer;
- if (multiAnalyzer == null) {
- multiAnalyzer = constructMultiTermAnalyzer(queryAnalyzer);
+ if (null == queryAnalyzer) {
+ queryAnalyzer = analyzer;
+ ft.setIsExplicitQueryAnalyzer(false);
+ } else {
+ ft.setIsExplicitQueryAnalyzer(true);
+ }
+ if (null == analyzer) {
+ analyzer = queryAnalyzer;
+ ft.setIsExplicitAnalyzer(false);
+ } else {
+ ft.setIsExplicitAnalyzer(true);
}
- if (analyzer!=null) {
+
+ if (null != analyzer) {
ft.setAnalyzer(analyzer);
ft.setQueryAnalyzer(queryAnalyzer);
- if (ft instanceof TextField)
+ if (ft instanceof TextField) {
+ if (null == multiAnalyzer) {
+ multiAnalyzer = constructMultiTermAnalyzer(queryAnalyzer);
+ ((TextField)ft).setIsExplicitMultiTermAnalyzer(false);
+ } else {
+ ((TextField)ft).setIsExplicitMultiTermAnalyzer(true);
+ }
((TextField)ft).setMultiTermAnalyzer(multiAnalyzer);
- }
- if (simFactory!=null) {
- ft.setSimilarity(simFactory.getSimilarity());
+ }
}
if (ft instanceof SchemaAware){
schemaAware.add((SchemaAware) ft);
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/IndexSchema.java Wed Mar 6 05:12:22 2013
@@ -22,8 +22,8 @@ import org.apache.lucene.analysis.Analyz
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.similarities.Similarity;
import org.apache.lucene.util.Version;
-import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.util.DOMUtil;
import org.apache.solr.util.SystemIdResolver;
@@ -32,13 +32,17 @@ import org.apache.solr.core.Config;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.search.similarities.DefaultSimilarityFactory;
import org.apache.solr.util.plugin.SolrCoreAware;
-import org.w3c.dom.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
@@ -75,6 +79,7 @@ public final class IndexSchema {
private final List<SchemaField> fieldsWithDefaultValue = new ArrayList<SchemaField>();
private final Collection<SchemaField> requiredFields = new HashSet<SchemaField>();
private DynamicField[] dynamicFields;
+ public DynamicField[] getDynamicFields() { return dynamicFields; }
private Analyzer analyzer;
private Analyzer queryAnalyzer;
@@ -84,13 +89,16 @@ public final class IndexSchema {
private final Map<String, List<CopyField>> copyFieldsMap = new HashMap<String, List<CopyField>>();
+ public Map<String,List<CopyField>> getCopyFieldsMap() { return Collections.unmodifiableMap(copyFieldsMap); }
+
private DynamicCopy[] dynamicCopyFields;
+ public DynamicCopy[] getDynamicCopyFields() { return dynamicCopyFields; }
+
/**
* keys are all fields copied to, count is num of copyField
* directives that target them.
*/
- private Map<SchemaField, Integer> copyFieldTargetCounts
- = new HashMap<SchemaField, Integer>();
+ private Map<SchemaField, Integer> copyFieldTargetCounts = new HashMap<SchemaField, Integer>();
/**
* Constructs a schema using the specified resource name and stream.
@@ -120,8 +128,7 @@ public final class IndexSchema {
/**
* @since solr 1.4
*/
- public SolrResourceLoader getResourceLoader()
- {
+ public SolrResourceLoader getResourceLoader() {
return loader;
}
@@ -207,8 +214,6 @@ public final class IndexSchema {
*/
public Analyzer getAnalyzer() { return analyzer; }
-
-
/**
* Returns the Analyzer used when searching this index
*
@@ -285,8 +290,7 @@ public final class IndexSchema {
*
* @since solr 1.3
*/
- public void refreshAnalyzers()
- {
+ public void refreshAnalyzers() {
analyzer = new SolrIndexAnalyzer();
queryAnalyzer = new SolrQueryAnalyzer();
}
@@ -387,7 +391,7 @@ public final class IndexSchema {
FieldType ft = fieldTypes.get(type);
if (ft==null) {
- throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Unknown fieldtype '" + type + "' specified on field " + name);
+ throw new SolrException(ErrorCode.BAD_REQUEST,"Unknown fieldtype '" + type + "' specified on field " + name);
}
Map<String,String> args = DOMUtil.toMapExcept(attrs, "name", "type");
@@ -402,7 +406,7 @@ public final class IndexSchema {
if( old != null ) {
String msg = "[schema.xml] Duplicate field definition for '"
+ f.getName() + "' [[["+old.toString()+"]]] and [[["+f.toString()+"]]]";
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg );
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg );
}
log.debug("field defined: " + f);
if( f.getDefaultValue() != null ) {
@@ -414,8 +418,14 @@ public final class IndexSchema {
requiredFields.add(f);
}
} else if (node.getNodeName().equals("dynamicField")) {
- // make sure nothing else has the same path
- addDynamicField(dFields, f);
+ if (isValidDynamicFieldName(name)) {
+ // make sure nothing else has the same path
+ addDynamicField(dFields, f);
+ } else {
+ String msg = "Dynamic field name '" + name
+ + "' should have either a leading or a trailing asterisk, and no others.";
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+ }
} else {
// we should never get here
throw new RuntimeException("Unknown field type");
@@ -452,7 +462,7 @@ public final class IndexSchema {
if (null != ft.getSimilarity()) {
String msg = "FieldType '" + ft.getTypeName() + "' is configured with a similarity, but the global similarity does not support it: " + simFactory.getClass();
log.error(msg);
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg);
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
}
}
@@ -468,7 +478,7 @@ public final class IndexSchema {
SchemaField defaultSearchField = getFields().get(defaultSearchFieldName);
if ((defaultSearchField == null) || !defaultSearchField.indexed()) {
String msg = "default search field '" + defaultSearchFieldName + "' not defined or not indexed" ;
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
}
log.info("default search field in schema is "+defaultSearchFieldName);
@@ -492,7 +502,7 @@ public final class IndexSchema {
") can not be configured with a default value ("+
uniqueKeyField.getDefaultValue()+")";
log.error(msg);
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
if (!uniqueKeyField.stored()) {
@@ -502,7 +512,7 @@ public final class IndexSchema {
String msg = "uniqueKey field ("+uniqueKeyFieldName+
") can not be configured to be multivalued";
log.error(msg);
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
uniqueKeyFieldName=uniqueKeyField.getName();
uniqueKeyFieldType=uniqueKeyField.getType();
@@ -544,7 +554,7 @@ public final class IndexSchema {
String msg = "uniqueKey field ("+uniqueKeyFieldName+
") can not be the dest of a copyField (src="+source+")";
log.error(msg);
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg);
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
@@ -568,38 +578,44 @@ public final class IndexSchema {
throw e;
} catch(Exception e) {
// unexpected exception...
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Schema Parsing Failed: " + e.getMessage(), e);
+ throw new SolrException(ErrorCode.SERVER_ERROR, "Schema Parsing Failed: " + e.getMessage(), e);
}
// create the field analyzers
refreshAnalyzers();
-
}
+ /** Returns true if the given name has exactly one asterisk either at the start or end of the name */
+ private boolean isValidDynamicFieldName(String name) {
+ if (name.startsWith("*") || name.endsWith("*")) {
+ int count = 0;
+ for (int pos = 0 ; pos < name.length() && -1 != (pos = name.indexOf('*', pos)) ; ++pos) ++count;
+ if (1 == count) return true;
+ }
+ return false;
+ }
+
private void addDynamicField(List<DynamicField> dFields, SchemaField f) {
- boolean dup = isDuplicateDynField(dFields, f);
- if( !dup ) {
- addDynamicFieldNoDupCheck(dFields, f);
+ if (isDuplicateDynField(dFields, f)) {
+ String msg = "[schema.xml] Duplicate DynamicField definition for '" + f.getName() + "'";
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
} else {
- String msg = "[schema.xml] Duplicate DynamicField definition for '"
- + f.getName() + "'";
-
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, msg);
+ addDynamicFieldNoDupCheck(dFields, f);
}
}
/**
- * Register one or more new Dynamic Field with the Schema.
- * @param f The {@link org.apache.solr.schema.SchemaField}
+ * Register one or more new Dynamic Fields with the Schema.
+ * @param fields The sequence of {@link org.apache.solr.schema.SchemaField}
*/
- public void registerDynamicField(SchemaField ... f) {
+ public void registerDynamicFields(SchemaField... fields) {
List<DynamicField> dynFields = new ArrayList<DynamicField>(Arrays.asList(dynamicFields));
- for (SchemaField field : f) {
- if (isDuplicateDynField(dynFields, field) == false) {
+ for (SchemaField field : fields) {
+ if (isDuplicateDynField(dynFields, field)) {
+ log.debug("dynamic field already exists: dynamic field: [" + field.getName() + "]");
+ } else {
log.debug("dynamic field creation for schema field: " + field.getName());
addDynamicFieldNoDupCheck(dynFields, field);
- } else {
- log.debug("dynamic field already exists: dynamic field: [" + field.getName() + "]");
}
}
Collections.sort(dynFields);
@@ -612,14 +628,13 @@ public final class IndexSchema {
}
private boolean isDuplicateDynField(List<DynamicField> dFields, SchemaField f) {
- for( DynamicField df : dFields ) {
- if( df.regex.equals( f.name ) ) return true;
+ for (DynamicField df : dFields) {
+ if (df.getRegex().equals(f.name)) return true;
}
return false;
}
- public void registerCopyField( String source, String dest )
- {
+ public void registerCopyField( String source, String dest ) {
registerCopyField(source, dest, CopyField.UNLIMITED);
}
@@ -632,56 +647,87 @@ public final class IndexSchema {
*
* @see SolrCoreAware
*/
- public void registerCopyField( String source, String dest, int maxChars )
- {
- boolean sourceIsPattern = isWildCard(source);
- boolean destIsPattern = isWildCard(dest);
-
- log.debug("copyField source='"+source+"' dest='"+dest+"' maxChars='"+maxChars);
- SchemaField d = getFieldOrNull(dest);
- if(d == null){
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "copyField destination :'"+dest+"' does not exist" );
- }
-
- if(sourceIsPattern) {
- if( destIsPattern ) {
- DynamicField df = null;
- for( DynamicField dd : dynamicFields ) {
- if( dd.regex.equals( dest ) ) {
- df = dd;
- break;
+ public void registerCopyField(String source, String dest, int maxChars) {
+ log.debug("copyField source='" + source + "' dest='" + dest + "' maxChars=" + maxChars);
+
+ DynamicField destDynamicField = null;
+ SchemaField destSchemaField = fields.get(dest);
+ SchemaField sourceSchemaField = fields.get(source);
+
+ DynamicField sourceDynamicBase = null;
+ DynamicField destDynamicBase = null;
+
+ boolean sourceIsDynamicFieldReference = false;
+
+ if (null == destSchemaField || null == sourceSchemaField) {
+ // Go through dynamicFields array only once, collecting info for both source and dest fields, if needed
+ for (DynamicField dynamicField : dynamicFields) {
+ if (null == sourceSchemaField && ! sourceIsDynamicFieldReference) {
+ if (dynamicField.matches(source)) {
+ sourceIsDynamicFieldReference = true;
+ if ( ! source.equals(dynamicField.getRegex())) {
+ sourceDynamicBase = dynamicField;
+ }
}
}
- if( df == null ) {
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, "copyField dynamic destination must match a dynamicField." );
+ if (null == destSchemaField) {
+ if (dest.equals(dynamicField.getRegex())) {
+ destDynamicField = dynamicField;
+ destSchemaField = dynamicField.prototype;
+ } else if (dynamicField.matches(dest)) {
+ destSchemaField = dynamicField.makeSchemaField(dest);
+ destDynamicField = new DynamicField(destSchemaField);
+ destDynamicBase = dynamicField;
+ }
}
- registerDynamicCopyField(new DynamicDestCopy(source, df, maxChars ));
+ if (null != destSchemaField && (null != sourceSchemaField || sourceIsDynamicFieldReference)) break;
}
- else {
- registerDynamicCopyField(new DynamicCopy(source, d, maxChars));
- }
- }
- else if( destIsPattern ) {
- String msg = "copyField only supports a dynamic destination if the source is also dynamic" ;
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, msg );
}
- else {
- // retrieve the field to force an exception if it doesn't exist
- SchemaField f = getField(source);
-
- List<CopyField> copyFieldList = copyFieldsMap.get(source);
- if (copyFieldList == null) {
- copyFieldList = new ArrayList<CopyField>();
- copyFieldsMap.put(source, copyFieldList);
+ if (null == sourceSchemaField && ! sourceIsDynamicFieldReference) {
+ String msg = "copyField source :'" + source + "' is not an explicit field and doesn't match a dynamicField.";
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+ }
+ if (null == destSchemaField) {
+ String msg = "copyField dest :'" + dest + "' is not an explicit field and doesn't match a dynamicField.";
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+ }
+ if (sourceIsDynamicFieldReference) {
+ if (null != destDynamicField) { // source & dest: dynamic field references
+ registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, destDynamicBase));
+ incrementCopyFieldTargetCount(destSchemaField);
+ } else { // source: dynamic field reference; dest: explicit field
+ destDynamicField = new DynamicField(destSchemaField);
+ registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, null));
+ incrementCopyFieldTargetCount(destSchemaField);
+ }
+ } else {
+ if (null != destDynamicField) { // source: explicit field; dest: dynamic field reference
+ if (destDynamicField.pattern instanceof DynamicReplacement.DynamicPattern.NameEquals) {
+ // Dynamic dest with no asterisk is acceptable
+ registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, destDynamicBase));
+ incrementCopyFieldTargetCount(destSchemaField);
+ } else {
+ String msg = "copyField only supports a dynamic destination with an asterisk "
+ + "if the source is also dynamic with an asterisk";
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+ }
+ } else { // source & dest: explicit fields
+ List<CopyField> copyFieldList = copyFieldsMap.get(source);
+ if (copyFieldList == null) {
+ copyFieldList = new ArrayList<CopyField>();
+ copyFieldsMap.put(source, copyFieldList);
+ }
+ copyFieldList.add(new CopyField(sourceSchemaField, destSchemaField, maxChars));
+ incrementCopyFieldTargetCount(destSchemaField);
}
- copyFieldList.add(new CopyField(f, d, maxChars));
-
- copyFieldTargetCounts.put(d, (copyFieldTargetCounts.containsKey(d) ? copyFieldTargetCounts.get(d) + 1 : 1));
}
}
- private void registerDynamicCopyField( DynamicCopy dcopy )
- {
+ private void incrementCopyFieldTargetCount(SchemaField dest) {
+ copyFieldTargetCounts.put(dest, copyFieldTargetCounts.containsKey(dest) ? copyFieldTargetCounts.get(dest) + 1 : 1);
+ }
+
+ private void registerDynamicCopyField( DynamicCopy dcopy ) {
if( dynamicCopyFields == null ) {
dynamicCopyFields = new DynamicCopy[] {dcopy};
}
@@ -691,14 +737,7 @@ public final class IndexSchema {
temp[temp.length -1] = dcopy;
dynamicCopyFields = temp;
}
- log.trace("Dynamic Copy Field:" + dcopy );
- }
-
- private static Object[] append(Object[] orig, Object item) {
- Object[] newArr = (Object[])java.lang.reflect.Array.newInstance(orig.getClass().getComponentType(), orig.length+1);
- System.arraycopy(orig, 0, newArr, 0, orig.length);
- newArr[orig.length] = item;
- return newArr;
+ log.trace("Dynamic Copy Field:" + dcopy);
}
static SimilarityFactory readSimilarity(SolrResourceLoader loader, Node node) {
@@ -726,34 +765,58 @@ public final class IndexSchema {
}
- static abstract class DynamicReplacement implements Comparable<DynamicReplacement> {
- final static int STARTS_WITH=1;
- final static int ENDS_WITH=2;
+ public static abstract class DynamicReplacement implements Comparable<DynamicReplacement> {
+ abstract protected static class DynamicPattern {
+ protected final String regex;
+ protected final String fixedStr;
+
+ protected DynamicPattern(String regex, String fixedStr) { this.regex = regex; this.fixedStr = fixedStr; }
+
+ static DynamicPattern createPattern(String regex) {
+ if (regex.startsWith("*")) { return new NameEndsWith(regex); }
+ else if (regex.endsWith("*")) { return new NameStartsWith(regex); }
+ else { return new NameEquals(regex);
+ }
+ }
+
+ /** Returns true if the given name matches this pattern */
+ abstract boolean matches(String name);
- final String regex;
- final int type;
+ /** Returns the remainder of the given name after removing this pattern's fixed string component */
+ abstract String remainder(String name);
- final String str;
+ /** Returns the result of combining this pattern's fixed string component with the given replacement */
+ abstract String subst(String replacement);
+
+ /** Returns the length of the original regex, including the asterisk, if any. */
+ public int length() { return regex.length(); }
- protected DynamicReplacement(String regex) {
- this.regex = regex;
- if (regex.startsWith("*")) {
- type=ENDS_WITH;
- str=regex.substring(1);
+ private static class NameStartsWith extends DynamicPattern {
+ NameStartsWith(String regex) { super(regex, regex.substring(0, regex.length() - 1)); }
+ boolean matches(String name) { return name.startsWith(fixedStr); }
+ String remainder(String name) { return name.substring(fixedStr.length()); }
+ String subst(String replacement) { return fixedStr + replacement; }
}
- else if (regex.endsWith("*")) {
- type=STARTS_WITH;
- str=regex.substring(0,regex.length()-1);
+ private static class NameEndsWith extends DynamicPattern {
+ NameEndsWith(String regex) { super(regex, regex.substring(1)); }
+ boolean matches(String name) { return name.endsWith(fixedStr); }
+ String remainder(String name) { return name.substring(0, name.length() - fixedStr.length()); }
+ String subst(String replacement) { return replacement + fixedStr; }
}
- else {
- throw new RuntimeException("dynamic field name must start or end with *");
+ private static class NameEquals extends DynamicPattern {
+ NameEquals(String regex) { super(regex, regex); }
+ boolean matches(String name) { return regex.equals(name); }
+ String remainder(String name) { return ""; }
+ String subst(String replacement) { return fixedStr; }
}
}
- public boolean matches(String name) {
- if (type==STARTS_WITH && name.startsWith(str)) return true;
- else if (type==ENDS_WITH && name.endsWith(str)) return true;
- else return false;
+ protected DynamicPattern pattern;
+
+ public boolean matches(String name) { return pattern.matches(name); }
+
+ protected DynamicReplacement(String regex) {
+ pattern = DynamicPattern.createPattern(regex);
}
/**
@@ -765,18 +828,17 @@ public final class IndexSchema {
*/
@Override
public int compareTo(DynamicReplacement other) {
- return other.regex.length() - regex.length();
+ return other.pattern.length() - pattern.length();
+ }
+
+ /** Returns the regex used to create this instance's pattern */
+ public String getRegex() {
+ return pattern.regex;
}
}
- //
- // Instead of storing a type, this could be implemented as a hierarchy
- // with a virtual matches().
- // Given how often a search will be done, however, speed is the overriding
- // concern and I'm not sure which is faster.
- //
- final static class DynamicField extends DynamicReplacement {
+ public final static class DynamicField extends DynamicReplacement {
final SchemaField prototype;
DynamicField(SchemaField prototype) {
@@ -799,78 +861,47 @@ public final class IndexSchema {
}
}
- static class DynamicCopy extends DynamicReplacement {
- final SchemaField targetField;
- final int maxChars;
+ public static class DynamicCopy extends DynamicReplacement {
+ private final DynamicField destination;
+
+ private final int maxChars;
+ public int getMaxChars() { return maxChars; }
- DynamicCopy(String regex, SchemaField targetField) {
- this(regex, targetField, CopyField.UNLIMITED);
- }
+ final DynamicField sourceDynamicBase;
+ public DynamicField getSourceDynamicBase() { return sourceDynamicBase; }
- DynamicCopy(String regex, SchemaField targetField, int maxChars) {
- super(regex);
- this.targetField = targetField;
+ final DynamicField destDynamicBase;
+ public DynamicField getDestDynamicBase() { return destDynamicBase; }
+
+ DynamicCopy(String sourceRegex, DynamicField destination, int maxChars,
+ DynamicField sourceDynamicBase, DynamicField destDynamicBase) {
+ super(sourceRegex);
+ this.destination = destination;
this.maxChars = maxChars;
- }
-
- public SchemaField getTargetField( String sourceField )
- {
- return targetField;
+ this.sourceDynamicBase = sourceDynamicBase;
+ this.destDynamicBase = destDynamicBase;
}
- @Override
- public String toString() {
- return targetField.toString();
- }
- }
+ public String getDestFieldName() { return destination.getRegex(); }
- static class DynamicDestCopy extends DynamicCopy
- {
- final DynamicField dynamic;
-
- final int dtype;
- final String dstr;
-
- DynamicDestCopy(String source, DynamicField dynamic) {
- this(source, dynamic, CopyField.UNLIMITED);
- }
-
- DynamicDestCopy(String source, DynamicField dynamic, int maxChars) {
- super(source, dynamic.prototype, maxChars);
- this.dynamic = dynamic;
-
- String dest = dynamic.regex;
- if (dest.startsWith("*")) {
- dtype=ENDS_WITH;
- dstr=dest.substring(1);
- }
- else if (dest.endsWith("*")) {
- dtype=STARTS_WITH;
- dstr=dest.substring(0,dest.length()-1);
- }
- else {
- throw new RuntimeException("dynamic copyField destination name must start or end with *");
- }
- }
-
- @Override
- public SchemaField getTargetField( String sourceField )
- {
- String dyn = ( type==STARTS_WITH )
- ? sourceField.substring( str.length() )
- : sourceField.substring( 0, sourceField.length()-str.length() );
-
- String name = (dtype==STARTS_WITH) ? (dstr+dyn) : (dyn+dstr);
- return dynamic.makeSchemaField( name );
+ /**
+ * Generates a destination field name based on this source pattern,
+ * by substituting the remainder of this source pattern into the
+ * the given destination pattern.
+ */
+ public SchemaField getTargetField(String sourceField) {
+ String remainder = pattern.remainder(sourceField);
+ String targetFieldName = destination.pattern.subst(remainder);
+ return destination.makeSchemaField(targetFieldName);
}
+
@Override
public String toString() {
- return targetField.toString();
+ return destination.prototype.toString();
}
}
-
public SchemaField[] getDynamicFieldPrototypes() {
SchemaField[] df = new SchemaField[dynamicFields.length];
for (int i=0;i<dynamicFields.length;i++) {
@@ -881,25 +912,24 @@ public final class IndexSchema {
public String getDynamicPattern(String fieldName) {
for (DynamicField df : dynamicFields) {
- if (df.matches(fieldName)) return df.regex;
+ if (df.matches(fieldName)) return df.getRegex();
}
return null;
}
/**
- * Does the schema have the specified field defined explicitly, i.e.
- * not as a result of a copyField declaration with a wildcard? We
- * consider it explicitly defined if it matches a field or dynamicField
- * declaration.
+ * Does the schema explicitly define the specified field, i.e. not as a result
+ * of a copyField declaration? We consider it explicitly defined if it matches
+ * a field name or a dynamicField name.
* @return true if explicitly declared in the schema.
*/
public boolean hasExplicitField(String fieldName) {
- if(fields.containsKey(fieldName)) {
+ if (fields.containsKey(fieldName)) {
return true;
}
for (DynamicField df : dynamicFields) {
- if (df.matches(fieldName)) return true;
+ if (fieldName.equals(df.getRegex())) return true;
}
return false;
@@ -962,7 +992,7 @@ public final class IndexSchema {
/*** REMOVED -YCS
if (defaultFieldType != null) return new SchemaField(fieldName,defaultFieldType);
***/
- throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"undefined field: \""+fieldName+"\"");
+ throw new SolrException(ErrorCode.BAD_REQUEST,"undefined field: \""+fieldName+"\"");
}
/**
@@ -974,7 +1004,7 @@ public final class IndexSchema {
* </p>
*
* @param fieldName may be an explicitly created field, or a name that
- * excercies a dynamic field.
+ * excercises a dynamic field.
* @throws SolrException if no such field exists
* @see #getField(String)
* @see #getFieldTypeNoEx
@@ -1005,7 +1035,7 @@ public final class IndexSchema {
* </p>
*
* @param fieldName may be an explicitly created field, or a name that
- * excercies a dynamic field.
+ * exercises a dynamic field.
* @return null if field is not defined.
* @see #getField(String)
* @see #getFieldTypeNoEx
@@ -1022,7 +1052,7 @@ public final class IndexSchema {
* the specified field name
*
* @param fieldName may be an explicitly created field, or a name that
- * excercies a dynamic field.
+ * exercises a dynamic field.
* @throws SolrException if no such field exists
* @see #getField(String)
* @see #getFieldTypeNoEx
@@ -1031,7 +1061,7 @@ public final class IndexSchema {
for (DynamicField df : dynamicFields) {
if (df.matches(fieldName)) return df.prototype.getType();
}
- throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"undefined field "+fieldName);
+ throw new SolrException(ErrorCode.BAD_REQUEST,"undefined field "+fieldName);
}
private FieldType dynFieldType(String fieldName) {
@@ -1060,6 +1090,11 @@ public final class IndexSchema {
}
}
}
+ for (DynamicCopy dynamicCopy : dynamicCopyFields) {
+ if (dynamicCopy.getDestFieldName().equals(destField)) {
+ sf.add(getField(dynamicCopy.getRegex()));
+ }
+ }
return sf.toArray(new SchemaField[sf.size()]);
}
@@ -1078,8 +1113,7 @@ public final class IndexSchema {
}
}
List<CopyField> fixedCopyFields = copyFieldsMap.get(sourceField);
- if (fixedCopyFields != null)
- {
+ if (null != fixedCopyFields) {
result.addAll(fixedCopyFields);
}
@@ -1091,17 +1125,7 @@ public final class IndexSchema {
*
* @since solr 1.3
*/
- public boolean isCopyFieldTarget( SchemaField f )
- {
+ public boolean isCopyFieldTarget( SchemaField f ) {
return copyFieldTargetCounts.containsKey( f );
}
-
- /**
- * Is the given field name a wildcard? I.e. does it begin or end with *?
- * @return true/false
- */
- private static boolean isWildCard(String name) {
- return name.startsWith("*") || name.endsWith("*");
- }
-
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/SchemaField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/SchemaField.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/SchemaField.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/SchemaField.java Wed Mar 6 05:12:22 2013
@@ -20,10 +20,14 @@ package org.apache.solr.schema;
import org.apache.solr.common.SolrException;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.SortField;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.common.util.StrUtils;
import org.apache.solr.search.QParser;
import org.apache.solr.response.TextResponseWriter;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.IOException;
@@ -34,11 +38,18 @@ import java.io.IOException;
*
*/
public final class SchemaField extends FieldProperties {
+ private static final String FIELD_NAME = "name";
+ private static final String TYPE_NAME = "type";
+ private static final String DEFAULT_VALUE = "default";
+
final String name;
final FieldType type;
final int properties;
final String defaultValue;
boolean required = false; // this can't be final since it may be changed dynamically
+
+ /** Declared field property overrides */
+ Map<String,String> args = Collections.emptyMap();
/** Create a new SchemaField with the given name and type,
@@ -52,7 +63,8 @@ public final class SchemaField extends F
* of the properties of the prototype except the field name.
*/
public SchemaField(SchemaField prototype, String name) {
- this(name, prototype.type, prototype.properties, prototype.defaultValue );
+ this(name, prototype.type, prototype.properties, prototype.defaultValue);
+ args = prototype.args;
}
/** Create a new SchemaField with the given name and type,
@@ -186,10 +198,12 @@ public final class SchemaField extends F
static SchemaField create(String name, FieldType ft, Map<String,String> props) {
String defaultValue = null;
- if( props.containsKey( "default" ) ) {
- defaultValue = props.get( "default" );
+ if (props.containsKey(DEFAULT_VALUE)) {
+ defaultValue = props.get(DEFAULT_VALUE);
}
- return new SchemaField(name, ft, calcProps(name, ft, props), defaultValue );
+ SchemaField field = new SchemaField(name, ft, calcProps(name, ft, props), defaultValue);
+ field.args = new HashMap<String,String>(props);
+ return field;
}
/**
@@ -285,10 +299,51 @@ public final class SchemaField extends F
public boolean equals(Object obj) {
return(obj instanceof SchemaField) && name.equals(((SchemaField)obj).name);
}
-}
-
-
-
-
-
+ /**
+ * Get a map of property name -> value for this field. If showDefaults is true,
+ * include default properties (those inherited from the declared property type and
+ * not overridden in the field declaration).
+ */
+ public SimpleOrderedMap<Object> getNamedPropertyValues(boolean showDefaults) {
+ SimpleOrderedMap<Object> properties = new SimpleOrderedMap<Object>();
+ properties.add(FIELD_NAME, getName());
+ properties.add(TYPE_NAME, getType().getTypeName());
+ if (showDefaults) {
+ if (null != getDefaultValue()) {
+ properties.add(DEFAULT_VALUE, getDefaultValue());
+ }
+ properties.add(getPropertyName(INDEXED), indexed());
+ properties.add(getPropertyName(STORED), stored());
+ properties.add(getPropertyName(DOC_VALUES), hasDocValues());
+ properties.add(getPropertyName(STORE_TERMVECTORS), storeTermVector());
+ properties.add(getPropertyName(STORE_TERMPOSITIONS), storeTermPositions());
+ properties.add(getPropertyName(STORE_TERMOFFSETS), storeTermOffsets());
+ properties.add(getPropertyName(OMIT_NORMS), omitNorms());
+ properties.add(getPropertyName(OMIT_TF_POSITIONS), omitTermFreqAndPositions());
+ properties.add(getPropertyName(OMIT_POSITIONS), omitPositions());
+ properties.add(getPropertyName(STORE_OFFSETS), storeOffsetsWithPositions());
+ properties.add(getPropertyName(MULTIVALUED), multiValued());
+ if (sortMissingFirst()) {
+ properties.add(getPropertyName(SORT_MISSING_FIRST), sortMissingFirst());
+ } else if (sortMissingLast()) {
+ properties.add(getPropertyName(SORT_MISSING_LAST), sortMissingLast());
+ }
+ properties.add(getPropertyName(REQUIRED), isRequired());
+ properties.add(getPropertyName(TOKENIZED), isTokenized());
+ // The BINARY property is always false
+ // properties.add(getPropertyName(BINARY), isBinary());
+ } else {
+ for (Map.Entry<String,String> arg : args.entrySet()) {
+ String key = arg.getKey();
+ String value = arg.getValue();
+ if (key.equals(DEFAULT_VALUE)) {
+ properties.add(key, value);
+ } else {
+ properties.add(key, StrUtils.parseBool(value, false));
+ }
+ }
+ }
+ return properties;
+ }
+}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/TextField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/TextField.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/TextField.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/schema/TextField.java Wed Mar 6 05:12:22 2013
@@ -56,6 +56,7 @@ public class TextField extends FieldType
* @see #setMultiTermAnalyzer
*/
protected Analyzer multiTermAnalyzer=null;
+ private boolean isExplicitMultiTermAnalyzer = false;
@Override
protected void init(IndexSchema schema, Map<String,String> args) {
@@ -329,4 +330,11 @@ public class TextField extends FieldType
}
+ public void setIsExplicitMultiTermAnalyzer(boolean isExplicitMultiTermAnalyzer) {
+ this.isExplicitMultiTermAnalyzer = isExplicitMultiTermAnalyzer;
+ }
+
+ public boolean isExplicitMultiTermAnalyzer() {
+ return isExplicitMultiTermAnalyzer;
+ }
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java Wed Mar 6 05:12:22 2013
@@ -23,8 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -248,6 +246,20 @@ public class SolrDispatchFilter implemen
parsers.put(config, parser );
}
+ // Handle /schema/* paths via Restlet
+ if( path.startsWith("/schema") ) {
+ solrReq = parser.parse(core, path, req);
+ SolrRequestInfo.setRequestInfo(new SolrRequestInfo(solrReq, new SolrQueryResponse()));
+ if( path.equals(req.getServletPath()) ) {
+ // avoid endless loop - pass through to Restlet via webapp
+ chain.doFilter(request, response);
+ } else {
+ // forward rewritten URI (without path prefix and core/collection name) to Restlet
+ req.getRequestDispatcher(path).forward(request, response);
+ }
+ return;
+ }
+
// Determine the handler from the url path if not set
// (we might already have selected the cores handler)
if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path
@@ -353,13 +365,17 @@ public class SolrDispatchFilter implemen
try {
con.connect();
- InputStream is = req.getInputStream();
- OutputStream os = con.getOutputStream();
- try {
- IOUtils.copyLarge(is, os);
- } finally {
- IOUtils.closeQuietly(os);
- IOUtils.closeQuietly(is); // TODO: I thought we weren't supposed to explicitly close servlet streams
+ InputStream is;
+ OutputStream os;
+ if ("POST".equals(req.getMethod())) {
+ is = req.getInputStream();
+ os = con.getOutputStream(); // side effect: method is switched to POST
+ try {
+ IOUtils.copyLarge(is, os);
+ } finally {
+ IOUtils.closeQuietly(os);
+ IOUtils.closeQuietly(is); // TODO: I thought we weren't supposed to explicitly close servlet streams
+ }
}
resp.setStatus(con.getResponseCode());
@@ -491,19 +507,11 @@ public class SolrDispatchFilter implemen
private void handleAdminRequest(HttpServletRequest req, ServletResponse response, SolrRequestHandler handler,
SolrQueryRequest solrReq) throws IOException {
SolrQueryResponse solrResp = new SolrQueryResponse();
- final NamedList<Object> responseHeader = new SimpleOrderedMap<Object>();
- solrResp.add("responseHeader", responseHeader);
- NamedList toLog = solrResp.getToLog();
- toLog.add("webapp", req.getContextPath());
- toLog.add("path", solrReq.getContext().get("path"));
- toLog.add("params", "{" + solrReq.getParamString() + "}");
+ SolrCore.preDecorateResponse(solrReq, solrResp);
handler.handleRequest(solrReq, solrResp);
- SolrCore.setResponseHeaderValues(handler, solrReq, solrResp);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < toLog.size(); i++) {
- String name = toLog.getName(i);
- Object val = toLog.getVal(i);
- sb.append(name).append("=").append(val).append(" ");
+ SolrCore.postDecorateResponse(handler, solrReq, solrResp);
+ if (log.isInfoEnabled() && solrResp.getToLog().size() > 0) {
+ log.info(solrResp.getToLogAsString("[admin] "));
}
QueryResponseWriter respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get(solrReq.getParams().get(CommonParams.WT));
if (respWriter == null) respWriter = SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard");
@@ -521,7 +529,7 @@ public class SolrDispatchFilter implemen
if (solrRsp.getException() != null) {
NamedList info = new SimpleOrderedMap();
- int code = getErrorInfo(solrRsp.getException(),info);
+ int code = ResponseUtils.getErrorInfo(solrRsp.getException(), info, log);
solrRsp.add("error", info);
((HttpServletResponse) response).setStatus(code);
}
@@ -543,38 +551,6 @@ public class SolrDispatchFilter implemen
//else http HEAD request, nothing to write out, waited this long just to get ContentType
}
- protected int getErrorInfo(Throwable ex, NamedList info) {
- int code=500;
- if( ex instanceof SolrException ) {
- code = ((SolrException)ex).code();
- }
-
- String msg = null;
- for (Throwable th = ex; th != null; th = th.getCause()) {
- msg = th.getMessage();
- if (msg != null) break;
- }
- if(msg != null) {
- info.add("msg", msg);
- }
-
- // For any regular code, don't include the stack trace
- if( code == 500 || code < 100 ) {
- StringWriter sw = new StringWriter();
- ex.printStackTrace(new PrintWriter(sw));
- SolrException.log(log, null, ex);
- info.add("trace", sw.toString());
-
- // non standard codes have undefined results with various servers
- if( code < 100 ) {
- log.warn( "invalid return code: "+code );
- code = 500;
- }
- }
- info.add("code", new Integer(code));
- return code;
- }
-
protected void execute( HttpServletRequest req, SolrRequestHandler handler, SolrQueryRequest sreq, SolrQueryResponse rsp) {
// a custom filter could add more stuff to the request before passing it on.
// for example: sreq.getContext().put( "HttpServletRequest", req );
@@ -615,7 +591,7 @@ public class SolrDispatchFilter implemen
}
catch( Throwable t ) { // This error really does not matter
SimpleOrderedMap info = new SimpleOrderedMap();
- int code=getErrorInfo(ex, info);
+ int code = ResponseUtils.getErrorInfo(ex, info, log);
response.sendError( code, info.toString() );
}
}
Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/update/processor/LogUpdateProcessorFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/update/processor/LogUpdateProcessorFactory.java?rev=1453162&r1=1453161&r2=1453162&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/update/processor/LogUpdateProcessorFactory.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/update/processor/LogUpdateProcessorFactory.java Wed Mar 6 05:12:22 2013
@@ -179,35 +179,24 @@ class LogUpdateProcessor extends UpdateR
if (next != null) next.finish();
// LOG A SUMMARY WHEN ALL DONE (INFO LEVEL)
-
+ if (log.isInfoEnabled()) {
+ StringBuilder sb = new StringBuilder(rsp.getToLogAsString(req.getCore().getLogId()));
- NamedList<Object> stdLog = rsp.getToLog();
+ rsp.getToLog().clear(); // make it so SolrCore.exec won't log this again
- StringBuilder sb = new StringBuilder(req.getCore().getLogId());
-
- for (int i=0; i<stdLog.size(); i++) {
- String name = stdLog.getName(i);
- Object val = stdLog.getVal(i);
- if (name != null) {
- sb.append(name).append('=');
+ // if id lists were truncated, show how many more there were
+ if (adds != null && numAdds > maxNumToLog) {
+ adds.add("... (" + numAdds + " adds)");
}
- sb.append(val).append(' ');
- }
-
- stdLog.clear(); // make it so SolrCore.exec won't log this again
+ if (deletes != null && numDeletes > maxNumToLog) {
+ deletes.add("... (" + numDeletes + " deletes)");
+ }
+ long elapsed = rsp.getEndTime() - req.getStartTime();
- // if id lists were truncated, show how many more there were
- if (adds != null && numAdds > maxNumToLog) {
- adds.add("... (" + numAdds + " adds)");
- }
- if (deletes != null && numDeletes > maxNumToLog) {
- deletes.add("... (" + numDeletes + " deletes)");
+ sb.append(toLog).append(" 0 ").append(elapsed);
+ log.info(sb.toString());
}
- long elapsed = rsp.getEndTime() - req.getStartTime();
-
- sb.append(toLog).append(" 0 ").append(elapsed);
- log.info(sb.toString());
}
}