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
