mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-10 03:27:54 +00:00
fix(search): improve short skill query matching
This commit is contained in:
parent
835b0da397
commit
091a5fe190
2 changed files with 49 additions and 5 deletions
|
|
@ -17,6 +17,7 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
private static final int MAX_QUERY_TERMS = 8;
|
||||
private static final int SHORT_PREFIX_LENGTH = 2;
|
||||
private static final String TITLE_VECTOR_SQL = "to_tsvector('simple', coalesce(title, ''))";
|
||||
private static final String TITLE_SQL = "LOWER(title)";
|
||||
|
||||
private final EntityManager entityManager;
|
||||
|
||||
|
|
@ -58,11 +59,14 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
|
||||
// Full-text search
|
||||
if (hasKeyword) {
|
||||
sql.append("AND (");
|
||||
if (useShortPrefixTitleSearch) {
|
||||
sql.append("AND ").append(TITLE_VECTOR_SQL).append(" @@ to_tsquery('simple', :tsQuery) ");
|
||||
sql.append(TITLE_VECTOR_SQL).append(" @@ to_tsquery('simple', :tsQuery) ");
|
||||
} else {
|
||||
sql.append("AND search_vector @@ to_tsquery('simple', :tsQuery) ");
|
||||
sql.append("search_vector @@ to_tsquery('simple', :tsQuery) ");
|
||||
}
|
||||
sql.append(" OR ").append(TITLE_SQL).append(" LIKE :titleLike");
|
||||
sql.append(") ");
|
||||
}
|
||||
|
||||
// Sorting
|
||||
|
|
@ -73,11 +77,16 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
} else if ("newest".equals(query.sortBy())) {
|
||||
sql.append("ORDER BY (SELECT updated_at FROM skill WHERE id = skill_id) DESC ");
|
||||
} else if ("relevance".equals(query.sortBy()) && hasKeyword) {
|
||||
sql.append("ORDER BY CASE ");
|
||||
sql.append("WHEN ").append(TITLE_SQL).append(" = :titleExact THEN 4 ");
|
||||
sql.append("WHEN ").append(TITLE_SQL).append(" LIKE :titlePrefix THEN 3 ");
|
||||
sql.append("WHEN ").append(TITLE_SQL).append(" LIKE :titleLike THEN 2 ");
|
||||
sql.append("ELSE 1 END DESC, ");
|
||||
if (useShortPrefixTitleSearch) {
|
||||
sql.append("ORDER BY ts_rank_cd(").append(TITLE_VECTOR_SQL)
|
||||
sql.append("ts_rank_cd(").append(TITLE_VECTOR_SQL)
|
||||
.append(", to_tsquery('simple', :tsQuery)) DESC, updated_at DESC ");
|
||||
} else {
|
||||
sql.append("ORDER BY ts_rank_cd(search_vector, to_tsquery('simple', :tsQuery)) DESC, updated_at DESC ");
|
||||
sql.append("ts_rank_cd(search_vector, to_tsquery('simple', :tsQuery)) DESC, updated_at DESC ");
|
||||
}
|
||||
} else {
|
||||
sql.append("ORDER BY updated_at DESC ");
|
||||
|
|
@ -100,6 +109,9 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
|
||||
if (hasKeyword) {
|
||||
nativeQuery.setParameter("tsQuery", tsQuery);
|
||||
nativeQuery.setParameter("titleExact", normalizedKeyword.toLowerCase());
|
||||
nativeQuery.setParameter("titlePrefix", normalizedKeyword.toLowerCase() + "%");
|
||||
nativeQuery.setParameter("titleLike", "%" + normalizedKeyword.toLowerCase() + "%");
|
||||
}
|
||||
|
||||
nativeQuery.setParameter("limit", query.size());
|
||||
|
|
@ -135,6 +147,7 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
|
||||
if (hasKeyword) {
|
||||
countQuery.setParameter("tsQuery", tsQuery);
|
||||
countQuery.setParameter("titleLike", "%" + normalizedKeyword.toLowerCase() + "%");
|
||||
}
|
||||
|
||||
long total = ((Number) countQuery.getSingleResult()).longValue();
|
||||
|
|
@ -146,7 +159,7 @@ public class PostgresFullTextQueryService implements SearchQueryService {
|
|||
if (keyword == null || keyword.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return keyword.trim();
|
||||
return keyword.trim().toLowerCase();
|
||||
}
|
||||
|
||||
private String buildPrefixTsQuery(String keyword) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class PostgresFullTextQueryServiceTest {
|
|||
var sqlCaptor = org.mockito.ArgumentCaptor.forClass(String.class);
|
||||
verify(entityManager, org.mockito.Mockito.times(2)).createNativeQuery(sqlCaptor.capture());
|
||||
assertThat(sqlCaptor.getAllValues().getFirst()).contains("to_tsvector('simple', coalesce(title, '')) @@ to_tsquery('simple', :tsQuery)");
|
||||
assertThat(sqlCaptor.getAllValues().getFirst()).contains("LOWER(title) LIKE :titleLike");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -107,6 +108,36 @@ class PostgresFullTextQueryServiceTest {
|
|||
assertThat(sqlCaptor.getAllValues().getFirst()).contains("ts_rank_cd(search_vector, to_tsquery('simple', :tsQuery))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shortPrefixRelevanceShouldRankUsingTitleVectorWithoutDuplicateOrderBy() {
|
||||
EntityManager entityManager = mock(EntityManager.class);
|
||||
Query nativeQuery = mock(Query.class);
|
||||
Query countQuery = mock(Query.class);
|
||||
when(entityManager.createNativeQuery(anyString()))
|
||||
.thenReturn(nativeQuery)
|
||||
.thenReturn(countQuery);
|
||||
when(nativeQuery.setParameter(anyString(), org.mockito.ArgumentMatchers.any())).thenReturn(nativeQuery);
|
||||
when(countQuery.setParameter(anyString(), org.mockito.ArgumentMatchers.any())).thenReturn(countQuery);
|
||||
when(nativeQuery.getResultList()).thenReturn(List.of());
|
||||
when(countQuery.getSingleResult()).thenReturn(0L);
|
||||
|
||||
PostgresFullTextQueryService service = new PostgresFullTextQueryService(entityManager);
|
||||
|
||||
service.search(new SearchQuery(
|
||||
"x",
|
||||
null,
|
||||
new SearchVisibilityScope(null, Set.of(), Set.of()),
|
||||
"relevance",
|
||||
0,
|
||||
20
|
||||
));
|
||||
|
||||
var sqlCaptor = org.mockito.ArgumentCaptor.forClass(String.class);
|
||||
verify(entityManager, org.mockito.Mockito.times(2)).createNativeQuery(sqlCaptor.capture());
|
||||
assertThat(sqlCaptor.getAllValues().getFirst()).contains("ts_rank_cd(to_tsvector('simple', coalesce(title, '')), to_tsquery('simple', :tsQuery))");
|
||||
assertThat(sqlCaptor.getAllValues().getFirst()).doesNotContain("ORDER BY ORDER BY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleTermsShouldBuildPrefixQueryForEachLexeme() {
|
||||
EntityManager entityManager = mock(EntityManager.class);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue