package org.apache.lucene.search; /** * 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.io.IOException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.DefaultSimilarity; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.Similarity; /** * The BoostingQuery class can be used to effectively demote results that match a given query. * Unlike the "NOT" clause, this still selects documents that contain undesirable terms, * but reduces their overall score: * * Query balancedQuery = new BoostingQuery(positiveQuery, negativeQuery, 0.01f); * In this scenario the positiveQuery contains the mandatory, desirable criteria which is used to * select all matching documents, and the negativeQuery contains the undesirable elements which * are simply used to lessen the scores. Documents that match the negativeQuery have their score * multiplied by the supplied "boost" parameter, so this should be less than 1 to achieve a * demoting effect * * This code was originally made available here: [WWW] http://marc.theaimsgroup.com/?l=lucene-user&m=108058407130459&w=2 * and is documented here: http://wiki.apache.org/lucene-java/CommunityContributions */ public class BoostingQuery extends Query { private float boost; // the amount to boost by private Query match; // query to match private Query context; // boost when matches too public BoostingQuery(Query match, Query context, float boost) { this.match = match; this.context = (Query)context.clone(); // clone before boost this.boost = boost; this.context.setBoost(0.0f); // ignore context-only matches } @Override public Query rewrite(IndexReader reader) throws IOException { BooleanQuery result = new BooleanQuery() { @Override public Similarity getSimilarity(Searcher searcher) { return new DefaultSimilarity() { @Override public float coord(int overlap, int max) { switch (overlap) { case 1: // matched only one clause return 1.0f; // use the score as-is case 2: // matched both clauses return boost; // multiply by boost default: return 0.0f; } } }; } }; result.add(match, BooleanClause.Occur.MUST); result.add(context, BooleanClause.Occur.SHOULD); return result; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Float.floatToIntBits(boost); result = prime * result + ((context == null) ? 0 : context.hashCode()); result = prime * result + ((match == null) ? 0 : match.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BoostingQuery other = (BoostingQuery) obj; if (Float.floatToIntBits(boost) != Float.floatToIntBits(other.boost)) return false; if (context == null) { if (other.context != null) return false; } else if (!context.equals(other.context)) return false; if (match == null) { if (other.match != null) return false; } else if (!match.equals(other.match)) return false; return true; } @Override public String toString(String field) { return match.toString(field) + "/" + context.toString(field); } }