You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by yo...@apache.org on 2008/02/26 20:47:10 UTC
svn commit: r631357 [2/2] - in /lucene/solr/trunk: ./
client/java/solrj/src/org/apache/solr/client/solrj/response/
src/java/org/apache/solr/common/util/ src/java/org/apache/solr/handler/
src/java/org/apache/solr/handler/component/ src/java/org/apache/s...
Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/component/SearchHandler.java Tue Feb 26 11:47:07 2008
@@ -17,41 +17,45 @@
package org.apache.solr.handler.component;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.RTimer;
-import org.apache.solr.core.SolrCore;
-import org.apache.solr.handler.RequestHandlerBase;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.SolrException;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
+import org.apache.solr.client.solrj.SolrServer;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.util.plugin.SolrCoreAware;
+import org.apache.solr.core.SolrCore;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.HttpClient;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.logging.Logger;
+import java.util.*;
+import java.util.concurrent.*;
/**
*
* Refer SOLR-281
*
- * @since solr 1.3
*/
public class SearchHandler extends RequestHandlerBase implements SolrCoreAware
{
- static final String RESPONSE_BUILDER_CONTEXT_KEY = "ResponseBuilder";
-
static final String INIT_COMPONENTS = "components";
static final String INIT_FIRST_COMPONENTS = "first-components";
static final String INIT_LAST_COMPONENTS = "last-components";
-
+
protected static Logger log = Logger.getLogger(SearchHandler.class.getName());
-
+
protected List<SearchComponent> components = null;
protected NamedList initArgs = null;
-
+
@Override
public void init(NamedList args) {
super.init( args );
@@ -73,7 +77,7 @@
* Initialize the components based on name
*/
@SuppressWarnings("unchecked")
- public void inform(SolrCore core)
+ public void inform(SolrCore core)
{
Object declaredComponents = initArgs.get(INIT_COMPONENTS);
List<String> first = (List<String>) initArgs.get(INIT_FIRST_COMPONENTS);
@@ -83,13 +87,13 @@
if( declaredComponents == null ) {
// Use the default component list
list = getDefaultComponets();
-
+
if( first != null ) {
List<String> clist = first;
clist.addAll( list );
list = clist;
}
-
+
if( last != null ) {
list.addAll( last );
}
@@ -101,7 +105,7 @@
"First/Last components only valid if you do not declare 'components'");
}
}
-
+
// Build the component list
components = new ArrayList<SearchComponent>( list.size() );
for(String c : list){
@@ -115,71 +119,147 @@
return components;
}
- public static ResponseBuilder getResponseBuilder(SolrQueryRequest req)
- {
- return (ResponseBuilder) req.getContext().get( RESPONSE_BUILDER_CONTEXT_KEY );
- }
-
- //---------------------------------------------------------------------------------------
- // SolrRequestHandler
- //---------------------------------------------------------------------------------------
-
+
@Override
- public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException, InstantiationException, IllegalAccessException
+ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception, ParseException, InstantiationException, IllegalAccessException
{
- ResponseBuilder builder = new ResponseBuilder();
- req.getContext().put( RESPONSE_BUILDER_CONTEXT_KEY, builder );
-
- if( components == null ) {
- throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
- "SearchHandler not initialized properly. No components registered." );
- }
-
- // The semantics of debugging vs not debugging are distinct enough
- // to justify two control loops
- if( !req.getParams().getBool( CommonParams.DEBUG_QUERY, false ) ) {
- // Prepare
- for( SearchComponent c : components ) {
- c.prepare( req, rsp );
- }
-
- // Process
+ ResponseBuilder rb = new ResponseBuilder();
+ rb.req = req;
+ rb.rsp = rsp;
+ rb.components = components;
+ rb.setDebug(req.getParams().getBool(CommonParams.DEBUG_QUERY, false));
+
+ final RTimer timer = rb.isDebug() ? new RTimer() : null;
+
+ if (timer == null) {
+ // non-debugging prepare phase
for( SearchComponent c : components ) {
- c.process( req, rsp );
+ c.prepare(rb);
}
- }
- else {
- builder.setDebug( true );
- RTimer timer = new RTimer();
-
- // Prepare
+ } else {
+ // debugging prepare phase
RTimer subt = timer.sub( "prepare" );
for( SearchComponent c : components ) {
- builder.setTimer( subt.sub( c.getName() ) );
- c.prepare( req, rsp );
- builder.getTimer().stop();
+ rb.setTimer( subt.sub( c.getName() ) );
+ c.prepare(rb);
+ rb.getTimer().stop();
}
subt.stop();
-
- // Process
- subt = timer.sub( "process" );
- for( SearchComponent c : components ) {
- builder.setTimer( subt.sub( c.getName() ) );
- c.process( req, rsp );
- builder.getTimer().stop();
+ }
+
+ if (rb.shards == null) {
+ // a normal non-distributed request
+
+ // The semantics of debugging vs not debugging are different enough that
+ // it makes sense to have two control loops
+ if(!rb.isDebug()) {
+ // Process
+ for( SearchComponent c : components ) {
+ c.process(rb);
+ }
}
- subt.stop();
- timer.stop();
-
- // add the timing info
- builder.addDebugInfo( "timing", timer.asNamedList() );
+ else {
+ // Process
+ RTimer subt = timer.sub( "process" );
+ for( SearchComponent c : components ) {
+ rb.setTimer( subt.sub( c.getName() ) );
+ c.process(rb);
+ rb.getTimer().stop();
+ }
+ subt.stop();
+ timer.stop();
+
+ // add the timing info
+ if( rb.getDebugInfo() == null ) {
+ rb.setDebugInfo( new SimpleOrderedMap<Object>() );
+ }
+ rb.getDebugInfo().add( "timing", timer.asNamedList() );
+ }
+
+ } else {
+ // a distributed request
+
+ HttpCommComponent comm = new HttpCommComponent();
+
+ if (rb.outgoing == null) {
+ rb.outgoing = new LinkedList<ShardRequest>();
+ }
+ rb.finished = new ArrayList<ShardRequest>();
+
+ int nextStage = 0;
+ do {
+ rb.stage = nextStage;
+ nextStage = ResponseBuilder.STAGE_DONE;
+
+ // call all components
+ for( SearchComponent c : components ) {
+ // the next stage is the minimum of what all components report
+ nextStage = Math.min(nextStage, c.distributedProcess(rb));
+ }
+
+
+ // check the outgoing queue and send requests
+ while (rb.outgoing.size() > 0) {
+
+ // submit all current request tasks at once
+ while (rb.outgoing.size() > 0) {
+ ShardRequest sreq = rb.outgoing.remove(0);
+ sreq.actualShards = sreq.shards;
+ if (sreq.actualShards==ShardRequest.ALL_SHARDS) {
+ sreq.actualShards = rb.shards;
+ }
+ sreq.responses = new ArrayList<ShardResponse>();
+
+ // TODO: map from shard to address[]
+ for (String shard : sreq.actualShards) {
+ ModifiableSolrParams params = sreq.params;
+ params.remove("shards"); // not a top-level request
+ params.remove("indent");
+ params.remove("echoParams");
+ params.set("isShard", true); // a sub (shard) request
+ comm.submit(sreq, shard, params);
+ }
+ }
+
+
+ // now wait for replies, but if anyone puts more requests on
+ // the outgoing queue, send them out immediately (by exiting
+ // this loop)
+ while (rb.outgoing.size() == 0) {
+ ShardResponse srsp = comm.takeCompletedOrError();
+ if (srsp == null) break; // no more requests to wait for
+
+ // Was there an exception? If so, abort everything and
+ // rethrow
+ if (srsp.exception != null) {
+ comm.cancelAll();
+ if (srsp.exception instanceof SolrException) {
+ throw (SolrException)srsp.exception;
+ } else {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, srsp.exception);
+ }
+ }
+
+ rb.finished.add(srsp.req);
+
+ // let the components see the responses to the request
+ for(SearchComponent c : components) {
+ c.handleResponses(rb, srsp.req);
+ }
+ }
+ }
+
+ for(SearchComponent c : components) {
+ c.finishStage(rb);
+ }
+
+ // we are done when the next stage is MAX_VALUE
+ } while (nextStage != Integer.MAX_VALUE);
}
}
- //---------------------------------------------------------------------------------------
- // SolrInfoMBeans
- //---------------------------------------------------------------------------------------
-
+ //////////////////////// SolrInfoMBeans methods //////////////////////
+
@Override
public String getDescription() {
StringBuilder sb = new StringBuilder();
@@ -205,4 +285,140 @@
public String getSource() {
return "$URL$";
}
+}
+
+
+// TODO: generalize how a comm component can fit into search component framework
+// TODO: statics should be per-core singletons
+
+class HttpCommComponent {
+
+ // We want an executor that doesn't take up any resources if
+ // it's not used, so it could be created statically for
+ // the distributed search component if desired.
+ //
+ // Consider CallerRuns policy and a lower max threads to throttle
+ // requests at some point (or should we simply return failure?)
+ static Executor commExecutor = new ThreadPoolExecutor(
+ 0,
+ Integer.MAX_VALUE,
+ 5, TimeUnit.SECONDS, // terminate idle threads after 5 sec
+ new SynchronousQueue<Runnable>() // directly hand off tasks
+ );
+
+
+ static HttpClient client;
+
+ static {
+ MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
+ mgr.getParams().setDefaultMaxConnectionsPerHost(20);
+ mgr.getParams().setMaxTotalConnections(10000);
+ // mgr.getParams().setStaleCheckingEnabled(false);
+ client = new HttpClient(mgr);
+ }
+
+ CompletionService<ShardResponse> completionService = new ExecutorCompletionService<ShardResponse>(commExecutor);
+ Set<Future<ShardResponse>> pending = new HashSet<Future<ShardResponse>>();
+
+ HttpCommComponent() {
+ }
+
+ void submit(final ShardRequest sreq, final String shard, final ModifiableSolrParams params) {
+ Callable<ShardResponse> task = new Callable<ShardResponse>() {
+ public ShardResponse call() throws Exception {
+
+ ShardResponse srsp = new ShardResponse();
+ srsp.req = sreq;
+ srsp.shard = shard;
+
+ try {
+ // String url = "http://" + shard + "/select";
+ String url = "http://" + shard;
+
+ params.remove("wt"); // use default (or should we explicitly set it?)
+ params.remove("version");
+
+ SolrServer server = new CommonsHttpSolrServer(url, client);
+ // SolrRequest req = new SolrRequest(SolrRequest.METHOD.GET, "/select");
+ // use generic request to avoid extra processing of queries
+ // QueryRequest req = new QueryRequest(sreq.params);
+ // srsp.rsp = server.request(req);
+ srsp.rsp = server.query(sreq.params);
+ } catch (Throwable th) {
+ srsp.exception = th;
+ if (th instanceof SolrException) {
+ srsp.rspCode = ((SolrException)th).code();
+ } else {
+ srsp.rspCode = -1;
+ }
+ }
+
+ return srsp;
+ }
+ };
+
+ pending.add( completionService.submit(task) );
+ }
+
+ /** returns a ShardResponse of the last response correlated with a ShardRequest */
+ ShardResponse take() {
+ while (pending.size() > 0) {
+ try {
+ Future<ShardResponse> future = completionService.take();
+ pending.remove(future);
+ ShardResponse rsp = future.get();
+ rsp.req.responses.add(rsp);
+ if (rsp.req.responses.size() == rsp.req.actualShards.length) {
+ return rsp;
+ }
+ } catch (InterruptedException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ } catch (ExecutionException e) {
+ // should be impossible... the problem with catching the exception
+ // at this level is we don't know what ShardRequest it applied to
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Impossible Exception",e);
+ }
+ }
+ return null;
+ }
+
+
+ /** returns a ShardResponse of the last response correlated with a ShardRequest,
+ * or immediately returns a ShardResponse if there was an error detected
+ */
+ ShardResponse takeCompletedOrError() {
+ while (pending.size() > 0) {
+ try {
+ Future<ShardResponse> future = completionService.take();
+ pending.remove(future);
+ ShardResponse rsp = future.get();
+ if (rsp.exception != null) return rsp; // if exception, return immediately
+ // add response to the response list... we do this after the take() and
+ // not after the completion of "call" so we know when the last response
+ // for a request was received. Otherwise we might return the same
+ // request more than once.
+ rsp.req.responses.add(rsp);
+ if (rsp.req.responses.size() == rsp.req.actualShards.length) {
+ return rsp;
+ }
+ } catch (InterruptedException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ } catch (ExecutionException e) {
+ // should be impossible... the problem with catching the exception
+ // at this level is we don't know what ShardRequest it applied to
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Impossible Exception",e);
+ }
+ }
+ return null;
+ }
+
+
+ void cancelAll() {
+ for (Future<ShardResponse> future : pending) {
+ // TODO: any issues with interrupting? shouldn't be if
+ // there are finally blocks to release connections.
+ future.cancel(true);
+ }
+ }
+
}
Added: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java?rev=631357&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java Tue Feb 26 11:47:07 2008
@@ -0,0 +1,269 @@
+package org.apache.solr.handler.component;
+
+import org.apache.lucene.search.SortComparatorSource;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.util.PriorityQueue;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.search.MissingStringLastComparatorSource;
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.List;
+import java.util.ArrayList;
+
+public class ShardDoc {
+ public String shard;
+ public String shardAddress; // TODO
+
+ int orderInShard;
+ // the position of this doc within the shard... this can be used
+ // to short-circuit comparisons if the shard is equal, and can
+ // also be used to break ties within the same shard.
+
+ Object id;
+ // this is currently the uniqueKeyField but
+ // may be replaced with internal docid in a future release.
+
+ Float score;
+
+ NamedList sortFieldValues;
+ // sort field values for *all* docs in a particular shard.
+ // this doc's values are in position orderInShard
+
+ // TODO: store the SolrDocument here?
+ // Store the order in the merged list for lookup when getting stored fields?
+ // (other components need this ordering to store data in order, like highlighting)
+ // but we shouldn't expose uniqueKey (have a map by it) until the stored-field
+ // retrieval stage.
+
+ int positionInResponse;
+ // the ordinal position in the merged response arraylist
+
+ public String toString(){
+ return "id="+id
+ +" ,score="+score
+ +" ,shard="+shard
+ +" ,orderInShard="+orderInShard
+ +" ,positionInResponse="+positionInResponse
+ +" ,sortFieldValues="+sortFieldValues;
+ }
+}
+
+
+
+// used by distributed search to merge results.
+class ShardFieldSortedHitQueue extends PriorityQueue {
+
+ /** Stores a comparator corresponding to each field being sorted by */
+ protected Comparator[] comparators;
+
+ /** Stores the sort criteria being used. */
+ protected SortField[] fields;
+
+ /** The order of these fieldNames should correspond to the order of sort field values retrieved from the shard */
+ protected List<String> fieldNames = new ArrayList<String>();
+
+ public ShardFieldSortedHitQueue(SortField[] fields, int size) {
+ final int n = fields.length;
+ comparators = new Comparator[n];
+ this.fields = new SortField[n];
+ for (int i = 0; i < n; ++i) {
+
+ // keep track of the named fields
+ int type = fields[i].getType();
+ if (type!=SortField.SCORE && type!=SortField.DOC) {
+ fieldNames.add(fields[i].getField());
+ }
+
+ String fieldname = fields[i].getField();
+ comparators[i] = getCachedComparator(fieldname, fields[i]
+ .getType(), fields[i].getLocale(), fields[i].getFactory());
+
+ if (fields[i].getType() == SortField.STRING) {
+ this.fields[i] = new SortField(fieldname, fields[i].getLocale(),
+ fields[i].getReverse());
+ } else {
+ this.fields[i] = new SortField(fieldname, fields[i].getType(),
+ fields[i].getReverse());
+ }
+
+ //System.out.println("%%%%%%%%%%%%%%%%%% got "+fields[i].getType() +" for "+ fieldname +" fields[i].getReverse(): "+fields[i].getReverse());
+ }
+
+ initialize(size);
+ }
+
+ @Override
+ protected boolean lessThan(Object objA, Object objB) {
+ ShardDoc docA = (ShardDoc)objA;
+ ShardDoc docB = (ShardDoc)objB;
+
+ // If these docs are from the same shard, then the relative order
+ // is how they appeared in the response from that shard.
+ if (docA.shard == docB.shard) {
+ // if docA has a smaller position, it should be "larger" so it
+ // comes before docB.
+ // This will handle sorting by docid within the same shard
+
+ // comment this out to test comparators.
+ return !(docA.orderInShard < docB.orderInShard);
+ }
+
+
+ // run comparators
+ final int n = comparators.length;
+ int c = 0;
+ for (int i = 0; i < n && c == 0; i++) {
+ c = (fields[i].getReverse()) ? comparators[i].compare(docB, docA)
+ : comparators[i].compare(docA, docB);
+ }
+
+ // solve tiebreaks by comparing shards (similar to using docid)
+ // smaller docid's beat larger ids, so reverse the natural ordering
+ if (c == 0) {
+ c = -docA.shard.compareTo(docB.shard);
+ }
+
+ return c < 0;
+ }
+
+ Comparator getCachedComparator(String fieldname, int type, Locale locale, SortComparatorSource factory) {
+ Comparator comparator = null;
+ switch (type) {
+ case SortField.SCORE:
+ comparator = comparatorScore(fieldname);
+ break;
+ case SortField.STRING:
+ if (locale != null)
+ comparator = comparatorStringLocale(fieldname, locale);
+ else
+ comparator = comparatorNatural(fieldname);
+ break;
+ case SortField.CUSTOM:
+ if (factory instanceof MissingStringLastComparatorSource){
+ comparator = comparatorMissingStringLast(fieldname);
+ } else {
+ // TODO: support other types such as random... is there a way to
+ // support generically? Perhaps just comparing Object
+ comparator = comparatorNatural(fieldname);
+ // throw new RuntimeException("Custom sort not supported factory is "+factory.getClass());
+ }
+ break;
+ case SortField.DOC:
+ // TODO: we can support this!
+ throw new RuntimeException("Doc sort not supported");
+ default:
+ comparator = comparatorNatural(fieldname);
+ break;
+ }
+ return comparator;
+ }
+
+ class ShardComparator implements Comparator {
+ String fieldName;
+ int fieldNum;
+ public ShardComparator(String fieldName) {
+ this.fieldName = fieldName;
+ this.fieldNum=0;
+ for (int i=0; i<fieldNames.size(); i++) {
+ if (fieldNames.get(i).equals(fieldName)) {
+ this.fieldNum = i;
+ break;
+ }
+ }
+ }
+
+ Object sortVal(ShardDoc shardDoc) {
+ assert(shardDoc.sortFieldValues.getName(fieldNum).equals(fieldName));
+ List lst = (List)shardDoc.sortFieldValues.getVal(fieldNum);
+ return lst.get(shardDoc.orderInShard);
+ }
+
+ public int compare(Object o1, Object o2) {
+ return 0;
+ }
+ }
+
+ static Comparator comparatorScore(final String fieldName) {
+ return new Comparator() {
+ public final int compare(final Object o1, final Object o2) {
+ ShardDoc e1 = (ShardDoc) o1;
+ ShardDoc e2 = (ShardDoc) o2;
+
+ final float f1 = e1.score;
+ final float f2 = e2.score;
+ if (f1 < f2)
+ return -1;
+ if (f1 > f2)
+ return 1;
+ return 0;
+ }
+ };
+ }
+
+ // The lucene natural sort ordering corresponds to numeric
+ // and string natural sort orderings (ascending). Since
+ // the PriorityQueue keeps the biggest elements by default,
+ // we need to reverse the natural compare ordering so that the
+ // smallest elements are kept instead of the largest... hence
+ // the negative sign on the final compareTo().
+ Comparator comparatorNatural(String fieldName) {
+ return new ShardComparator(fieldName) {
+ public final int compare(final Object o1, final Object o2) {
+ ShardDoc sd1 = (ShardDoc) o1;
+ ShardDoc sd2 = (ShardDoc) o2;
+ Comparable v1 = (Comparable)sortVal(sd1);
+ Comparable v2 = (Comparable)sortVal(sd2);
+ if (v1==v2)
+ return 0;
+ if (v1==null)
+ return 1;
+ if(v2==null)
+ return -1;
+ return -v1.compareTo(v2);
+ }
+ };
+ }
+
+
+ Comparator comparatorStringLocale(final String fieldName,
+ Locale locale) {
+ final Collator collator = Collator.getInstance(locale);
+ return new ShardComparator(fieldName) {
+ public final int compare(final Object o1, final Object o2) {
+ ShardDoc sd1 = (ShardDoc) o1;
+ ShardDoc sd2 = (ShardDoc) o2;
+ Comparable v1 = (Comparable)sortVal(sd1);
+ Comparable v2 = (Comparable)sortVal(sd2);
+ if (v1==v2)
+ return 0;
+ if (v1==null)
+ return 1;
+ if(v2==null)
+ return -1;
+ return -collator.compare(v1,v2);
+ }
+ };
+ }
+
+
+ Comparator comparatorMissingStringLast(final String fieldName) {
+ return new ShardComparator(fieldName) {
+ public final int compare(final Object o1, final Object o2) {
+ ShardDoc sd1 = (ShardDoc) o1;
+ ShardDoc sd2 = (ShardDoc) o2;
+ Comparable v1 = (Comparable)sortVal(sd1);
+ Comparable v2 = (Comparable)sortVal(sd2);
+ if (v1==v2)
+ return 0;
+ if (v1==null)
+ return -1;
+ if(v2==null)
+ return 1;
+ return -v1.compareTo(v2);
+ }
+ };
+ }
+
+}
\ No newline at end of file
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardDoc.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java?rev=631357&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java Tue Feb 26 11:47:07 2008
@@ -0,0 +1,70 @@
+package org.apache.solr.handler.component;
+
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.NamedList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+// todo... when finalized make accessors
+public class ShardRequest {
+ public final static String[] ALL_SHARDS = null;
+
+ public final static int PURPOSE_PRIVATE = 0x01;
+ public final static int PURPOSE_GET_TERM_DFS = 0x02;
+ public final static int PURPOSE_GET_TOP_IDS = 0x04;
+ public final static int PURPOSE_REFINE_TOP_IDS = 0x08;
+ public final static int PURPOSE_GET_FACETS = 0x10;
+ public final static int PURPOSE_REFINE_FACETS = 0x20;
+ public final static int PURPOSE_GET_FIELDS = 0x40;
+ public final static int PURPOSE_GET_HIGHLIGHTS = 0x80;
+ public final static int PURPOSE_GET_DEBUG =0x100;
+
+ public int purpose; // the purpose of this request
+
+ public String[] shards; // the shards this request should be sent to, null for all
+// TODO: how to request a specific shard address?
+
+
+ public ModifiableSolrParams params;
+
+
+ /** list of responses... filled out by framework */
+ public List<ShardResponse> responses = new ArrayList<ShardResponse>();
+
+ /** actual shards to send the request to, filled out by framework */
+ public String[] actualShards;
+
+ // TODO: one could store a list of numbers to correlate where returned docs
+ // go in the top-level response rather than looking up by id...
+ // this would work well if we ever transitioned to using internal ids and
+ // didn't require a uniqueId
+
+ public String toString() {
+ return "ShardRequest:{params=" + params
+ + ", purpose=" + Integer.toHexString(purpose)
+ + ", nResponses =" + responses.size()
+ + "}";
+ }
+}
+
+
+class ShardResponse {
+ public ShardRequest req;
+ public String shard;
+ public String shardAddress; // the specific shard that this response was received from
+ public int rspCode;
+ public Throwable exception;
+ public SolrResponse rsp;
+
+ public String toString() {
+ return "ShardResponse:{shard="+shard+",shardAddress="+shardAddress
+ +"\n\trequest=" + req
+ +"\n\tresponse=" + rsp
+ + (exception==null ? "" : "\n\texception="+ SolrException.toStr(exception))
+ +"\n}";
+ }
+}
\ No newline at end of file
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: lucene/solr/trunk/src/java/org/apache/solr/handler/component/ShardRequest.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Modified: lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java Tue Feb 26 11:47:07 2008
@@ -62,12 +62,13 @@
protected SolrParams params;
/** Searcher to use for all calculations */
protected SolrIndexSearcher searcher;
+ protected SolrQueryRequest req;
-
- public SimpleFacets(SolrIndexSearcher searcher,
+ public SimpleFacets(SolrQueryRequest req,
DocSet docs,
SolrParams params) {
- this.searcher = searcher;
+ this.req = req;
+ this.searcher = req.getSearcher();
this.docs = docs;
this.params = params;
}
@@ -117,12 +118,13 @@
* If user doesn't want schema default for facet.query, they should be
* explicit.
*/
- SolrQueryParser qp = searcher.getSchema().getSolrQueryParser(null);
+ // SolrQueryParser qp = searcher.getSchema().getSolrQueryParser(null);
String[] facetQs = params.getParams(FacetParams.FACET_QUERY);
if (null != facetQs && 0 != facetQs.length) {
for (String q : facetQs) {
- res.add(q, searcher.numDocs(qp.parse(q), docs));
+ Query qobj = QParser.getParser(q, null, req).getQuery();
+ res.add(q, searcher.numDocs(qobj, docs));
}
}
Modified: lucene/solr/trunk/src/java/org/apache/solr/request/XMLWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/XMLWriter.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/XMLWriter.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/XMLWriter.java Tue Feb 26 11:47:07 2008
@@ -473,6 +473,7 @@
SolrIndexSearcher searcher = request.getSearcher();
DocIterator iterator = ids.iterator();
int sz = ids.size();
+ includeScore = includeScore && ids.hasScores();
for (int i=0; i<sz; i++) {
int id = iterator.nextDoc();
Document doc = searcher.doc(id, fields);
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java Tue Feb 26 11:47:07 2008
@@ -33,7 +33,7 @@
*/
// move to apache package and make public if it is accepted as a patch
-class MissingStringLastComparatorSource implements SortComparatorSource {
+public class MissingStringLastComparatorSource implements SortComparatorSource {
public static final String bigString="\uffff\uffff\uffff\uffff\uffff\uffff\uffff\uffffNULL_VAL";
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/QParser.java Tue Feb 26 11:47:07 2008
@@ -197,7 +197,7 @@
QParserPlugin qplug = req.getCore().getQueryPlugin(type);
return qplug.createParser(qstr, localParams, req.getParams(), req);
- }
+ }
}
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java Tue Feb 26 11:47:07 2008
@@ -519,8 +519,15 @@
private static Query matchAllDocsQuery = new MatchAllDocsQuery();
-
- protected DocSet getDocSet(List<Query> queries) throws IOException {
+ /**
+ * Returns the set of document ids matching all queries.
+ * This method is cache-aware and attempts to retrieve the answer from the cache if possible.
+ * If the answer was not cached, it may have been inserted into the cache as a result of this call.
+ * This method can handle negative queries.
+ * <p>
+ * The DocSet returned should <b>not</b> be modified.
+ */
+ public DocSet getDocSet(List<Query> queries) throws IOException {
if (queries==null) return null;
if (queries.size()==1) return getDocSet(queries.get(0));
DocSet answer=null;
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/SortSpec.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/SortSpec.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/SortSpec.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/SortSpec.java Tue Feb 26 11:47:07 2008
@@ -18,6 +18,7 @@
package org.apache.solr.search;
import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
/***
* SortSpec encapsulates a Lucene Sort and a count of the number of documents
@@ -42,6 +43,18 @@
public void setSort( Sort s )
{
sort = s;
+ }
+
+ public static boolean includesScore(Sort sort) {
+ if (sort==null) return true;
+ for (SortField sf : sort.getSort()) {
+ if (sf.getType() == SortField.SCORE) return true;
+ }
+ return false;
+ }
+
+ public boolean includesScore() {
+ return includesScore(sort);
}
/**
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/Sorting.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/Sorting.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/Sorting.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/Sorting.java Tue Feb 26 11:47:07 2008
@@ -52,6 +52,6 @@
}
- static final SortComparatorSource nullStringLastComparatorSource = new MissingStringLastComparatorSource();
+ static final SortComparatorSource nullStringLastComparatorSource = new MissingStringLastComparatorSource(null);
}
Modified: lucene/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java Tue Feb 26 11:47:07 2008
@@ -437,11 +437,12 @@
Document doc = searcher.doc(id);
String strid = schema.printableUniqueKey(doc);
- String docname = "";
- if (strid != null) docname="id="+strid+",";
- docname = docname + "internal_docid="+id;
- explainList.add(docname, "\n" +explain.toString());
+ // String docname = "";
+ // if (strid != null) docname="id="+strid+",";
+ // docname = docname + "internal_docid="+id;
+
+ explainList.add(strid, "\n" +explain.toString());
}
return explainList;
}
Modified: lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java Tue Feb 26 11:47:07 2008
@@ -225,7 +225,7 @@
String resp = h.query(lrf.makeRequest("q", "text:hello", "debugQuery", "true"));
//System.out.println(resp);
// second doc ranked first
- assertTrue( resp.indexOf("id=2") < resp.indexOf("id=1") );
+ assertTrue( resp.indexOf("\"2\"") < resp.indexOf("\"1\"") );
}
public void testFieldBoost() throws Exception {
@@ -243,7 +243,7 @@
String resp = h.query(lrf.makeRequest("q", "text:hello", "debugQuery", "true"));
//System.out.println(resp);
// second doc ranked first
- assertTrue( resp.indexOf("id=2") < resp.indexOf("id=1") );
+ assertTrue( resp.indexOf("\"2\"") < resp.indexOf("\"1\"") );
}
public void testXMLWriter() throws Exception {
Added: lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java?rev=631357&view=auto
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java (added)
+++ lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java Tue Feb 26 11:47:07 2008
@@ -0,0 +1,504 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr;
+
+import org.apache.solr.client.solrj.SolrServer;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.util.AbstractSolrTestCase;
+
+import java.io.File;
+import java.util.*;
+
+import junit.framework.TestCase;
+
+/**
+ * TODO? perhaps use:
+ * http://docs.codehaus.org/display/JETTY/ServletTester
+ * rather then open a real connection?
+ *
+ * @version $Id$
+ * @since solr 1.3
+ */
+public class TestDistributedSearch extends TestCase {
+
+ Random r = new Random(0);
+ File testDir;
+
+ int controlPort = 8985;
+ SolrServer controlClient;
+ JettySolrRunner controlJetty;
+
+ int[] ports = new int[] {7574, 7576};
+ List<SolrServer> clients = new ArrayList<SolrServer>();
+ List<JettySolrRunner> jettys = new ArrayList<JettySolrRunner>();
+ String context = "/solr";
+ String shards;
+
+
+ String id="id";
+ String t1="a_t";
+ String i1="a_i";
+
+
+ @Override public void setUp() throws Exception
+ {
+ System.setProperty("solr.test.sys.prop1", "propone");
+ System.setProperty("solr.test.sys.prop2", "proptwo");
+ testDir = new File(System.getProperty("java.io.tmpdir")
+ + System.getProperty("file.separator")
+ + getClass().getName() + "-" + System.currentTimeMillis());
+ testDir.mkdirs();
+ }
+
+ @Override public void tearDown() throws Exception
+ {
+ destroyServers();
+ AbstractSolrTestCase.recurseDelete(testDir);
+ }
+
+
+ private void createServers() throws Exception {
+ controlJetty = createJetty(controlPort);
+ controlClient = createNewSolrServer(controlPort);
+
+ StringBuilder sb = new StringBuilder();
+ for (int port : ports) {
+ if (sb.length()>0) sb.append(',');
+ sb.append("localhost:"+port+context);
+ jettys.add(createJetty(port));
+ clients.add(createNewSolrServer(port));
+ }
+
+ shards = sb.toString();
+ }
+
+ private void destroyServers() throws Exception {
+ controlJetty.stop();
+ for (JettySolrRunner jetty : jettys) jetty.stop();
+ clients.clear();
+ jettys.clear();
+ }
+
+ private JettySolrRunner createJetty(int port) throws Exception {
+ File subDir = new File(testDir, ""+port);
+ subDir.mkdirs();
+ System.setProperty("solr.data.dir", subDir.toString());
+
+ JettySolrRunner jetty = new JettySolrRunner("/solr", port);
+ jetty.start();
+ return jetty;
+ }
+
+ protected SolrServer createNewSolrServer(int port)
+ {
+ try {
+ // setup the server...
+ String url = "http://localhost:"+port+context;
+ CommonsHttpSolrServer s = new CommonsHttpSolrServer( url );
+ s.setConnectionTimeout(100); // 1/10th sec
+ s.setDefaultMaxConnectionsPerHost(100);
+ s.setMaxTotalConnections(100);
+ return s;
+ }
+ catch( Exception ex ) {
+ throw new RuntimeException( ex );
+ }
+ }
+
+
+ void index(Object... fields) throws Exception {
+ SolrInputDocument doc = new SolrInputDocument();
+ for (int i=0; i<fields.length; i+=2) {
+ doc.addField((String)(fields[i]), fields[i+1]);
+ }
+ controlClient.add(doc);
+
+ int which = (doc.getField(id).toString().hashCode() &0x7fffffff) % clients.size();
+ SolrServer client = clients.get(which);
+ client.add(doc);
+ }
+
+ void index_specific(int serverNumber, Object... fields) throws Exception {
+ SolrInputDocument doc = new SolrInputDocument();
+ for (int i=0; i<fields.length; i+=2) {
+ doc.addField((String)(fields[i]), fields[i+1]);
+ }
+ controlClient.add(doc);
+
+ int which = serverNumber;
+ SolrServer client = clients.get(which);
+ client.add(doc);
+ }
+
+ void del(String q) throws Exception {
+ controlClient.deleteByQuery(q);
+ for (SolrServer client : clients) {
+ client.deleteByQuery(q);
+ }
+ }
+
+
+ // serial commit...
+ void commit() throws Exception {
+ controlClient.commit();
+ for (SolrServer client : clients) client.commit();
+ }
+
+ void query(Object... q) throws Exception {
+ ModifiableSolrParams params = new ModifiableSolrParams();
+
+ for (int i=0; i<q.length; i+=2) {
+ params.add(q[i].toString(), q[i+1].toString());
+ }
+
+ QueryResponse controlRsp = controlClient.query(params);
+
+ // query a random server
+ params.set("shards", shards);
+ int which = r.nextInt(clients.size());
+ SolrServer client = clients.get(which);
+ QueryResponse rsp = client.query(params);
+
+ compareResponses(rsp, controlRsp);
+ }
+
+
+ private static int ORDERED=1;
+ private static int SKIP=2;
+ private static int SKIPVAL=4;
+ private static int UNORDERED=8;
+
+
+ public static boolean eq(String a, String b) {
+ return a==b || (a != null && a.equals(b));
+ }
+
+ public static int flags(Map<String,Integer> handle, Object key) {
+ if (handle==null) return 0;
+ Integer f = handle.get(key);
+ return f == null ? 0 : f;
+ }
+
+ public static String compare(NamedList a, NamedList b, int flags, Map<String,Integer> handle) {
+ boolean ordered = (flags&UNORDERED) == 0;
+
+ int posa = 0, posb = 0;
+ int na = 0, nb = 0;
+
+ for(;;) {
+ if (posa >= a.size() || posb >= b.size()) {
+ break;
+ }
+
+ String namea=null, nameb=null;
+ Object vala=null, valb=null;
+
+ int flagsa, flagsb;
+ for (;;) {
+ namea = a.getName(posa);
+ vala = a.getVal(posa);
+ posa++;
+ flagsa = flags(handle, namea);
+ if ((flagsa & SKIP) != 0) continue;
+ na++;
+ break;
+ }
+
+ if (!ordered) posb=0; // reset if not ordered
+
+ while (posb<b.size()) {
+ nameb = b.getName(posb);
+ valb = b.getVal(posb);
+ posb++;
+ flagsb = flags(handle, nameb);
+ if ((flagsb & SKIP) != 0) continue;
+ if (eq(namea, nameb)) {
+ nb++;
+ break;
+ }
+ if (ordered) {
+ return "."+namea+"!="+nameb+" (unordered or missing)";
+ }
+ // if unordered, continue until we find the right field.
+ }
+
+ // ok, namea and nameb should be equal here already.
+ if ((flagsa & SKIPVAL) != 0) continue; // keys matching is enough
+
+ String cmp = compare(vala, valb, flagsa, handle);
+ if (cmp != null) return "."+namea+cmp;
+ }
+
+
+ if (na != nb) {
+ return ".size()=="+na+","+nb;
+ }
+
+ return null;
+ }
+
+ private static String compare1(Map a, Map b, int flags, Map<String,Integer> handle) {
+ String cmp;
+
+ for (Object keya : a.keySet()) {
+ Object vala = a.get(keya);
+ int flagsa = flags(handle, keya);
+ if ((flagsa & SKIP) != 0) continue;
+ if (!b.containsKey(keya)) {
+ return "[" + keya + "]==null";
+ }
+ if ((flagsa & SKIPVAL) != 0) continue;
+ Object valb = b.get(keya);
+ cmp = compare(vala, valb, flagsa, handle);
+ if (cmp != null) return "[" + keya + "]" + cmp;
+ }
+ return null;
+ }
+
+ public static String compare(Map a, Map b, int flags, Map<String,Integer> handle) {
+ String cmp;
+ cmp = compare1(a,b,flags,handle);
+ if (cmp != null) return cmp;
+ return compare1(b,a,flags,handle);
+ }
+
+ public static String compare(SolrDocument a, SolrDocument b, int flags, Map<String,Integer> handle) {
+ return compare(a.getFieldValuesMap(), b.getFieldValuesMap(), flags, handle);
+ }
+
+ public static String compare(SolrDocumentList a, SolrDocumentList b, int flags, Map<String,Integer> handle) {
+ boolean ordered = (flags & UNORDERED) == 0;
+
+ String cmp;
+ int f = flags(handle, "maxScore");
+ if ((f & SKIPVAL) == 0) {
+ cmp = compare(a.getMaxScore(), b.getMaxScore(), 0, handle);
+ if (cmp != null) return ".maxScore" + cmp;
+ } else {
+ if (a.getMaxScore() != null) {
+ if (b.getMaxScore() == null) {
+ return ".maxScore missing";
+ }
+ }
+ }
+
+ cmp = compare(a.getNumFound(), b.getNumFound(), 0, handle);
+ if (cmp != null) return ".numFound" + cmp;
+
+ cmp = compare(a.getStart(), b.getStart(), 0, handle);
+ if (cmp != null) return ".start" + cmp;
+
+ cmp = compare(a.size(), b.size(), 0, handle);
+ if (cmp != null) return ".size()" + cmp;
+
+ // only for completely ordered results (ties might be in a different order)
+ if (ordered) {
+ for (int i=0; i<a.size(); i++) {
+ cmp = compare(a.get(i), b.get(i), 0, handle);
+ if (cmp != null) return "["+i+"]"+cmp;
+ }
+ return null;
+ }
+
+ // unordered case
+ for (int i=0; i<a.size(); i++) {
+ SolrDocument doc = a.get(i);
+ Object key = doc.getFirstValue("id");
+ SolrDocument docb=null;
+ if (key==null) {
+ // no id field to correlate... must compare ordered
+ docb = b.get(i);
+ } else {
+ for (int j=0; j<b.size(); j++) {
+ docb = b.get(j);
+ if (key.equals(docb.getFirstValue("id"))) break;
+ }
+ }
+ // if (docb == null) return "[id="+key+"]";
+ cmp = compare(doc, docb, 0, handle);
+ if (cmp != null) return "[id="+key+"]" + cmp;
+ }
+ return null;
+ }
+
+ public static String compare(Object[] a, Object[] b, int flags, Map<String,Integer> handle) {
+ if (a.length != b.length) {
+ return ".length:"+a.length+"!="+b.length;
+ }
+ for (int i=0; i<a.length; i++) {
+ String cmp = compare(a[i], b[i], flags, handle);
+ if (cmp != null) return "["+i+"]"+cmp;
+ }
+ return null;
+ }
+
+
+ static String compare(Object a, Object b, int flags, Map<String,Integer> handle) {
+ if (a==b) return null;
+ if (a==null || b==null) return ":" +a + "!=" + b;
+
+ if (a instanceof NamedList && b instanceof NamedList) {
+ return compare((NamedList)a, (NamedList)b, flags, handle);
+ }
+
+ if (a instanceof SolrDocumentList && b instanceof SolrDocumentList) {
+ return compare((SolrDocumentList)a, (SolrDocumentList)b, flags, handle);
+ }
+
+ if (a instanceof SolrDocument && b instanceof SolrDocument) {
+ return compare((SolrDocument)a, (SolrDocument)b, flags, handle);
+ }
+
+ if (a instanceof Map && b instanceof Map) {
+ return compare((Map)a, (Map)b, flags, handle);
+ }
+
+ if (a instanceof Object[] && b instanceof Object[]) {
+ return compare((Object[])a, (Object[])b, flags, handle);
+ }
+
+ if (!(a.equals(b))) {
+ return ":" + a + "!=" + b;
+ }
+
+ return null;
+ }
+
+
+ void compareResponses(QueryResponse a, QueryResponse b) {
+ String cmp;
+ System.out.println(a);
+ System.out.println(b);
+
+ cmp = compare(a.getResponse(), b.getResponse(), flags, handle);
+ if (cmp != null) {
+ System.out.println(a);
+ System.out.println(b);
+ TestCase.fail(cmp);
+ }
+ }
+
+ int flags;
+ Map<String, Integer> handle = new HashMap<String,Integer>();
+
+
+ public void testDistribSearch() throws Exception {
+ for (int nServers=1; nServers<4; nServers++) {
+ ports = new int[nServers];
+ for (int i=0; i<nServers; i++) {
+ ports[i] = 7574 + i*2;
+ }
+ doTest();
+ }
+ }
+
+ public void doTest() throws Exception {
+ createServers();
+ del("*:*");
+ index(id,1, i1, 100,t1,"now is the time for all good men");
+ index(id,2, i1, 50 ,t1,"to come to the aid of their country.");
+ index(id,3, i1, 2 ,t1,"how now brown cow");
+ index(id,4, i1, -100 ,t1,"the quick fox jumped over the lazy dog");
+ index(id,5, i1, 500 ,t1,"the quick fox jumped way over the lazy dog");
+ index(id,6, i1, -600 ,t1,"humpty dumpy sat on a wall");
+ index(id,7, i1, 123 ,t1,"humpty dumpy had a great fall");
+ index(id,8, i1, 876 ,t1,"all the kings horses and all the kings men");
+ index(id,9, i1, 7 ,t1,"couldn't put humpty together again");
+ index(id,10, i1, 4321 ,t1,"this too shal pass");
+ index(id,11, i1, -987 ,t1,"An eye for eye only ends up making the whole world blind.");
+ index(id,12, i1, 379 ,t1,"Great works are performed, not by strength, but by perseverance.");
+
+ commit();
+
+ handle.clear();
+ handle.put("QTime", SKIPVAL);
+ handle.put("timestamp", SKIPVAL);
+
+ // these queries should be exactly ordered and scores should exactly match
+ query("q","*:*", "sort",i1+" desc");
+ query("q","<!func>"+i1);
+ query("q","<!func>"+i1, "fl","*,score"); // even scores should match exactly here
+
+ handle.put("highlighting", UNORDERED);
+ handle.put("response", UNORDERED);
+
+ query("q","quick");
+ query("q","all","fl","id","start","0");
+ query("q","all","fl","foofoofoo","start","0"); // no fields in returned docs
+ query("q","all","fl","id","start","100");
+
+ handle.put("maxScore", SKIPVAL);
+ handle.put("score", SKIPVAL);
+ query("q","quick","fl","*,score");
+ query("q","all","fl","*,score","start","1");
+ query("q","all","fl","*,score","start","100");
+
+ query("q","now their fox sat had put","fl","*,score",
+ "hl","true","hl.fl",t1);
+
+ query("q","now their fox sat had put","fl","foofoofoo",
+ "hl","true","hl.fl",t1);
+
+
+ handle.put("debug", UNORDERED);
+ handle.put("time", SKIPVAL);
+
+ query("q","now their fox sat had put","fl","*,score",
+ "debugQuery", "true");
+
+ query("q","*:*", "rows",100, "facet","true", "facet.field",t1);
+ query("q","*:*", "rows",100, "facet","true", "facet.query","quick", "facet.query","all", "facet.query","*:*");
+ query("q","*:*", "rows",100, "facet","true", "facet.field",t1, "facet.offset",1);
+ query("q","*:*", "rows",100, "facet","true", "facet.field",t1,"facet.mincount",2);
+
+
+ // index the same document to two servers and make sure things
+ // don't blow up.
+ if (clients.size()>=2) {
+ index(id,100, i1, 107 ,t1,"oh no, a duplicate!");
+ for (int i=0; i<clients.size(); i++) {
+ index_specific(i, id,100, i1, 107 ,t1,"oh no, a duplicate!");
+ }
+ commit();
+ query("q","duplicate", "hl","true", "hl.fl", t1);
+ query("q","fox duplicate horses", "hl","true", "hl.fl", t1);
+ query("q","*:*", "rows",100);
+ }
+
+ // Thread.sleep(10000000000L);
+
+ destroyServers();
+ }
+
+
+}
+
+
+
Propchange: lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: lucene/solr/trunk/src/test/org/apache/solr/TestDistributedSearch.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Modified: lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml?rev=631357&r1=631356&r2=631357&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml (original)
+++ lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml Tue Feb 26 11:47:07 2008
@@ -27,9 +27,7 @@
<!-- Used to specify an alternate directory to hold all index data.
It defaults to "index" if not present, and should probably
not be changed if replication is in use. -->
- <!--
- <indexDir>index</indexDir>
- -->
+ <dataDir>${solr.data.dir:./solr/data}</dataDir>
<indexDefaults>
<!-- Values here affect all index writers and act as a default