Browse Source

Optimizing

imwald
Nuša Pukšič 3 months ago
parent
commit
4a2d7786e2
  1. 13
      config/packages/fos_elastica.yaml
  2. 124
      src/Twig/Components/SearchComponent.php

13
config/packages/fos_elastica.yaml

@ -16,6 +16,12 @@ fos_elastica:
# Optimize indexing # Optimize indexing
number_of_shards: 1 number_of_shards: 1
number_of_replicas: 0 number_of_replicas: 0
# Add query cache settings for faster repeated searches
queries:
cache:
enabled: true
# Optimize for search performance
max_result_window: 10000
analysis: analysis:
analyzer: analyzer:
custom_analyzer: custom_analyzer:
@ -29,16 +35,23 @@ fos_elastica:
title: title:
type: text type: text
analyzer: custom_analyzer analyzer: custom_analyzer
# Add term_vector for faster phrase queries
term_vector: with_positions_offsets
content: content:
type: text type: text
analyzer: custom_analyzer analyzer: custom_analyzer
# Don't store norms for content to save space and improve speed
norms: false
summary: summary:
type: text type: text
analyzer: custom_analyzer analyzer: custom_analyzer
term_vector: with_positions_offsets
tags: tags:
type: keyword type: keyword
slug: slug:
type: keyword type: keyword
# Enable doc_values for faster sorting/aggregations
doc_values: true
pubkey: pubkey:
type: keyword type: keyword
topics: ~ topics: ~

124
src/Twig/Components/SearchComponent.php

