Skip to content

Commit

Permalink
Add similarity space search option
Browse files Browse the repository at this point in the history
This includes layout improvements, including updates in how facets are defined and sorted
  • Loading branch information
ffont committed Apr 16, 2024
1 parent b35a641 commit 3565f54
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 76 deletions.
28 changes: 13 additions & 15 deletions freesound/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,24 +615,24 @@
SEARCH_SOUNDS_SORT_DEFAULT = SEARCH_SOUNDS_SORT_OPTION_AUTOMATIC

SEARCH_SOUNDS_DEFAULT_FACETS = {
SEARCH_SOUNDS_FIELD_SAMPLERATE: {},
SEARCH_SOUNDS_FIELD_PACK_GROUPING: {'limit': 10},
SEARCH_SOUNDS_FIELD_USER_NAME: {'limit': 30},
SEARCH_SOUNDS_FIELD_TAGS: {'limit': 30},
SEARCH_SOUNDS_FIELD_BITRATE: {},
SEARCH_SOUNDS_FIELD_BITDEPTH: {},
SEARCH_SOUNDS_FIELD_SAMPLERATE: {'sort': 'index asc'},
SEARCH_SOUNDS_FIELD_PACK_GROUPING: {'limit': 10, 'title': 'Packs'},
SEARCH_SOUNDS_FIELD_USER_NAME: {'limit': 10, 'widget': 'cloud', 'title': 'Users'},
SEARCH_SOUNDS_FIELD_TAGS: {'limit': 30, 'widget': 'cloud'},
SEARCH_SOUNDS_FIELD_BITRATE: {'sort': 'index asc'},
SEARCH_SOUNDS_FIELD_BITDEPTH: {'sort': 'index asc'},
SEARCH_SOUNDS_FIELD_TYPE: {'limit': len(SOUND_TYPE_CHOICES)},
SEARCH_SOUNDS_FIELD_CHANNELS: {},
SEARCH_SOUNDS_FIELD_CHANNELS: {'sort': 'index asc'},
SEARCH_SOUNDS_FIELD_LICENSE_NAME: {'limit': 10},
}

SEARCH_SOUNDS_BETA_FACETS = {
'fsdsinet_detected_class': {'limit': 30},
'ac_brightness': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20},
'ac_depth': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20},
'ac_warmth': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20},
'ac_hardness': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20},
'ac_boominess': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20},
'fsdsinet_detected_class': {'limit': 30, 'title': 'FSD-SINet class'},
'ac_brightness': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20, 'widget': 'range', 'title': 'Brightness'},
'ac_depth': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20, 'widget': 'range', 'title': 'Depth'},
'ac_warmth': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20, 'widget': 'range', 'title': 'Warmth'},
'ac_hardness': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20, 'widget': 'range', 'title': 'Hardness'},
'ac_boominess': {'type': 'range', 'start': 0, 'end': 100, 'gap': 20, 'widget': 'range', 'title': 'Boominess'},
}

SEARCH_FORUM_SORT_OPTION_THREAD_DATE_FIRST = "Thread creation (newest first)"
Expand Down Expand Up @@ -766,8 +766,6 @@
# Timeout for returning clustering results to the user
CLUSTERING_TASK_TIMEOUT = 30

CLUSTERING_SIMILARITY_ANALYZER = FSDSINET_ANALYZER_NAME

# -------------------------------------------------------------------------------
# Rate limiting

Expand Down
10 changes: 9 additions & 1 deletion freesound/static/bw-frontend/styles/pages/search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
}

.bw-search__filter-tags-list {
margin: 13px 0;
margin-top: $tiny-spacing;
margin-bottom: $small-spacing;
}

.bw-search__filter-value {
Expand Down Expand Up @@ -48,6 +49,13 @@
}
}

#collapsable-filters {
.bw-search__filter-section {
margin-bottom: $tiny-spacing;
border-bottom: 1px solid $dividers-color;
}
}

.bw-search__player-small {
flex: 0 0 120px;
}
Expand Down
12 changes: 6 additions & 6 deletions search/templatetags/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@


