-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/metadata search with postgres fts #310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
0b2be19
Adding SchemaQuerySet plus the tsvector on Schema model
aaronjae22 d57e10c
Creating schema search vector migration
aaronjae22 232d5bc
Swapping index view to .search()
aaronjae22 3c51551
Renaming migration
aaronjae22 2690ae5
Adding tests plus fixing SearchRank string issue
aaronjae22 c9a2af1
Adding PostgreSQL guard test as comment until I fix some django.db.u…
aaronjae22 df498d5
Changing PostgreSQL to a config check
aaronjae22 f8b02d4
Dropping PostgreSQL check
aaronjae22 f8d7c1b
Fixing linting from unused config import
aaronjae22 62c92f8
Updating test to preserve order when search box is empty
aaronjae22 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Generated by Django 5.2.5 on 2026-07-17 21:19 | ||
|
|
||
| import django.contrib.postgres.indexes | ||
| import django.contrib.postgres.search | ||
| from django.conf import settings | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('core', '0016_schemaref_id_value'), | ||
| migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name='schema', | ||
| name='search_vector', | ||
| field=models.GeneratedField(db_persist=True, expression=django.contrib.postgres.search.CombinedSearchVector(django.contrib.postgres.search.SearchVector('name', config='english', weight='A'), '||', django.contrib.postgres.search.SearchVector('description', config='english', weight='B'), django.contrib.postgres.search.SearchConfig('english')), output_field=django.contrib.postgres.search.SearchVectorField()), | ||
| ), | ||
| migrations.AddIndex( | ||
| model_name='schema', | ||
| index=django.contrib.postgres.indexes.GinIndex(fields=['search_vector'], name='schema_search_vec_gin'), | ||
| ), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import pytest | ||
|
|
||
| from tests.factories import SchemaFactory | ||
| from core.models import Schema | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_stemming_makes_singular_and_plural_equivalent(): | ||
| # "Schema" and "Schemas" must return the same result. | ||
| # decoy proves the @@ filter actually excludes non-matches (not just that matches are found). | ||
| schema = SchemaFactory(name="Invoice Schema") | ||
| decoy = SchemaFactory(name="Weather Report") | ||
| for term in ("Schemas", "Schema"): | ||
| results = Schema.public_objects.search(term) | ||
| assert schema in results | ||
| assert decoy not in results | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_search_is_case_insensitive(): | ||
| schema = SchemaFactory(name="Invoice Schema") | ||
| assert schema in Schema.public_objects.search("INVOICE") | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_search_matches_description_field(): | ||
| schema = SchemaFactory(name="ACME Format", description="A standard for invoices") | ||
| assert schema in Schema.public_objects.search("invoice") | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_name_hit_outranks_description_only_hit(): | ||
| # Assert on the rank *values*, not list order. | ||
| # Order under equal ranks is decided by the name tie-break, | ||
| # which would hide a weight bug for some names. | ||
| SchemaFactory(name="Invoice Format", description=None) | ||
| SchemaFactory(name="ACME Format", description="Used for invoice data") | ||
| ranks = {s.name: s.rank for s in Schema.public_objects.search("invoice")} | ||
| assert ranks["Invoice Format"] > ranks["ACME Format"] | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_blank_query_returns_all_and_preserves_order(): | ||
| SchemaFactory(name="One") | ||
| SchemaFactory(name="Two") | ||
| # A blank / None query must neither filter nor reorder: .search() returns the | ||
| # queryset unchanged, so the caller's ordering survives and no relevance | ||
| # ranking is applied. (Whitespace-only counts as blank via .strip().) | ||
| for blank in ("", " ", None): | ||
| results = Schema.public_objects.order_by("name").search(blank) | ||
| assert [schema.name for schema in results] == ["One", "Two"] | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_search_excludes_unpublished_schemas(): | ||
| SchemaFactory(name="Public Invoice") | ||
| SchemaFactory(name="Private Invoice", published_at=None) | ||
| assert Schema.public_objects.search("invoice").count() == 1 | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_malformed_query_does_not_raise(): | ||
| SchemaFactory(name="Invoice") | ||
| # websearch_to_tsquery tolerates junk such as unbalanced quote plus dangling operator. | ||
| # The guarantee is that evaluating the queryset does not raise. | ||
| results = Schema.public_objects.search('"invoice OR -') | ||
| assert results.count() >= 0 | ||
|
|
||
|
|
||
| @pytest.mark.django_db | ||
| def test_generated_vector_is_populated_on_insert(): | ||
| # Postgres computes the stored generated column on write | ||
| schema = SchemaFactory(name="Invoice Schema") | ||
| schema.refresh_from_db() | ||
| assert schema.search_vector is not None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh nice! I introduced a
SchemaQuerySeton my branch for #293 too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!