@ -131,14 +131,8 @@ final class SearchComponent
$this->creditsManager->spendCredits($this->npub, 1, 'search'); $this->creditsManager->spendCredits($this->npub, 1, 'search');
$this->credits--; $this->credits--;
// Step 1: Run a quick naive query on title and summary only // Perform optimized single search query
$quickResults = $this->performQuickSearch($this->query); $this->results = $this->performOptimizedSearch($this->query);
// Step 2: Run the comprehensive query
$comprehensiveResults = $this->performComprehensiveSearch($this->query);
// Combine results, making sure we don't have duplicates
$this->results = $this->mergeSearchResults($quickResults, $comprehensiveResults);
// Cache the search results in session // Cache the search results in session
$this->saveSearchToSession($this->query, $this->results); $this->saveSearchToSession($this->query, $this->results);
@ -172,59 +166,14 @@ final class SearchComponent
} }
/** /**
* Perform a quick search on title and summary only * Perform optimized single search query
*/ */
private function performQuickSearch(string $query): array private function performOptimizedSearch(string $query): array
{ {
$mainQuery = new Query(); $mainQuery = new Query();
// Simple multi-match query for searching across title and summary only
$multiMatch = new MultiMatch();
$multiMatch->setQuery($query);
$multiMatch->setFields([
'title^5', // Increased weight for title
'summary^3' // Increased weight for summary
]);
$multiMatch->setType(MultiMatch::TYPE_BEST_FIELDS); // Changed to BEST_FIELDS for more precise matching
$multiMatch->setOperator(MultiMatch::OPERATOR_AND); // Require all terms to match for better precision
$boolQuery = new BoolQuery(); $boolQuery = new BoolQuery();
$boolQuery->addMust($multiMatch);
$boolQuery->addMustNot(new Query\Wildcard('slug', '*/*'));
$mainQuery->setQuery($boolQuery);
// Use the collapse field to prevent duplicate content // Add phrase match for exact matches (high boost)
$mainQuery->setParam('collapse', [
'field' => 'slug'
]);
// Set a minimum score to filter out irrelevant results
$mainQuery->setMinScore(0.5); // Higher minimum score for quick results
// Sort by relevance only for quick results
$mainQuery->setSort(['_score' => ['order' => 'desc']]);
// Limit to 5 results for the quick search
$mainQuery->setSize(5);
// Execute the quick search
$results = $this->finder->find($mainQuery);
$this->logger->info('Quick search results count: ' . count($results));
return $results;
}
/**
* Perform a comprehensive search across all fields
*/
private function performComprehensiveSearch(string $query): array
{
$mainQuery = new Query();
// Build bool query with multiple conditions for more precise matching
$boolQuery = new BoolQuery();
// Add exact phrase match with high boost for very relevant results
$phraseMatch = new Query\MatchPhrase(); $phraseMatch = new Query\MatchPhrase();
$phraseMatch->setField('title', [ $phraseMatch->setField('title', [
'query' => $query, 'query' => $query,
@ -232,86 +181,51 @@ final class SearchComponent
]); ]);
$boolQuery->addShould($phraseMatch); $boolQuery->addShould($phraseMatch);
// Add regular multi-match with adjusted weights // Main multi-match query with optimized settings
$multiMatch = new MultiMatch(); $multiMatch = new MultiMatch();
$multiMatch->setQuery($query); $multiMatch->setQuery($query);
$multiMatch->setFields([ $multiMatch->setFields([
'title^4', 'title^5',
'summary^3', 'summary^3',
'content^1.2', 'content^1.5',
'topics^2' 'topics^2'
]); ]);
$multiMatch->setType(MultiMatch::TYPE_MOST_FIELDS); $multiMatch->setType(MultiMatch::TYPE_BEST_FIELDS); // Faster than MOST_FIELDS
$multiMatch->setFuzziness('AUTO'); $multiMatch->setFuzziness('AUTO');
$multiMatch->setOperator(MultiMatch::OPERATOR_AND); // Require all terms to match $multiMatch->setOperator(MultiMatch::OPERATOR_OR); // OR is faster and more forgiving
$boolQuery->addMust($multiMatch); $boolQuery->addMust($multiMatch);
// Exclude specific patterns // Exclude specific patterns
$boolQuery->addMustNot(new Query\Wildcard('slug', '*/*')); $boolQuery->addMustNot(new Query\Wildcard('slug', '*/*'));
// For content relevance, filter by minimum content length
$lengthFilter = new Query\QueryString();
$lengthFilter->setQuery('content:/.{250,}/');
$boolQuery->addFilter($lengthFilter);
$mainQuery->setQuery($boolQuery); $mainQuery->setQuery($boolQuery);
// Use the collapse field // Simplified collapse - no inner_hits for better performance
$mainQuery->setParam('collapse', [ $mainQuery->setParam('collapse', [
'field' => 'slug', 'field' => 'slug'
'inner_hits' => [
'name' => 'latest_articles',
'size' => 1
]
]); ]);
// Increase minimum score to filter out irrelevant results // Lower minimum score for better recall
$mainQuery->setMinScore(0.35); $mainQuery->setMinScore(0.25);
// Sort by score and createdAt // Sort by score first, then date
$mainQuery->setSort([ $mainQuery->setSort([
'_score' => ['order' => 'desc'], '_score' => ['order' => 'desc'],
'createdAt' => ['order' => 'desc'] 'createdAt' => ['order' => 'desc']
]); ]);
// Add pagination for the comprehensive results // Pagination
// Adjust the pagination to account for the quick results $offset = ($this->page - 1) * $this->resultsPerPage;
$offset = ($this->page - 1) * ($this->resultsPerPage - 5);
if ($offset < 0) $offset = 0;
$mainQuery->setFrom($offset); $mainQuery->setFrom($offset);
$mainQuery->setSize($this->resultsPerPage - 5); $mainQuery->setSize($this->resultsPerPage);
// Execute the search // Execute the search
$results = $this->finder->find($mainQuery); $results = $this->finder->find($mainQuery);
$this->logger->info('Comprehensive search results count: ' . count($results)); $this->logger->info('Search results count: ' . count($results));
return $results; return $results;
} }
/**
* Merge quick and comprehensive search results, ensuring no duplicates
*/
private function mergeSearchResults(array $quickResults, array $comprehensiveResults): array
{
$mergedResults = $quickResults;
$slugs = [];
// Collect slugs from quick results to avoid duplicates
foreach ($quickResults as $result) {
$slugs[] = $result->getSlug();
}
// Add comprehensive results that aren't already in quick results
foreach ($comprehensiveResults as $result) {
if (!in_array($result->getSlug(), $slugs)) {
$mergedResults[] = $result;
$slugs[] = $result->getSlug();
}
}
return $mergedResults;
}
#[LiveListener('creditsAdded')] #[LiveListener('creditsAdded')]
public function incrementCreditsCount(): void public function incrementCreditsCount(): void

Loading…
Cancel
Save