@register.inclusion_tag('search/facet.html', takes_context=True)
def display_facet(context, facet_name, facet_title=None, facet_type='list'):
def display_facet(context, facet_name, facet_title=None):
sqp = context['sqp']
facets = context['facets']
if facet_title is None:
facet_title = facet_name.capitalize()

solr_fieldname = FIELD_NAMES_MAP.get(facet_name, facet_name)

if facet_name in facets:
facet_title = sqp.facets[facet_name].get('title', facet_name.capitalize())
facet_type = sqp.facets[facet_name].get('widget', 'list')

# If a facet contains a value which is already used in a filter (this can hapen with facets with multiple values like
# tags), then we remove it from the list of options so we don't show redundant information
facet_values_to_skip = []
Expand All @@ -55,7 +54,8 @@ def display_facet(context, facet_name, facet_title=None, facet_type='list'):
else:
facet = [{'value': value, 'count': count, 'size': -1} for value, count in facets[facet_name]]
else:
facet = []
# Return "empty" data (facet will not be displayed)
return {'type': 'list', 'title': facet_name, 'facet': []}

# If the filter is grouping_pack and there are elements which do not contain the character "_" means that
# these sounds do not belong to any pack (as grouping pack values should by "packId_packName" if there is a pack
Expand Down
5 changes: 3 additions & 2 deletions search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def search_view_helper(request):

# Run the query and post-process the results
try:
query_params = sqp.as_query_params()
query_params = sqp.as_query_params()
results, paginator = perform_search_engine_query(query_params)
if not sqp.map_mode_active():
if not sqp.display_as_packs_active():
Expand Down Expand Up @@ -171,6 +171,7 @@ def search_view_helper(request):
'facets': results.facets,
'non_grouped_number_of_results': results.non_grouped_number_of_results,
'show_beta_search_options': allow_beta_search_features(request),
'experimental_facets': settings.SEARCH_SOUNDS_BETA_FACETS,
}

except SearchEngineException as e:
Expand All @@ -179,7 +180,7 @@ def search_view_helper(request):
return {'error_text': 'There was an error while searching, is your query correct?'}
except Exception as e:
search_logger.info(f'Could probably not connect to Solr - {e}')
sentry_sdk.capture_exception(e) # Manually capture exception so it has mroe info and Sentry can organize it properly
sentry_sdk.capture_exception(e) # Manually capture exception so it has more info and Sentry can organize it properly
return {'error_text': 'The search server could not be reached, please try again later.'}


Expand Down
4 changes: 2 additions & 2 deletions templates/search/facet.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{% ifequal type "list" %}
<ul class="bw-search__filter-value-list">
{% for f in facet %}
<li class="bw-search__filter-value v-padding-1">
<li class="bw-search__filter-value padding-bottom-1">
<div class="bw-search__filter-name">
<a href="{{ f.add_filter_url }}" class="bw-link--black">{% if f.icon %}{% bw_icon f.icon %}{% endif %}{{ f.display_value }}</a>
<div class="bw-search__filter-count padding-left-1">({{ f.count|bw_intcomma }})</div>
Expand All @@ -17,7 +17,7 @@
{% ifequal type "range" %}
<ul class="bw-search__filter-value-list">
{% for f in facet %}
<li class="bw-search__filter-value v-padding-1">
<li class="bw-search__filter-value padding-bottom-1">
<div class="bw-search__filter-name">
<a href="{{ f.add_filter_url }}" class="bw-link--black">{% if f.icon %}{% bw_icon f.icon %}{% endif %}{{ f.display_value }}</a>
<div class="bw-search__filter-count padding-left-1">({{ f.count|bw_intcomma }})</div>
Expand Down
59 changes: 15 additions & 44 deletions templates/search/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,45 +158,20 @@ <h3>
{% if show_beta_search_options %}
<div class="row v-spacing-top-1">
<div class="col-12">
<div class="bw-search__filter-section-name caps text-light-grey text-18 text-center">
<div class="bw-search__filter-section-name caps text-light-grey text-18">
Experimental Search Options
</div>
</div>
</div>
<div class="row">
<div class="col-4 v-padding-1">
<div class="v-spacing-top-1">{% display_search_option "include_audio_problems" %}</div>
<div class="v-spacing-top-1">{% display_search_option "compute_clusters" %}</div>
<div class="v-spacing-top-1">{% display_search_option "similarity_space" %}</div>
<div class="v-spacing-top-1">{% display_search_option "similar_to" %}</div>
<div class="v-spacing-top-1">{% display_search_option "compute_clusters" %}</div>
</div>
<div class="col-8 v-padding-1">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">FSDSINET class:</div>
{% display_facet "fsdsinet_detected_class" "" "cloud" %}
</div>
</div>
<div class="row">
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">Brightness:</div>
{% display_facet "ac_brightness" "" "range" %}
</div>
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">Hardness:</div>
{% display_facet "ac_hardness" "" "range" %}
</div>
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">Depth:</div>
{% display_facet "ac_depth" "" "range" %}
</div>
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">Warmth:</div>
{% display_facet "ac_warmth" "" "range" %}
</div>
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">
<div class="v-spacing-top-1 v-spacing-negative-1 text-grey">Boominess:</div>
{% display_facet "ac_boominess" "" "range" %}
</div>
<div class="col-4 col-lg-2 v-padding-1 padding-right-3">

