solr random fix

This commit is contained in:
Stefan Hardegger
2025-09-26 15:05:27 +02:00
parent 574f20bfd7
commit 74cdd5dc57
4 changed files with 98 additions and 33 deletions

View File

@@ -218,43 +218,91 @@ export const storyApi = {
hiddenGemsOnly?: boolean;
}): Promise<Story | null> => {
try {
// Create URLSearchParams to properly handle array parameters like tags
const searchParams = new URLSearchParams();
if (filters?.searchQuery) {
searchParams.append('searchQuery', filters.searchQuery);
}
if (filters?.tags && filters.tags.length > 0) {
filters.tags.forEach(tag => searchParams.append('tags', tag));
}
// Advanced filters
if (filters?.minWordCount !== undefined) searchParams.append('minWordCount', filters.minWordCount.toString());
if (filters?.maxWordCount !== undefined) searchParams.append('maxWordCount', filters.maxWordCount.toString());
if (filters?.createdAfter) searchParams.append('createdAfter', filters.createdAfter);
if (filters?.createdBefore) searchParams.append('createdBefore', filters.createdBefore);
if (filters?.lastReadAfter) searchParams.append('lastReadAfter', filters.lastReadAfter);
if (filters?.lastReadBefore) searchParams.append('lastReadBefore', filters.lastReadBefore);
if (filters?.minRating !== undefined) searchParams.append('minRating', filters.minRating.toString());
if (filters?.maxRating !== undefined) searchParams.append('maxRating', filters.maxRating.toString());
if (filters?.unratedOnly !== undefined) searchParams.append('unratedOnly', filters.unratedOnly.toString());
if (filters?.readingStatus) searchParams.append('readingStatus', filters.readingStatus);
if (filters?.hasReadingProgress !== undefined) searchParams.append('hasReadingProgress', filters.hasReadingProgress.toString());
if (filters?.hasCoverImage !== undefined) searchParams.append('hasCoverImage', filters.hasCoverImage.toString());
if (filters?.sourceDomain) searchParams.append('sourceDomain', filters.sourceDomain);
if (filters?.seriesFilter) searchParams.append('seriesFilter', filters.seriesFilter);
if (filters?.minTagCount !== undefined) searchParams.append('minTagCount', filters.minTagCount.toString());
if (filters?.popularOnly !== undefined) searchParams.append('popularOnly', filters.popularOnly.toString());
if (filters?.hiddenGemsOnly !== undefined) searchParams.append('hiddenGemsOnly', filters.hiddenGemsOnly.toString());
const response = await api.get(`/stories/random?${searchParams.toString()}`);
return response.data;
// Use proper Solr RandomSortField with dynamic field random_* for true randomness
// Each call generates a different random seed to ensure different random results
const randomSeed = Math.floor(Math.random() * 1000000);
const searchResult = await searchApi.search({
query: filters?.searchQuery || '*:*',
page: 0,
size: 1, // Only get one result - Solr RandomSortField considers entire dataset
authors: [],
tags: filters?.tags || [],
minRating: filters?.minRating,
maxRating: filters?.maxRating,
sortBy: `random_${randomSeed}`, // Use proper dynamic field with random seed
sortDir: 'desc',
// Advanced filters - pass through all filter options
minWordCount: filters?.minWordCount,
maxWordCount: filters?.maxWordCount,
createdAfter: filters?.createdAfter,
createdBefore: filters?.createdBefore,
lastReadAfter: filters?.lastReadAfter,
lastReadBefore: filters?.lastReadBefore,
unratedOnly: filters?.unratedOnly,
readingStatus: filters?.readingStatus,
hasReadingProgress: filters?.hasReadingProgress,
hasCoverImage: filters?.hasCoverImage,
sourceDomain: filters?.sourceDomain,
seriesFilter: filters?.seriesFilter,
minTagCount: filters?.minTagCount,
popularOnly: filters?.popularOnly,
hiddenGemsOnly: filters?.hiddenGemsOnly,
});
return searchResult.results && searchResult.results.length > 0
? searchResult.results[0]
: null;
} catch (error: any) {
if (error.response?.status === 204) {
if (error.response?.status === 404 || error.response?.status === 204) {
// No content - no stories match filters
return null;
}
throw error;
// If random sorting fails, fallback to client-side approach
console.warn('Solr random sorting failed, falling back to client-side selection:', error.message);
try {
// Fallback: get larger sample and pick randomly client-side
const fallbackResult = await searchApi.search({
query: filters?.searchQuery || '*:*',
page: 0,
size: 200, // Large enough sample for good randomness
authors: [],
tags: filters?.tags || [],
minRating: filters?.minRating,
maxRating: filters?.maxRating,
sortBy: 'createdAt',
sortDir: 'desc',
// Same advanced filters
minWordCount: filters?.minWordCount,
maxWordCount: filters?.maxWordCount,
createdAfter: filters?.createdAfter,
createdBefore: filters?.createdBefore,
lastReadAfter: filters?.lastReadAfter,
lastReadBefore: filters?.lastReadBefore,
unratedOnly: filters?.unratedOnly,
readingStatus: filters?.readingStatus,
hasReadingProgress: filters?.hasReadingProgress,
hasCoverImage: filters?.hasCoverImage,
sourceDomain: filters?.sourceDomain,
seriesFilter: filters?.seriesFilter,
minTagCount: filters?.minTagCount,
popularOnly: filters?.popularOnly,
hiddenGemsOnly: filters?.hiddenGemsOnly,
});
if (fallbackResult.results && fallbackResult.results.length > 0) {
const randomIndex = Math.floor(Math.random() * fallbackResult.results.length);
return fallbackResult.results[randomIndex];
}
return null;
} catch (fallbackError: any) {
throw fallbackError;
}
}
},
};