In Lucene 6.6.0 and greater, Field level index time boosting is deprecated. The documentation states:
Index-time boosts are deprecated, please index index-time scoring
factors into a doc value field and combine them with the score at
query time using eg. FunctionScoreQuery.
Previously one would boost a field at index time like so:
Field title = new Field(PaperDAO.LUCENE_FIELD_TITLE, titleStr, fieldType);
title.setBoost(3.00f);
document.add(title);
Field authors = new Field(PaperDAO.LUCENE_FIELD_AUTHOR, StringEscapeUtils.unescapeHtml4(this.getAuthorsForLucene()), fieldType);
authors.setBoost(10.00f);
document.add(authors);
I do not understand how the suggested FunctionScoreQuery is an appropriate replacement for field level boosting, as one constructs a FunctionScoreQuery given only an existing Query and a DoubleValuesSource representing the boost value for only one of potentially many fields:
// INDEX TIME
Field title = new Field(PaperDAO.LUCENE_FIELD_TITLE, titleStr, fieldType);
document.add(title);
document.add(new FloatDocValuesField(PaperDAO.LUCENE_FIELD_TITLE + "_boost", 3.00f));
// QUERY TIME
new FunctionScoreQuery(query, DoubleValuesSource.fromFloatField(PaperDAO.LUCENE_FIELD_TITLE + "_boost"))
Can someone please explain the appropriate replacement for Field#setBoost # index time in Lucene >= 6.6.0? Are we supposed to be enumerating all possible fields at query time and applying the relevant boost? If so, how is that query constructed?
First of all, you still have some time to use old-style index time boosts, since they only will be remove in Lucene 7.0 :)
Moving on to the subject, community long time ago decided, that index-time boost is a complex and hard to get it right technique.
What I think is the current idea - is not to replace per-field index time boost with per field docvalues field, but rather replace all index time boosts for a document with 1 accumulated score in the docvalues field and later use it during search.
please index index-time scoring factors into a doc value field
and combine them with the score at query time
The quote is from the javadoc, which only strengthen me in this idea. You could index multiple factors into just one field.
The open question to me is - how to combine several factors into 1. I hope that's something to test and validate (to use multiplication, sum or some linear combination)
if you want to boost different fields using FunctionScoreQuery, the suggested method is the following (taken from CustomeScoreProvider):
For more complex custom scores, use the lucene-expressions library
SimpleBindings bindings = new SimpleBindings();
bindings.add("score", DoubleValuesSource.SCORES);
bindings.add("boost1", DoubleValuesSource.fromIntField("myboostfield"));
bindings.add("boost2", DoubleValuesSource.fromIntField("myotherboostfield"));
Expression expr = JavascriptCompiler.compile("score * (boost1 + ln(boost2))");
FunctionScoreQuery q = new FunctionScoreQuery(inputQuery, expr.getDoubleValuesSource(bindings));
Related
Boost is by-Field in Lucene, and that is then set in the Index. You can add multiple Fields with the Name property set to the same value, and they'll then be searched for that Document as though you just added the union of those Terms to that Document under that Field name.
var field1 = new Field("Text", "aquaculture");
var field2 = new Field("Text", "fish");
field1.setBoost(1.0f);
field2.setBoost(2.0f);
var doc = new Document();
doc.AddField(field1);
doc.AddField(field2);
But you can set the Boost to multiple values as you do so - if you do, what happens? Do the Terms in each Field get set to the separate Boost levels, or does one shared Boost level get used? If one, is it Last-In, First-In, random?
(The above code is pseudocode just to illustrate the Fields getting added to a single Doc to help coders visualize it, I realize the actual code to do the above is a bit different in actual Lucene API)
For the current version of Lucene, this question is no longer relevant. Setting boosts on fields was deprecated in 6.5, and no longer supported in 7.0. See LUCENE-6819 for discussion. The replacement, per the migration guide:
...index-time scoring factors should be indexed in a doc value field and combined with the score at query time using FunctionScoreQuery for instance.
In previous versions, if multiple boosts added to the same field, they are multiplied. This specified by the FieldInvertState, which is the information that gets passed to the Similarity, which performs the translation of that data into what gets stored in the index.
This is the cumulative product of document boost and field boost for all field instances sharing the same field name.
Using SOLR version 4.3, it appears that SOLR is valuing the percentage of matching terms more than the number of matching terms.
For example, we do a search for Dog and a document with just the word dog and a three other words returns. We have another article with hundreds of words, that has the word dog in it 27 times.
I would expect the second article to return first. However, the one with one word out of three returns first. I was hoping to find out what in SOLR controls this so I can make the appropriate modifications. I have looked the SOLR documentation and have seen COORD mentioned, but it seems to indicate that the article with 27 references should return first. Any help would be appreciated.
For 4.x Solr still used regular TF/IDF as its scoring formula, and you can see the Lucene implementation detailed in the documentation for TFIDFSimilarity.
For your question, the two factors that affect the score is:
The length of the field, as given in norm():
norm(t,d) encapsulates a few (indexing time) boost and length factors:
Field boost - set by calling field.setBoost() before adding the field to a document.
lengthNorm - computed when the document is added to the index in accordance with the number of tokens of this field in the document, so that shorter fields contribute more to the score. LengthNorm is computed by the Similarity class in effect at indexing.
.. while the number of terms matching (not their frequency), is given by coord():
coord(q,d) is a score factor based on how many of the query terms are found in the specified document. Typically, a document that contains more of the query's terms will receive a higher score than another document with fewer query terms. This is a search time factor computed in coord(q,d) by the Similarity in effect at search time.
There are a few settings in your schema that can affect how Solr scores the documents in your example:
omitNorms
If true, omits the norms associated with this field (this disables length normalization for the field, and saves some memory)
.. this will remove the norm() part of the score.
omitTermFreqAndPositions
If true, omits term frequency, positions, and payloads from postings for this field.
.. and this will remove the boost from multiple occurrences of the same term. Be aware that this will remove positions as well, making phrase queries impossible.
But you should also consider upgrading Solr, as the BM25 similarity that's the default from 6.x usually performs better. I can't remember if a version is available for 4.3.
I have 2 documents, and am searching for the keyword "Twitter". Suppose both documents are blog posts with a "tags" field.
Document A has ONLY 1 term in the "tags" field, and it's "Twitter".
Document B has 100 terms in the "tags" field, but 3 of them is "Twitter".
Elastic Search gives the higher score to Document A even though Document B has a higher frequency. But the score is "diluted" because it has more terms. How do I give Document B a higher score, since it has a higher frequency of the search term?
I know ElasticSearch/Lucene performs some normalization based on the number of terms in the document. How can I disable this normalization, so that Document B gets a higher score above?
As the other answer says it would be interesting to see whether you have the same result on a single shard. I think you would and that depends on the norms for the tags field, which is taken into account when computing the score using the tf/idf similarity (default).
In fact, lucene does take into account the term frequency, in other words the number of times the term appears within the field (1 or 3 in your case), and the inverted document frequency, in other words how the term is frequent in the index, in order to compare it with other terms in the query (in your case it doesn't make any difference if you are searching for a single term).
But there's another factor called norms, that rewards shorter fields and take into account eventual index time boosting, which can be per field (in the mapping) or even per document. You can verify that norms are the reason of your result enabling the explain option in your search request and looking at the explain output.
I guess the fact that the first document contains only that tag makes it more important that the other ones that contains that tag multiple times but a lot of ther tags as well. If you don't like this behaviour you can just disable norms in your mapping for the tags field. It should be enabled by default if the field is "index":"analyzed" (default). You can either switch to "index":"not_analyzed" if you don't want your tags field to be analyzed (it usually makes sense but depends on your data and domain) or add the "omit_norms": true option in the mapping for your tags field.
Are the documents found on different shards? From Elastic search documentation:
"When a query is executed on a specific shard, it does not take into account term frequencies and other search engine information from the other shards. If we want to support accurate ranking, we would need to first execute the query against all shards and gather the relevant term frequencies, and then, based on it, execute the query."
The solution is to specify the search type. Use dfs_query_and_fetch search type to execute an initial scatter phase which goes and computes the distributed term frequencies for more accurate scoring.
You can read more here.
Is it possible to determine that specific terms are more important then other when creating the index (not when querying it) ?
Consider for example a synonym filter:
doc 1: "this is a nice car"
doc 2: "this is a nice vehicle"
I want to add the term vehicle to the first doc and the term car to the second doc,
but I want that if later the index is queried with the word car then the first document will be scored higher then the second one and if queried for vehicle it will be the other way around.
Will calling setBoost on the fields before adding them to their respective documents do the trick?
Or maybe I should add the synonyms to a different field name?
Or am I looking at this from a wrong point of view ?
Thanks
Setting boost on a filed affects all terms in that field so this wouldn't work in your case.
But it should be posible using Lucene payloads (a byte array that can be set for every term). You would use them to set term specific boosts (vehicle to 0.5 for doc 1, for example). Then you'll implement your own Similarity and override scorePayload() method to decode that boost and then use PayloadTermQuery which allows you to contribute to the score based on the boots you have in the payload for that term.
I am storing various articles in my lucene index.
When user searches for articles which contain a specific term or phrase,I need to show all th articles (could be anywhere between 1000 to 10000 articles) but with newest articles "bubbled up" in the search results.
I believe you can bubble up a search result in Lucene using "Date field Boosting".
Can someone please give me the details of how to go about this?
Thanks in advance!
I would implement the SortComparatorSource interface. You should write a new ScoreDocComparator, whose compare() function compares two dates. Then you will need to sort your searches using the new sorter. This advice is taken from chapter 6 of Lucene in Action.
You can use the setBoost method to set the "boost" for a particular document in the index at index time. Since the default boost value is 1.0, setting a value less than 1.0 will make the document "less relevant" in search results. By tying the boost value of a document to its age (lower boost the older the document gets), you can make newer content seem more relevant in search results.
Note in the documentation for setBoost that the boost value set at indexing time is not available for retrieved documents (boost works, you just can't read the value back at retrieval time to see if you applied the correct value at index time).