<div class="v-spacing-top-1">{% display_search_option "include_audio_problems" %}</div>
<div class="v-spacing-top-1">{% display_search_option "single_event" %}</div>
</div>
</div>
{% endif %}
Expand Down Expand Up @@ -236,30 +211,26 @@ <h3>
{% comment %}facets{% endcomment%}
<aside class="col-md-4 col-lg-3 collapsable-block md-max-h-100" id="collapsable-filters">
{% if sqp.tags_mode_active %}
{% display_facet "tags" "Tags" "cloud" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "tags" %}
{% endif %}
{% display_facet "license" "Licenses" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "license" %}
{% if not sqp.tags_mode_active %}
{% display_facet "tags" "Tags" "cloud" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "tags"%}
{% endif %}
{% display_facet "type" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "samplerate" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "channels" %}
<div class="divider-light padding-top-1"></div>
{% if not sqp.display_as_packs_active %}
{% display_facet "pack_grouping" "Packs" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "pack_grouping" %}
{% endif %}
{% display_facet "username" "Users" "cloud" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "username" %}
{% display_facet "bitdepth" %}
<div class="divider-light padding-top-1"></div>
{% display_facet "bitrate" %}
{% if show_beta_search_options %}
{% for experimental_facet in experimental_facets %}
{% display_facet experimental_facet %}
{% endfor %}
{% endif %}
</aside>
<div class="col-12 divider-light d-md-none v-spacing-top-3 v-spacing-3"></div>
{% comment %}search results{% endcomment %}
Expand Down
6 changes: 3 additions & 3 deletions templates/search/search_option.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<div class="bw-search__filter-name">{{ label }}</div>
</label>
{% elif widget == 'text' %}
<label for="id_{{ option_name }}" class="{% if option.disabled %}opacity-020{% endif %}">{{ label }}</label>:
<input id="id_{{ option_name }}" name="{{ option.query_param_name }}" class="bw-search_input" type="text" value="{{ option.value_formatted }}" {% if option.disabled %}disabled{% endif %}/>
<label for="id_{{ option_name }}" class="{% if option.disabled %}opacity-020{% endif %} text-light-grey">{{ label }}:</label>
<input id="id_{{ option_name }}" name="{{ option.query_param_name }}" class="bw-search_input" type="text" value="{{ option.value_formatted }}" {% if option.placeholder %}placeholder="{{ option.placeholder }}"{% endif %} {% if option.disabled %}disabled{% endif %}/>
{% elif widget == 'select' %}
<span class="font-weight-normal text-light-grey d-none d-md-inline">{{ label }}:</span>
<label class="text-light-grey d-none d-md-inline">{{ label }}:</label>
<select id="id_{{ option_name }}" name="{{ option.query_param_name }}" {% if option.disabled %}disabled{% endif %}>
{% for choice in option.get_choices_annotated_with_selection %}
<option value="{{choice.0}}"{% if choice.2 %}selected="selected"{% endif %}>{{choice.1}}</option>
Expand Down
2 changes: 1 addition & 1 deletion utils/clustering_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_clusters_for_query(sqp, compute_if_not_in_cache=True):
# First get the similarity vectors for the first settings.MAX_RESULTS_FOR_CLUSTERING results from the query
similarity_vectors_map = get_sound_similarity_from_search_engine_query(
query_params,
analyzer_name=settings.CLUSTERING_SIMILARITY_ANALYZER,
analyzer_name=query_params['similar_to_analyzer'], # This is the similarity_space param in sqp
num_sounds=settings.MAX_RESULTS_FOR_CLUSTERING,
current_page=1)
sound_ids = list(similarity_vectors_map.keys())
Expand Down
18 changes: 16 additions & 2 deletions utils/search/search_query_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,21 @@ class SearchQueryProcessor(object):
advanced=False,
element_in_path='/browse/tags/')
similar_to = SearchOptionStr(
query_param_name='st')
query_param_name='st',
label='Similarity target',
placeholder='Sound ID')
compute_clusters = SearchOptionBool(
query_param_name='cc',
label='Cluster results by sound similarity')
cluster_id = SearchOptionInt(
advanced=False,
query_param_name='cid',
get_value_to_apply = lambda option: -1 if not option.sqp.get_option_value_to_apply('compute_clusters') else option.value)
similarity_space = SearchOptionChoice(
query_param_name='ss',
label='Similarity space',
choices = [(option, option) for option in settings.SEARCH_ENGINE_SIMILARITY_ANALYZERS.keys()],
get_default_value = lambda option: settings.SEARCH_ENGINE_DEFAULT_SIMILARITY_ANALYZER)
field_weights = SearchOptionFieldWeights(
query_param_name = 'w'
)
Expand All @@ -150,6 +157,11 @@ class SearchQueryProcessor(object):
search_engine_field_name= 'has_audio_problems',
label='Exclude sounds with potential audio problems'
)
single_event = SearchOptionBool(
query_param_name='se',
search_engine_field_name= 'ac_single_event',
label='Only include "single event" sounds',
)

