Filtering by Vocabulary-Specific Keywords#

The ProjectFilter class supports creating multiple keyword filters, each tied to a specific vocabulary. This allows users to filter projects by different types of controlled keywords (e.g., science keywords, platforms, instruments) rather than having a single generic “Keywords” filter.

Autocomplete for Large Vocabularies#

For extremely large vocabularies (e.g., GCMD Science Keywords with thousands of concepts), FairDM uses django-autocomplete-light to efficiently load concepts on demand rather than loading entire querysets into the browser.

Concept Autocomplete View#

The ConceptAutocomplete view at /autocomplete/concept/ provides filtered concept lookups:

# URL: /autocomplete/concept/?vocabulary=gcmd-science-keywords&q=ocean
# Returns: Concepts from the specified vocabulary matching the search term

Features:

  • Filters by vocabulary name via URL parameter or forwarded field

  • Searches concept labels and names

  • Only authenticated users can access

  • Returns results ordered by label

Usage in custom widgets:

from dal import autocomplete
from django import forms

field = forms.ModelMultipleChoiceField(
    queryset=Concept.objects.all(),
    widget=autocomplete.ModelSelect2Multiple(
        url='concept-autocomplete?vocabulary=gcmd-science-keywords',
        attrs={
            'data-placeholder': 'Select keywords...',
            'data-minimum-input-length': 2,
        }
    )
)

Keyword Form Configuration#

How It Works#

The ProjectFilter base class has a KEYWORD_VOCABULARIES configuration attribute that accepts a list of vocabulary specifications. For each vocabulary specified, the filter automatically creates a separate filter field that:

  1. Only shows concepts from that specific vocabulary

  2. Only shows concepts that are actually used by projects

  3. Filters the keywords relationship on the Project model

Basic Usage#

To add vocabulary-specific filters, create a custom filter class that extends ProjectFilter and specify the vocabularies you want:

from django.utils.translation import gettext_lazy as _
from fairdm.core.project.filters import ProjectFilter


class MyProjectFilter(ProjectFilter):
    """Custom project filter with vocabulary-specific keyword filters."""
    
    KEYWORD_VOCABULARIES = [
        ("gcmd-science-keywords", _("Science Keywords")),
        ("gcmd-platforms", _("Platforms")),
        ("gcmd-instruments", _("Instruments")),
    ]

This will automatically create three new filter fields:

  • keywords_gcmd_science_keywords - labeled “Science Keywords”

  • keywords_gcmd_platforms - labeled “Platforms”

  • keywords_gcmd_instruments - labeled “Instruments”

How Filter Names Are Generated#

The filter field name is generated by:

  1. Prefixing with keywords_

  2. Converting the vocabulary name to a valid Python identifier (replacing - with _)

For example:

  • "gcmd-science-keywords"keywords_gcmd_science_keywords

  • "my-custom-vocab"keywords_my_custom_vocab

Finding Vocabulary Names#

Vocabulary names in the database correspond to the name field on vocabulary objects. To find available vocabularies:

from research_vocabs.models import Vocabulary

# List all vocabularies
for vocab in Vocabulary.objects.all():
    print(f"{vocab.name}: {vocab.title}")

Common GCMD vocabularies include:

  • gcmd-science-keywords

  • gcmd-platforms

  • gcmd-instruments

  • gcmd-locations

  • gcmd-projects

Widget Customization#

By default, vocabulary filters use CheckboxSelectMultiple widgets for multi-select functionality. The filters use OR logic, meaning projects matching ANY selected keyword will be returned.

If you need different widget behavior, you can override the filter creation in __init__:

class MyProjectFilter(ProjectFilter):
    KEYWORD_VOCABULARIES = [
        ("gcmd-science-keywords", _("Science Keywords")),
    ]
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Customize the widget for a specific vocabulary filter
        self.filters["keywords_gcmd_science_keywords"].widget = forms.SelectMultiple(
            attrs={"class": "form-select", "size": "10"}
        )

Empty State Handling#

The filters automatically exclude concepts that aren’t used by any projects. This means if no projects use concepts from a vocabulary, that filter will have an empty queryset and won’t show any options.

Complete Example#

# myapp/filters.py
from django.utils.translation import gettext_lazy as _
from fairdm.core.project.filters import ProjectFilter


class GeoscienceProjectFilter(ProjectFilter):
    """Project filter configured for geoscience research portals."""
    
    KEYWORD_VOCABULARIES = [
        ("gcmd-science-keywords", _("Science Keywords")),
        ("gcmd-platforms", _("Observation Platforms")),
        ("gcmd-instruments", _("Instruments")),
        ("gcmd-locations", _("Locations")),
    ]

Then use it in your view configuration or registry:

# myapp/config.py
from .filters import GeoscienceProjectFilter

PROJECT_FILTER = GeoscienceProjectFilter

Performance Considerations#

The filter queries are optimized to:

  • Only load concepts from specified vocabularies

  • Only load concepts actually used by projects (via projects__isnull=False)

  • Use distinct() to avoid duplicate results

For large vocabularies, consider adding database indexes on the Concept model’s vocabulary_id field if not already present.