Skip to content

Commit 54d355c

Browse files
authored
Merge branch 'apache:master' into async-replication
2 parents 4a06a5d + 0a8d39c commit 54d355c

2 files changed

Lines changed: 168 additions & 4 deletions

File tree

src/Lucene.Net.QueryParser/Classic/MultiFieldQueryParser.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ protected internal override Query GetFieldQuery(string field, string queryText,
120120
if (m_boosts != null)
121121
{
122122
//Get the boost from the map and apply them
123-
float boost = m_boosts[m_fields[i]];
124-
q.Boost = boost;
123+
if (m_boosts.TryGetValue(m_fields[i], out float boost))
124+
{
125+
q.Boost = boost;
126+
}
125127
}
126128
ApplySlop(q, slop);
127129
clauses.Add(new BooleanClause(q, Occur.SHOULD));
@@ -163,8 +165,10 @@ protected internal override Query GetFieldQuery(string field, string queryText,
163165
if (m_boosts != null)
164166
{
165167
//Get the boost from the map and apply them
166-
float boost = m_boosts[m_fields[i]];
167-
q.Boost = boost;
168+
if (m_boosts.TryGetValue(m_fields[i], out float boost))
169+
{
170+
q.Boost = boost;
171+
}
168172
}
169173
clauses.Add(new BooleanClause(q, Occur.SHOULD));
170174
}

src/Lucene.Net.Tests.QueryParser/Classic/TestMultiFieldQueryParser.cs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Lucene.Net.Analysis;
2+
using Lucene.Net.Attributes;
23
using Lucene.Net.Documents;
34
using Lucene.Net.Index;
45
using Lucene.Net.Search;
@@ -385,5 +386,164 @@ public virtual void TestSimpleRegex()
385386
bq.Add(new RegexpQuery(new Term("b", "[a-z][123]")), Occur.SHOULD);
386387
assertEquals(bq, mfqp.Parse("/[a-z][123]/"));
387388
}
389+
390+
[Test]
391+
[LuceneNetSpecific] // LUCENENET specific - Issue #1157
392+
public virtual void TestFieldBoostsWithPartialBoostMap()
393+
{
394+
string[] fields = { "title", "keyword", "description" };
395+
MockAnalyzer analyzer = new MockAnalyzer(Random);
396+
397+
// Create a boost map that only contains boosts for some fields, not all
398+
// This tests that the TryGetValue fix prevents KeyNotFoundException
399+
var boosts = new Dictionary<string, float>
400+
{
401+
{ "title", 2.0f },
402+
// Intentionally omitting "keyword" and "description" from boost map
403+
};
404+
405+
MultiFieldQueryParser parser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, boosts);
406+
Query q = parser.Parse("test");
407+
408+
// The query should successfully parse without throwing KeyNotFoundException
409+
string queryString = q.toString();
410+
assertTrue("Query should contain boosted title field", queryString.Contains("title:test^2.0"));
411+
assertTrue("Query should contain keyword field without boost", queryString.Contains("keyword:test"));
412+
assertTrue("Query should contain description field without boost", queryString.Contains("description:test"));
413+
414+
// Ensure no boost notation for fields not in the boost map
415+
assertFalse("Keyword should not have boost notation", queryString.Contains("keyword:test^"));
416+
assertFalse("Description should not have boost notation", queryString.Contains("description:test^"));
417+
}
418+
419+
[Test]
420+
[LuceneNetSpecific] // LUCENENET specific - Issue #1157
421+
public virtual void TestFieldBoosts()
422+
{
423+
string[] fields = { "title", "keyword" };
424+
MockAnalyzer analyzer = new MockAnalyzer(Random);
425+
426+
// Test 1: Verify boosts are applied to the query string representation
427+
var boosts = new Dictionary<string, float>
428+
{
429+
{ "title", 2.0f },
430+
{ "keyword", 1.0f }
431+
};
432+
433+
MultiFieldQueryParser parser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, boosts);
434+
Query q = parser.Parse("ldqk");
435+
436+
// The query should have different boosts for each field
437+
string queryString = q.toString();
438+
assertTrue("Query should contain boosted title field", queryString.Contains("title:ldqk^2.0"));
439+
assertFalse("Keyword field should not have boost notation when boost is 1.0", queryString.Contains("keyword:ldqk^"));
440+
441+
// Test 2: Different boost configuration
442+
var boosts2 = new Dictionary<string, float>
443+
{
444+
{ "title", 1.0f },
445+
{ "keyword", 2.0f }
446+
};
447+
448+
MultiFieldQueryParser parser2 = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, boosts2);
449+
Query q2 = parser2.Parse("ldqk");
450+
451+
string queryString2 = q2.toString();
452+
assertFalse("Title field should not have boost notation when boost is 1.0", queryString2.Contains("title:ldqk^"));
453+
assertTrue("Query should contain boosted keyword field", queryString2.Contains("keyword:ldqk^2.0"));
454+
455+
// Test 3: Verify that boosts actually affect document scoring
456+
using var ramDir = NewDirectory();
457+
using (IndexWriter iw = new IndexWriter(ramDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, analyzer)))
458+
{
459+
// Doc 0: "ldqk" only in title
460+
Document doc0 = new Document();
461+
doc0.Add(NewTextField("title", "ldqk", Field.Store.YES));
462+
doc0.Add(NewTextField("keyword", "other", Field.Store.YES));
463+
iw.AddDocument(doc0);
464+
465+
// Doc 1: "ldqk" only in keyword
466+
Document doc1 = new Document();
467+
doc1.Add(NewTextField("title", "other", Field.Store.YES));
468+
doc1.Add(NewTextField("keyword", "ldqk", Field.Store.YES));
469+
iw.AddDocument(doc1);
470+
}
471+
472+
using (IndexReader ir = DirectoryReader.Open(ramDir))
473+
{
474+
IndexSearcher searcher = NewSearcher(ir);
475+
476+
// Test with equal boosts first (baseline)
477+
var equalBoosts = new Dictionary<string, float>
478+
{
479+
{ "title", 1.0f },
480+
{ "keyword", 1.0f }
481+
};
482+
MultiFieldQueryParser equalParser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, equalBoosts);
483+
Query equalQuery = equalParser.Parse("ldqk");
484+
TopDocs equalDocs = searcher.Search(equalQuery, 10);
485+
486+
// Get baseline scores
487+
float doc0BaseScore = 0, doc1BaseScore = 0;
488+
foreach (var scoreDoc in equalDocs.ScoreDocs)
489+
{
490+
if (scoreDoc.Doc == 0) doc0BaseScore = scoreDoc.Score;
491+
if (scoreDoc.Doc == 1) doc1BaseScore = scoreDoc.Score;
492+
}
493+
494+
// Search with title boosted 2.0
495+
var titleBoosts = new Dictionary<string, float>
496+
{
497+
{ "title", 2.0f },
498+
{ "keyword", 1.0f }
499+
};
500+
MultiFieldQueryParser titleParser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, titleBoosts);
501+
Query titleQuery = titleParser.Parse("ldqk");
502+
TopDocs titleDocs = searcher.Search(titleQuery, 10);
503+
504+
// Get scores with title boost
505+
float doc0TitleBoostScore = 0, doc1TitleBoostScore = 0;
506+
foreach (var scoreDoc in titleDocs.ScoreDocs)
507+
{
508+
if (scoreDoc.Doc == 0) doc0TitleBoostScore = scoreDoc.Score;
509+
if (scoreDoc.Doc == 1) doc1TitleBoostScore = scoreDoc.Score;
510+
}
511+
512+
// Search with keyword boosted 2.0
513+
var keywordBoosts = new Dictionary<string, float>
514+
{
515+
{ "title", 1.0f },
516+
{ "keyword", 2.0f }
517+
};
518+
MultiFieldQueryParser keywordParser = new MultiFieldQueryParser(TEST_VERSION_CURRENT, fields, analyzer, keywordBoosts);
519+
Query keywordQuery = keywordParser.Parse("ldqk");
520+
TopDocs keywordDocs = searcher.Search(keywordQuery, 10);
521+
522+
// Get scores with keyword boost
523+
float doc0KeywordBoostScore = 0, doc1KeywordBoostScore = 0;
524+
foreach (var scoreDoc in keywordDocs.ScoreDocs)
525+
{
526+
if (scoreDoc.Doc == 0) doc0KeywordBoostScore = scoreDoc.Score;
527+
if (scoreDoc.Doc == 1) doc1KeywordBoostScore = scoreDoc.Score;
528+
}
529+
530+
// Assertions:
531+
// When title is boosted, doc0 (title match) should score higher than baseline
532+
assertTrue("Doc0 with title match should score higher when title is boosted compared to equal boosts",
533+
doc0TitleBoostScore > doc0BaseScore);
534+
535+
// When keyword is boosted, doc1 (keyword match) should score higher than baseline
536+
assertTrue("Doc1 with keyword match should score higher when keyword is boosted compared to equal boosts",
537+
doc1KeywordBoostScore > doc1BaseScore);
538+
539+
// Doc0 should score higher with title boost than with keyword boost
540+
assertTrue("Doc0 (title match) should score higher with title boost than keyword boost",
541+
doc0TitleBoostScore > doc0KeywordBoostScore);
542+
543+
// Doc1 should score higher with keyword boost than with title boost
544+
assertTrue("Doc1 (keyword match) should score higher with keyword boost than title boost",
545+
doc1KeywordBoostScore > doc1TitleBoostScore);
546+
}
547+
}
388548
}
389549
}

0 commit comments

Comments
 (0)