def __init__(self, request, facets=None):
"""Initializes the SearchQueryProcessor object by parsing data from the request and setting up search options.
Expand Down Expand Up @@ -408,6 +420,7 @@ def get_clustering_data_cache_key(self, include_filters_from_facets=False):
key = f'cluster-results-{self.get_option_value_to_apply("query")}-' + \
f'{query_filter}-{self.get_option_value_to_apply("sort_by")}-' + \
f'{self.get_option_value_to_apply("similar_to")}-' + \
f'{self.get_option_value_to_apply("similarity_space")}-' + \
f'{self.get_option_value_to_apply("group_by_pack")}'
return create_hash(key, limit=32)

Expand Down Expand Up @@ -515,7 +528,8 @@ def as_query_params(self, exclude_facet_filters=False):
facets=facets,
only_sounds_with_pack=self.get_option_value_to_apply('display_as_packs'),
only_sounds_within_ids=only_sounds_within_ids,
similar_to=similar_to
similar_to=similar_to,
similar_to_analyzer=self.get_option_value_to_apply('similarity_space')
)

def get_url(self, add_filters=None, remove_filters=None):
Expand Down
1 change: 1 addition & 0 deletions utils/tests/test_search_query_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class SearchQueryProcessorTests(TestCase):
'query_fields': settings.SEARCH_SOUNDS_DEFAULT_FIELD_WEIGHTS,
'query_filter': '',
'similar_to': None,
'similar_to_analyzer': settings.SEARCH_ENGINE_DEFAULT_SIMILARITY_ANALYZER,
'sort': settings.SEARCH_SOUNDS_SORT_OPTION_DATE_NEW_FIRST, # Empty query should sort by date added, so use this as expected default
'textual_query': ''}

Expand Down

0 comments on commit 3565f54

Please sign in to comment.