content: /**
   * 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.handler.component;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.StringReader;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.WeakHashMap;
  
  import org.apache.solr.common.params.QueryElevationParams;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import javax.xml.xpath.XPath;
  import javax.xml.xpath.XPathConstants;
  import javax.xml.xpath.XPathExpressionException;
  import javax.xml.xpath.XPathFactory;
  
  import org.apache.lucene.analysis.Analyzer;
  import org.apache.lucene.analysis.TokenStream;
  import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
  import org.apache.lucene.index.IndexReader;
  import org.apache.lucene.index.Term;
  import org.apache.lucene.search.*;
  import org.apache.lucene.util.StringHelper;
  import org.apache.solr.common.SolrException;
  import org.apache.solr.common.params.SolrParams;
  import org.apache.solr.common.util.DOMUtil;
  import org.apache.solr.common.util.NamedList;
  import org.apache.solr.common.util.SimpleOrderedMap;
  import org.apache.solr.core.Config;
  import org.apache.solr.core.SolrCore;
  import org.apache.solr.schema.StrField;
  import org.apache.solr.schema.FieldType;
  import org.apache.solr.schema.SchemaField;
  import org.apache.solr.search.SortSpec;
  import org.apache.solr.search.SolrIndexSearcher;
  import org.apache.solr.update.UpdateHandler;
  import org.apache.solr.util.VersionedFile;
  import org.apache.solr.util.RefCounted;
  import org.apache.solr.util.plugin.SolrCoreAware;
  import org.apache.solr.request.SolrQueryRequest;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.xml.sax.InputSource;
  
  /**
   * A component to elevate some documents to the top of the result set.
   * 
   * @version $Id: QueryElevationComponent.java 1175532 2011-09-25 19:30:34Z rmuir $
   * @since solr 1.3
   */
  
  public class QueryElevationComponent{
  	
  }
  //
  //public class QueryElevationComponent extends SearchComponent implements SolrCoreAware
  //{
  //	
  //  private static Logger log = LoggerFactory.getLogger(QueryElevationComponent.class);
  //  
  //  // Constants used in solrconfig.xml
  //  static final String FIELD_TYPE = "queryFieldType";
  //  static final String CONFIG_FILE = "config-file";
  //  static final String EXCLUDE = "exclude";
  //  
  //  // Runtime param -- should be in common?
  //
  //  private SolrParams initArgs = null;
  //  private Analyzer analyzer = null;
  //  private String idField = null;
  //
  //  boolean forceElevation = false;
  //  // For each IndexReader, keep a query->elevation map
  //  // When the configuration is loaded from the data directory.
  //  // The key is null if loaded from the config directory, and
  //  // is never re-loaded.
  //  final Map<IndexReader,Map<String, ElevationObj>> elevationCache =
  //    new WeakHashMap<IndexReader, Map<String,ElevationObj>>();
  //
  //  class ElevationObj {
  //    final String text;
  //    final String analyzed;
  //    final BooleanClause[] exclude;
  //    final BooleanQuery include;
  //    final Map<String,Integer> priority;
  //    
  //    // use singletons so hashCode/equals on Sort will just work
  //    final FieldComparatorSource comparatorSource;
  //
  //    ElevationObj( String qstr, List<String> elevate, List<String> exclude ) throws IOException
  //    {
  //      this.text = qstr;
  //      this.analyzed = getAnalyzedQuery( this.text );
  //      
  //      this.include = new BooleanQuery();
  //      this.include.setBoost( 0 );
  //      this.priority = new HashMap<String, Integer>();
  //      int max = elevate.size()+5;
  //      for( String id : elevate ) {
  //        TermQuery tq = new TermQuery( new Term( idField, id ) );
  //        include.add( tq, BooleanClause.Occur.SHOULD );
  //        this.priority.put( id, max-- );
  //      }
  //      
  //      if( exclude == null || exclude.isEmpty() ) {
  //        this.exclude = null;
  //      }
  //      else {
  //        this.exclude = new BooleanClause[exclude.size()];
  //        for( int i=0; i<exclude.size(); i++ ) {
  //          TermQuery tq = new TermQuery( new Term( idField, exclude.get(i) ) );
  //          this.exclude[i] = new BooleanClause( tq, BooleanClause.Occur.MUST_NOT );
  //        }
  //      }
  //
  //      this.comparatorSource = new ElevationComparatorSource(priority);
  //    }
  //  }
  //  
  //  @Override
  //  public void init( NamedList args )
  //  {
  //    this.initArgs = SolrParams.toSolrParams( args );
  //  }
  //  
  //  public void inform(SolrCore core)
  //  {
  //    String a = initArgs.get( FIELD_TYPE );
  //    if( a != null ) {
  //      FieldType ft = core.getSchema().getFieldTypes().get( a );
  //      if( ft == null ) {
  //        throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //            "Unknown FieldType: '"+a+"' used in QueryElevationComponent" );
  //      }
  //      analyzer = ft.getQueryAnalyzer();
  //    }
  //
  //    SchemaField sf = core.getSchema().getUniqueKeyField();
  //    if( sf == null || !(sf.getType() instanceof StrField)) {
  //      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
  //          "QueryElevationComponent requires the schema to have a uniqueKeyField implemented using StrField" );
  //    }
  //    idField = StringHelper.intern(sf.getName());
  //    
  //    forceElevation = initArgs.getBool( QueryElevationParams.FORCE_ELEVATION, forceElevation );
  //    try {
  //      synchronized( elevationCache ) {
  //        elevationCache.clear();
  //        String f = initArgs.get( CONFIG_FILE );
  //        if( f == null ) {
  //          throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //              "QueryElevationComponent must specify argument: '"+CONFIG_FILE
  //              +"' -- path to elevate.xml" );
  //        }
  //        File fC = new File( core.getResourceLoader().getConfigDir(), f );
  //        File fD = new File( core.getDataDir(), f );
  //        if( fC.exists() == fD.exists() ) {
  //          throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //              "QueryElevationComponent missing config file: '"+f + "\n"
  //              +"either: "+fC.getAbsolutePath() + " or " + fD.getAbsolutePath() + " must exist, but not both." );
  //        }
  //        if( fC.exists() ) {
  //          log.info( "Loading QueryElevation from: "+fC.getAbsolutePath() );
  //          Config cfg = new Config( core.getResourceLoader(), f );
  //          elevationCache.put(null, loadElevationMap( cfg ));
  //        }
  //        else {
  //          // preload the first data
  //          RefCounted<SolrIndexSearcher> searchHolder = null;
  //          try {
  //            searchHolder = core.getSearcher(UpdateHandler.UPDATEPARTION,false,false);
  //            IndexReader reader = searchHolder.get().getReader();
  //            getElevationMap( reader, core );
  //          } finally {
  //            if (searchHolder != null) searchHolder.decref();
  //          }
  //        }
  //      }
  //    }
  //    catch( Exception ex ) {
  //      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //          "Error initializing QueryElevationComponent.", ex );
  //    }
  //  }
  //
  //  Map<String, ElevationObj> getElevationMap( IndexReader reader, SolrCore core ) throws Exception
  //  {
  //    synchronized( elevationCache ) {
  //      Map<String, ElevationObj> map = elevationCache.get( null );
  //      if (map != null) return map;
  //
  //      map = elevationCache.get( reader );
  //      if( map == null ) {
  //        String f = initArgs.get( CONFIG_FILE );
  //        if( f == null ) {
  //          throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //                  "QueryElevationComponent must specify argument: "+CONFIG_FILE );
  //        }
  //        log.info( "Loading QueryElevation from data dir: "+f );
  //
  //        InputStream is = VersionedFile.getLatestFile( core.getDataDir(), f );
  //        Config cfg = new Config( core.getResourceLoader(), f, new InputSource(is), null );
  //        map = loadElevationMap( cfg );
  //        elevationCache.put( reader, map );
  //      }
  //      return map;
  //    }
  //  }
  //  
  //  private Map<String, ElevationObj> loadElevationMap( Config cfg ) throws IOException
  //  {
  //    XPath xpath = XPathFactory.newInstance().newXPath();
  //    Map<String, ElevationObj> map = new HashMap<String, ElevationObj>();
  //    NodeList nodes = (NodeList)cfg.evaluate( "elevate/query", XPathConstants.NODESET );
  //    for (int i=0; i<nodes.getLength(); i++) {
  //      Node node = nodes.item( i );
  //      String qstr = DOMUtil.getAttr( node, "text", "missing query 'text'" );
  //      
  //      NodeList children = null;
  //      try {
  //        children = (NodeList)xpath.evaluate("doc", node, XPathConstants.NODESET);
  //      } 
  //      catch (XPathExpressionException e) {
  //        throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
  //            "query requires '<doc .../>' child" );
  //      }
  //
  //      ArrayList<String> include = new ArrayList<String>();
  //      ArrayList<String> exclude = new ArrayList<String>();
  //      for (int j=0; j<children.getLength(); j++) {
  //        Node child = children.item(j);
  //        String id = DOMUtil.getAttr( child, "id", "missing 'id'" );
  //        String e = DOMUtil.getAttr( child, EXCLUDE, null );
  //        if( e != null ) {
  //          if( Boolean.valueOf( e ) ) {
  //            exclude.add( id );
  //            continue;
  //          }
  //        }
  //        include.add( id );
  //      }
  //      
  //      ElevationObj elev = new ElevationObj( qstr, include, exclude );
  //      if( map.containsKey( elev.analyzed ) ) {
  //        throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, 
  //            "Boosting query defined twice for query: '"+elev.text+"' ("+elev.analyzed+"')" );
  //      }
  //      map.put( elev.analyzed, elev );
  //    }
  //    return map;
  //  }
  //  
  //  /**
  //   * Helpful for testing without loading config.xml
  //   * @throws IOException 
  //   */
  //  void setTopQueryResults( IndexReader reader, String query, String[] ids, String[] ex ) throws IOException
  //  {
  //    if( ids == null ) {
  //      ids = new String[0];
  //    }
  //    if( ex == null ) {
  //      ex = new String[0];
  //    }
  //    
  //    Map<String,ElevationObj> elev = elevationCache.get( reader );
  //    if( elev == null ) {
  //      elev = new HashMap<String, ElevationObj>();
  //      elevationCache.put( reader, elev );
  //    }
  //    ElevationObj obj = new ElevationObj( query, Arrays.asList(ids), Arrays.asList(ex) );
  //    elev.put( obj.analyzed, obj );
  //  }
  //  
  //  String getAnalyzedQuery( String query ) throws IOException
  //  {
  //    if( analyzer == null ) {
  //      return query;
  //    }
  //    StringBuilder norm = new StringBuilder();
  //    TokenStream tokens = analyzer.reusableTokenStream( "", new StringReader( query ) );
  //    tokens.reset();
  //    
  //    CharTermAttribute termAtt = tokens.addAttribute(CharTermAttribute.class);
  //    while( tokens.incrementToken() ) {
  //      norm.append( termAtt.buffer(), 0, termAtt.length() );
  //    }
  //    tokens.end();
  //    tokens.close();
  //    return norm.toString();
  //  }
  //
  //  //---------------------------------------------------------------------------------
  //  // SearchComponent
  //  //---------------------------------------------------------------------------------
  //  
  //  @Override
  //  public void prepare(ResponseBuilder rb) throws IOException
  //  {
  //    SolrQueryRequest req = rb.req;
  //    SolrParams params = req.getParams();
  //    // A runtime param can skip 
  //    if( !params.getBool( QueryElevationParams.ENABLE, true ) ) {
  //      return;
  //    }
  //
  //    boolean exclusive = params.getBool(QueryElevationParams.EXCLUSIVE, false);
  //    // A runtime parameter can alter the config value for forceElevation
  //    boolean force = params.getBool( QueryElevationParams.FORCE_ELEVATION, forceElevation );
  //    
  //    Query query = rb.getQuery();
  //    String qstr = rb.getQueryString();
  //    if( query == null || qstr == null) {
  //      return;
  //    }
  //
  //    qstr = getAnalyzedQuery(qstr);
  //    IndexReader reader = req.getSearcher().getReader();
  //    ElevationObj booster = null;
  //    try {
  //      booster = getElevationMap( reader, req.getCore() ).get( qstr );
  //    }
  //    catch( Exception ex ) {
  //      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
  //          "Error loading elevation", ex );      
  //    }
  //    
  //    if( booster != null ) {
  //      // Change the query to insert forced documents
  //      if (exclusive == true){
  //        //we only want these results
  //        rb.setQuery(booster.include);
  //      } else {
  //        BooleanQuery newq = new BooleanQuery( true );
  //        newq.add( query, BooleanClause.Occur.SHOULD );
  //        newq.add( booster.include, BooleanClause.Occur.SHOULD );
  //        if( booster.exclude != null ) {
  //          for( BooleanClause bq : booster.exclude ) {
  //            newq.add( bq );
  //          }
  //        }
  //        rb.setQuery( newq );
  //      }
  //
  //      
  //      // if the sort is 'score desc' use a custom sorting method to 
  //      // insert documents in their proper place 
  //      SortSpec sortSpec = rb.getSortSpec();
  //      if( sortSpec.getSort() == null ) {
  //        sortSpec.setSort( new Sort( new SortField[] {
  //            new SortField(idField, booster.comparatorSource, false ),
  //            new SortField(null, SortField.SCORE, false)
  //        }));
  //      }
  //      else {
  //        // Check if the sort is based on score
  //        boolean modify = false;
  //        SortField[] current = sortSpec.getSort().getSort();
  //        ArrayList<SortField> sorts = new ArrayList<SortField>( current.length + 1 );
  //        // Perhaps force it to always sort by score
  //        if( force && current[0].getType() != SortField.SCORE ) {
  //          sorts.add( new SortField(idField, booster.comparatorSource, false ) );
  //          modify = true;
  //        }
  //        for( SortField sf : current ) {
  //          if( sf.getType() == SortField.SCORE ) {
  //            sorts.add( new SortField(idField, booster.comparatorSource, sf.getReverse() ) );
  //            modify = true;
  //          }
  //          sorts.add( sf );
  //        }
  //        if( modify ) {
  //          sortSpec.setSort( new Sort( sorts.toArray( new SortField[sorts.size()] ) ) );
  //        }
  //      }
  //    }
  //    
  //    // Add debugging information
  //    if( rb.isDebug() ) {
  //      List<String> match = null;
  //      if( booster != null ) {
  //        // Extract the elevated terms into a list
  //        match = new ArrayList<String>(booster.priority.size());
  //        for( Object o : booster.include.clauses() ) {
  //          TermQuery tq = (TermQuery)((BooleanClause)o).getQuery();
  //          match.add( tq.getTerm().text() );
  //        }
  //      }
  //      
  //      SimpleOrderedMap<Object> dbg = new SimpleOrderedMap<Object>();
  //      dbg.add( "q", qstr );
  //      dbg.add( "match", match );
  //      rb.addDebugInfo( "queryBoosting", dbg );
  //    }
  //  }
  //
  //  @Override
  //  public void process(ResponseBuilder rb) throws IOException {
  //    // Do nothing -- the real work is modifying the input query
  //  }
  //    
  //  //---------------------------------------------------------------------------------
  //  // SolrInfoMBean
  //  //---------------------------------------------------------------------------------
  //
  //  @Override
  //  public String getDescription() {
  //    return "Query Boosting -- boost particular documents for a given query";
  //  }
  //
  //  @Override
  //  public String getVersion() {
  //    return "$Revision: 1175532 $";
  //  }
  //
  //  @Override
  //  public String getSourceId() {
  //    return "$Id: QueryElevationComponent.java 1175532 2011-09-25 19:30:34Z rmuir $";
  //  }
  //
  //  @Override
  //  public String getSource() {
  //    return "$URL: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene_solr_3_5/solr/core/src/java/org/apache/solr/handler/component/QueryElevationComponent.java $";
  //  }
  //
  //  @Override
  //  public URL[] getDocs() {
  //    try {
  //      return new URL[] {
  //        new URL("http://wiki.apache.org/solr/QueryElevationComponent")
  //      };
  //    } 
  //    catch (MalformedURLException e) {
  //      throw new RuntimeException( e );
  //    }
  //  }
  //}
  //
  //class ElevationComparatorSource extends FieldComparatorSource {
  //  private final Map<String,Integer> priority;
  //
  //  public ElevationComparatorSource( final Map<String,Integer> boosts) {
  //    this.priority = boosts;
  //  }
  //
  //  @Override
  //  public FieldComparator<Integer> newComparator(final String fieldname, final int numHits, int sortPos, boolean reversed) throws IOException {
  //    return new FieldComparator<Integer>() {
  //      
  //      FieldCache.StringIndex idIndex;
  //      private final int[] values = new int[numHits];
  //      int bottomVal;
  //
  //      @Override
  //      public int compare(int slot1, int slot2) {
  //        return values[slot2] - values[slot1];  // values will be small enough that there is no overflow concern
  //      }
  //
  //      @Override
  //      public void setBottom(int slot) {
  //        bottomVal = values[slot];
  //      }
  //
  //      private int docVal(int doc) throws IOException {
  //        String id = idIndex.lookup[idIndex.order[doc]];
  //        Integer prio = priority.get(id);
  //        return prio == null ? 0 : prio.intValue();
  //      }
  //
  //      @Override
  //      public int compareBottom(int doc) throws IOException {
  //        return docVal(doc) - bottomVal;
  //      }
  //
  //      @Override
  //      public void copy(int slot, int doc) throws IOException {
  //        values[slot] = docVal(doc);
  //      }
  //
  //      @Override
  //      public void setNextReader(IndexReader reader, int docBase) throws IOException {
  //        idIndex = FieldCache.DEFAULT.getStringIndex(reader, fieldname);
  //      }
  //
  //      @Override
  //      public Integer value(int slot) {
  //        return values[slot];
  //      }
  //      
  //      @Override
  //      public double docValue(int doc) {
  //        return Double.valueOf(0);//docVal(doc);
  //      }
  //    };
  //  }
  //}
  