|
1 | 1 | using Lucene.Net.Analysis; |
| 2 | +using Lucene.Net.Attributes; |
2 | 3 | using Lucene.Net.Documents; |
3 | 4 | using Lucene.Net.Index; |
4 | 5 | using Lucene.Net.Search; |
@@ -385,5 +386,164 @@ public virtual void TestSimpleRegex() |
385 | 386 | bq.Add(new RegexpQuery(new Term("b", "[a-z][123]")), Occur.SHOULD); |
386 | 387 | assertEquals(bq, mfqp.Parse("/[a-z][123]/")); |
387 | 388 | } |
| 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 | + } |
388 | 548 | } |
389 | 549 | } |
0 commit comments