mirror of https://github.com/apache/lucene.git
SOLR-2037: hook in DocTransformer for QueryElevationComponent to mark documents that have been boosted
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1203839 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5267d23d46
commit
00f5fef162
|
@ -1705,6 +1705,10 @@ public final class SolrCore implements SolrInfoMBean {
|
|||
public TransformerFactory getTransformerFactory(String name) {
|
||||
return transformerFactories.get(name);
|
||||
}
|
||||
|
||||
public void addTransformerFactory(String name, TransformerFactory factory){
|
||||
transformerFactories.put(name, factory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,14 +23,10 @@ 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 java.util.*;
|
||||
|
||||
import org.apache.solr.common.params.QueryElevationParams;
|
||||
import org.apache.solr.response.transform.EditorialMarkerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -103,6 +99,7 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore
|
|||
final BooleanClause[] exclude;
|
||||
final BooleanQuery include;
|
||||
final Map<BytesRef,Integer> priority;
|
||||
final Set<String> ids;
|
||||
|
||||
// use singletons so hashCode/equals on Sort will just work
|
||||
final FieldComparatorSource comparatorSource;
|
||||
|
@ -111,12 +108,14 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore
|
|||
{
|
||||
this.text = qstr;
|
||||
this.analyzed = getAnalyzedQuery( this.text );
|
||||
this.ids = new HashSet<String>();
|
||||
|
||||
this.include = new BooleanQuery();
|
||||
this.include.setBoost( 0 );
|
||||
this.priority = new HashMap<BytesRef, Integer>();
|
||||
int max = elevate.size()+5;
|
||||
for( String id : elevate ) {
|
||||
ids.add(id);
|
||||
TermQuery tq = new TermQuery( new Term( idField, id ) );
|
||||
include.add( tq, BooleanClause.Occur.SHOULD );
|
||||
this.priority.put( new BytesRef(id), max-- );
|
||||
|
@ -161,7 +160,13 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore
|
|||
"QueryElevationComponent requires the schema to have a uniqueKeyField implemented using StrField" );
|
||||
}
|
||||
idField = sf.getName();
|
||||
|
||||
//register the EditorialMarkerFactory
|
||||
EditorialMarkerFactory factory = new EditorialMarkerFactory();
|
||||
String markerName = initArgs.get(QueryElevationParams.EDITORIAL_MARKER_FIELD_NAME, "elevated");
|
||||
if (markerName == null || markerName.equals("") == true){
|
||||
markerName = "elevated";
|
||||
}
|
||||
core.addTransformerFactory(markerName, factory);
|
||||
forceElevation = initArgs.getBool( QueryElevationParams.FORCE_ELEVATION, forceElevation );
|
||||
try {
|
||||
synchronized( elevationCache ) {
|
||||
|
@ -357,6 +362,8 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore
|
|||
}
|
||||
|
||||
if( booster != null ) {
|
||||
rb.req.getContext().put("BOOSTED", booster.ids);
|
||||
|
||||
// Change the query to insert forced documents
|
||||
if (exclusive == true){
|
||||
//we only want these results
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.solr.core.SolrCore;
|
|||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.response.transform.DocTransformer;
|
||||
import org.apache.solr.response.transform.TransformContext;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
|
@ -40,6 +41,7 @@ import org.apache.solr.update.DocumentBuilder;
|
|||
import org.apache.solr.update.UpdateLog;
|
||||
import org.apache.solr.util.RefCounted;
|
||||
|
||||
import javax.xml.transform.Transformer;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
@ -103,6 +105,11 @@ public class RealTimeGetComponent extends SearchComponent
|
|||
RefCounted<SolrIndexSearcher> searcherHolder = null;
|
||||
|
||||
DocTransformer transformer = rsp.getReturnFields().getTransformer();
|
||||
if (transformer != null) {
|
||||
TransformContext context = new TransformContext();
|
||||
context.req = req;
|
||||
transformer.setContext(context);
|
||||
}
|
||||
try {
|
||||
SolrIndexSearcher searcher = null;
|
||||
|
||||
|
|
|
@ -119,17 +119,17 @@ public class BinaryResponseWriter implements BinaryQueryResponseWriter {
|
|||
protected void writeResultsBody( ResultContext res, JavaBinCodec codec ) throws IOException
|
||||
{
|
||||
DocList ids = res.docs;
|
||||
TransformContext context = new TransformContext();
|
||||
context.query = res.query;
|
||||
context.wantsScores = returnFields.wantsScore() && ids.hasScores();
|
||||
|
||||
int sz = ids.size();
|
||||
codec.writeTag(JavaBinCodec.ARR, sz);
|
||||
if(searcher == null) searcher = solrQueryRequest.getSearcher();
|
||||
if(schema == null) schema = solrQueryRequest.getSchema();
|
||||
|
||||
context.searcher = searcher;
|
||||
if(schema == null) schema = solrQueryRequest.getSchema();
|
||||
|
||||
DocTransformer transformer = returnFields.getTransformer();
|
||||
TransformContext context = new TransformContext();
|
||||
context.query = res.query;
|
||||
context.wantsScores = returnFields.wantsScore() && ids.hasScores();
|
||||
context.req = solrQueryRequest;
|
||||
context.searcher = searcher;
|
||||
if( transformer != null ) {
|
||||
transformer.setContext( context );
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ public class BinaryResponseWriter implements BinaryQueryResponseWriter {
|
|||
Document doc = searcher.doc(id, fnames);
|
||||
SolrDocument sdoc = getDoc(doc);
|
||||
if( transformer != null ) {
|
||||
transformer.transform(sdoc, id );
|
||||
transformer.transform(sdoc, id);
|
||||
}
|
||||
codec.writeSolrDocument(sdoc);
|
||||
}
|
||||
|
|
|
@ -146,8 +146,12 @@ public abstract class TextResponseWriter {
|
|||
writeDouble(name, ((Double)val).doubleValue());
|
||||
} else if (val instanceof Document) {
|
||||
SolrDocument doc = toSolrDocument( (Document)val );
|
||||
if( returnFields.getTransformer() != null ) {
|
||||
returnFields.getTransformer().transform( doc, -1 );
|
||||
DocTransformer transformer = returnFields.getTransformer();
|
||||
if( transformer != null ) {
|
||||
TransformContext context = new TransformContext();
|
||||
context.req = req;
|
||||
transformer.setContext(context);
|
||||
transformer.transform(doc, -1);
|
||||
}
|
||||
writeSolrDocument(name, doc, returnFields, 0 );
|
||||
} else if (val instanceof SolrDocument) {
|
||||
|
@ -232,6 +236,7 @@ public abstract class TextResponseWriter {
|
|||
TransformContext context = new TransformContext();
|
||||
context.query = res.query;
|
||||
context.wantsScores = fields.wantsScore() && ids.hasScores();
|
||||
context.req = req;
|
||||
writeStartDocumentList(name, ids.offset(), ids.size(), ids.matches(),
|
||||
context.wantsScores ? new Float(ids.maxScore()) : null );
|
||||
|
||||
|
@ -248,7 +253,7 @@ public abstract class TextResponseWriter {
|
|||
Document doc = context.searcher.doc(id, fnames);
|
||||
SolrDocument sdoc = toSolrDocument( doc );
|
||||
if( transformer != null ) {
|
||||
transformer.transform( sdoc, id );
|
||||
transformer.transform( sdoc, id);
|
||||
}
|
||||
writeSolrDocument( null, sdoc, returnFields, i );
|
||||
}
|
||||
|
|
|
@ -20,16 +20,41 @@ package org.apache.solr.response.transform;
|
|||
import java.io.IOException;
|
||||
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
/**
|
||||
* A DocTransformer can add, remove or alter a Document before it is written out to the Response. For instance, there are implementations
|
||||
* that can put explanations inline with a document, add constant values and mark items as being artificially boosted (see {@link org.apache.solr.handler.component.QueryElevationComponent})
|
||||
*
|
||||
* <p/>
|
||||
* New instance for each request
|
||||
*
|
||||
* @see TransformerFactory
|
||||
*
|
||||
*/
|
||||
public abstract class DocTransformer
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @return The name of the transformer
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
/**
|
||||
* This is called before transform and sets
|
||||
* @param context The {@link org.apache.solr.response.transform.TransformContext} stores information about the current state of things in Solr that may be
|
||||
* useful for doing transformations.
|
||||
*/
|
||||
public void setContext( TransformContext context ) {}
|
||||
|
||||
/**
|
||||
* This is where implementations do the actual work
|
||||
*
|
||||
*
|
||||
* @param doc The document to alter
|
||||
* @param docid The Lucene internal doc id
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract void transform(SolrDocument doc, int docid) throws IOException;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
/**
|
||||
* Transform a document before it gets sent out
|
||||
|
@ -73,7 +74,7 @@ public class DocTransformers extends DocTransformer
|
|||
@Override
|
||||
public void transform(SolrDocument doc, int docid) throws IOException {
|
||||
for( DocTransformer a : children ) {
|
||||
a.transform( doc, docid );
|
||||
a.transform( doc, docid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package org.apache.solr.response.transform;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since solr 4.0
|
||||
*/
|
||||
public class EditorialMarkerFactory extends TransformerFactory
|
||||
{
|
||||
@Override
|
||||
public DocTransformer create(String field, SolrParams params, SolrQueryRequest req) {
|
||||
String idfield = req.getSchema().getUniqueKeyField().getName();
|
||||
return new MarkTransformer(field,idfield);
|
||||
}
|
||||
}
|
||||
|
||||
class MarkTransformer extends TransformerWithContext
|
||||
{
|
||||
final String name;
|
||||
final String idFieldName;
|
||||
|
||||
public MarkTransformer( String name, String idFieldName)
|
||||
{
|
||||
this.name = name;
|
||||
this.idFieldName = idFieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(SolrDocument doc, int docid) {
|
||||
Set<?> ids = (Set<?>)context.req.getContext().get("BOOSTED");
|
||||
if(ids!=null) {
|
||||
String key;
|
||||
Object field = doc.get(idFieldName);
|
||||
if (field instanceof Field){
|
||||
key = ((Field)field).stringValue();
|
||||
} else {
|
||||
key = field.toString();
|
||||
}
|
||||
doc.setField(name, ids.contains(key));
|
||||
} else {
|
||||
//if we have no ids, that means we weren't boosting, but the user still asked for the field to be added, so just mark everything as false
|
||||
doc.setField(name, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ package org.apache.solr.response.transform;
|
|||
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
/**
|
||||
* Return a field with a name that is different that what is indexed
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.solr.response.transform;
|
||||
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
|
||||
/**
|
||||
* Simple Augmenter that adds the docId
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.solr.response.transform;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.search.DocIterator;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
|
||||
|
@ -32,4 +33,5 @@ public class TransformContext
|
|||
public boolean wantsScores = false;
|
||||
public DocIterator iterator;
|
||||
public SolrIndexSearcher searcher;
|
||||
public SolrQueryRequest req;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.lucene.queries.function.ValueSource;
|
|||
import org.apache.lucene.util.ReaderUtil;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.search.QParser;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
|
||||
|
|
|
@ -117,6 +117,45 @@ public class QueryElevationComponentTest extends SolrTestCaseJ4 {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMarker() throws Exception {
|
||||
assertU(adoc("id", "1", "title", "XXXX XXXX", "str_s1", "a" ));
|
||||
assertU(adoc("id", "2", "title", "YYYY", "str_s1", "b" ));
|
||||
assertU(adoc("id", "3", "title", "ZZZZ", "str_s1", "c" ));
|
||||
|
||||
assertU(adoc("id", "4", "title", "XXXX XXXX", "str_s1", "x" ));
|
||||
assertU(adoc("id", "5", "title", "YYYY YYYY", "str_s1", "y" ));
|
||||
assertU(adoc("id", "6", "title", "XXXX XXXX", "str_s1", "z" ));
|
||||
assertU(adoc("id", "7", "title", "AAAA", "str_s1", "a" ));
|
||||
assertU(commit());
|
||||
|
||||
assertQ("", req(CommonParams.Q, "XXXX", CommonParams.QT, "/elevate",
|
||||
CommonParams.FL, "id, score, [elevated]")
|
||||
,"//*[@numFound='3']"
|
||||
,"//result/doc[1]/str[@name='id'][.='1']"
|
||||
,"//result/doc[2]/str[@name='id'][.='4']"
|
||||
,"//result/doc[3]/str[@name='id'][.='6']",
|
||||
"//result/doc[1]/bool[@name='[elevated]'][.='true']",
|
||||
"//result/doc[2]/bool[@name='[elevated]'][.='false']",
|
||||
"//result/doc[3]/bool[@name='[elevated]'][.='false']"
|
||||
);
|
||||
|
||||
assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate",
|
||||
CommonParams.FL, "id, score, [elevated]")
|
||||
,"//*[@numFound='1']"
|
||||
,"//result/doc[1]/str[@name='id'][.='7']",
|
||||
"//result/doc[1]/bool[@name='[elevated]'][.='false']"
|
||||
);
|
||||
|
||||
assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate",
|
||||
CommonParams.FL, "id, score, [elev]")
|
||||
,"//*[@numFound='1']"
|
||||
,"//result/doc[1]/str[@name='id'][.='7']",
|
||||
"not(//result/doc[1]/bool[@name='[elevated]'][.='false'])",
|
||||
"not(//result/doc[1]/bool[@name='[elev]'][.='false'])" // even though we asked for elev, there is no Transformer registered w/ that, so we shouldn't get a result
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSorting() throws IOException
|
||||
{
|
||||
|
|
|
@ -1631,6 +1631,10 @@
|
|||
<transformer name="mytrans3" class="org.apache.solr.response.transform.ValueAugmenterFactory" >
|
||||
<double name="defaultValue">5</double>
|
||||
</transformer>
|
||||
|
||||
If you are using the QueryElevationComponent, you may wish to mark documents that get boosted. The
|
||||
EditorialMarkerFactory will do exactly that:
|
||||
<transformer name="qecBooster" class="org.apache.solr.response.transform.EditorialMarkerFactory" />
|
||||
-->
|
||||
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ public class SolrDocument implements Map<String,Object>, Iterable<Map.Entry<Stri
|
|||
public Set<Entry<String, Object>> entrySet() {
|
||||
return _fields.entrySet();
|
||||
}
|
||||
|
||||
//TODO: Shouldn't the input parameter here be a String? The _fields map requires a String.
|
||||
public Object get(Object key) {
|
||||
return _fields.get(key);
|
||||
}
|
||||
|
|
|
@ -27,4 +27,11 @@ public interface QueryElevationParams {
|
|||
String ENABLE = "enableElevation";
|
||||
String EXCLUSIVE = "exclusive";
|
||||
String FORCE_ELEVATION = "forceElevation";
|
||||
/**
|
||||
* The name of the field that editorial results will be written out as when using the QueryElevationComponent, which
|
||||
* automatically configures the EditorialMarkerFactory. The default name is "elevated"
|
||||
* <br/>
|
||||
* See http://wiki.apache.org/solr/DocTransformers
|
||||
*/
|
||||
String EDITORIAL_MARKER_FIELD_NAME = "editorialMarkerFieldName";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue