Raised from JasperFx/marten#5298, a request for ranked full-text search. Part of that feature — per-member weighting via setweight — cannot be built on the Marten side alone, because this definition unconditionally wraps whatever it is given in to_tsvector.
The constraint
Weasel.Postgresql/Tables/Indexes/FullTextIndexDefinition.cs:47:
public override string[] Columns
{
get => new[] { $"to_tsvector('{regConfig}',{DocumentConfig.Trim()})" };
set { /* nothing */ }
}
DocumentConfig is therefore always treated as text to be converted. There is no way to hand this type an expression that is already a tsvector.
Why that blocks the feature
PostgreSQL's weighting works by labelling each member's vector and concatenating the vectors, not the text (textsearch-features):
create index mt_doc_achievement_idx_fts on public.mt_doc_achievement using gin ((
setweight(to_tsvector('english', coalesce(d.data ->> 'Title', '')), 'A') ||
setweight(to_tsvector('english', coalesce(d.data ->> 'Tagline', '')), 'B') ||
setweight(to_tsvector('english', coalesce(d.data ->> 'Description', '')), 'C')
));
The expression is a tsvector at the top level. Passing it as DocumentConfig yields to_tsvector('english', setweight(...) || setweight(...)) — a type error, not a weighted index. So a Marten-side change can produce the right inner text and still not emit this DDL.
Today Marten concatenates the members as text before a single to_tsvector, which is exactly what the current shape supports and why weighting has never come up:
gin (to_tsvector('english', ((d.data ->> 'Title') || ' ' || (d.data ->> 'Description'))))
Shape of a fix
The suggestion in the Marten issue is a second property — say TsVectorExpression — that is consumed by Columns without the to_tsvector wrapping, with DocumentConfig continuing to behave exactly as it does now when it is not set. That keeps every existing index byte-identical and avoids a schema diff for anyone not opting in, which matters because a changed index expression means Weasel drops and recreates the index — on a large table, an outage rather than a migration.
Whatever the shape, the requirement is: one property that both the DDL and the consumer's query-side filter read, so the indexed vector and the searched vector cannot drift apart. Marten's FullTextWhereFragment already reads the expression back off the index definition for exactly this reason, and ranking makes the coupling stricter still — a ts_rank computed over a different vector than the @@ filtered on is silently wrong rather than merely slow.
Not urgent
Nothing is broken today; this is a capability gap. Marten is on Weasel 9.27.0 and the ranking work is not scheduled — the Marten issue splits it so that ts_rank ordering (Marten-only, no dependency here) can land first and setweight follows a Weasel release. Filing now so the dependency is recorded rather than rediscovered when that half comes up.
Raised from JasperFx/marten#5298, a request for ranked full-text search. Part of that feature — per-member weighting via
setweight— cannot be built on the Marten side alone, because this definition unconditionally wraps whatever it is given into_tsvector.The constraint
Weasel.Postgresql/Tables/Indexes/FullTextIndexDefinition.cs:47:DocumentConfigis therefore always treated as text to be converted. There is no way to hand this type an expression that is already atsvector.Why that blocks the feature
PostgreSQL's weighting works by labelling each member's vector and concatenating the vectors, not the text (textsearch-features):
The expression is a
tsvectorat the top level. Passing it asDocumentConfigyieldsto_tsvector('english', setweight(...) || setweight(...))— a type error, not a weighted index. So a Marten-side change can produce the right inner text and still not emit this DDL.Today Marten concatenates the members as text before a single
to_tsvector, which is exactly what the current shape supports and why weighting has never come up:Shape of a fix
The suggestion in the Marten issue is a second property — say
TsVectorExpression— that is consumed byColumnswithout theto_tsvectorwrapping, withDocumentConfigcontinuing to behave exactly as it does now when it is not set. That keeps every existing index byte-identical and avoids a schema diff for anyone not opting in, which matters because a changed index expression means Weasel drops and recreates the index — on a large table, an outage rather than a migration.Whatever the shape, the requirement is: one property that both the DDL and the consumer's query-side filter read, so the indexed vector and the searched vector cannot drift apart. Marten's
FullTextWhereFragmentalready reads the expression back off the index definition for exactly this reason, and ranking makes the coupling stricter still — ats_rankcomputed over a different vector than the@@filtered on is silently wrong rather than merely slow.Not urgent
Nothing is broken today; this is a capability gap. Marten is on Weasel 9.27.0 and the ranking work is not scheduled — the Marten issue splits it so that
ts_rankordering (Marten-only, no dependency here) can land first andsetweightfollows a Weasel release. Filing now so the dependency is recorded rather than rediscovered when that half comes up.