fixed and expanded on trigram search
This commit is contained in:
parent
a0e9b88062
commit
856c34a3bf
@ -1,7 +1,7 @@
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms import widgets
|
||||
from django.forms import widgets, NumberInput
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes import scopes_disabled
|
||||
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
|
||||
@ -390,10 +390,12 @@ class UserCreateForm(forms.Form):
|
||||
|
||||
class SearchPreferenceForm(forms.ModelForm):
|
||||
prefix = 'search'
|
||||
trigram_threshold = forms.DecimalField(min_value=0.01, max_value=1, decimal_places=2, widget=NumberInput(attrs={'class': "form-control-range", 'type': 'range'}),
|
||||
help_text=_('Determines how fuzzy a search is if it uses trigram similarity matching (e.g. low values mean more typos are ignored).'))
|
||||
|
||||
class Meta:
|
||||
model = SearchPreference
|
||||
fields = ('search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext')
|
||||
fields = ('search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext', 'trigram_threshold')
|
||||
|
||||
help_texts = {
|
||||
'search': _('Select type method of search. Click <a href="/docs/search/">here</a> for full desciption of choices.'),
|
||||
@ -402,7 +404,7 @@ class SearchPreferenceForm(forms.ModelForm):
|
||||
'icontains': _("Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"),
|
||||
'istartswith': _("Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"),
|
||||
'trigram': _("Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."),
|
||||
'fulltext': _("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields.")
|
||||
'fulltext': _("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
|
||||
}
|
||||
|
||||
labels = {
|
||||
|
@ -112,16 +112,15 @@ def search_recipes(request, queryset, params):
|
||||
)
|
||||
|
||||
# iterate through fields to use in trigrams generating a single trigram
|
||||
if search_trigram & len(trigram_include) > 1:
|
||||
if search_trigram and len(trigram_include) > 0:
|
||||
trigram = None
|
||||
for f in trigram_include:
|
||||
if trigram:
|
||||
trigram += TrigramSimilarity(f, search_string)
|
||||
else:
|
||||
trigram = TrigramSimilarity(f, search_string)
|
||||
queryset.annotate(simularity=trigram)
|
||||
# TODO allow user to play with trigram scores
|
||||
filters += [Q(simularity__gt=0.5)]
|
||||
queryset = queryset.annotate(similarity=trigram)
|
||||
filters += [Q(similarity__gt=search_prefs.trigram_threshold)]
|
||||
|
||||
if 'name' in fulltext_include:
|
||||
filters += [Q(name_search_vector=search_query)]
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-28 16:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0155_mealtype_default'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='searchpreference',
|
||||
name='trigram_threshold',
|
||||
field=models.DecimalField(decimal_places=2, default=0.1, max_digits=3),
|
||||
),
|
||||
]
|
@ -873,6 +873,7 @@ class SearchPreference(models.Model, PermissionModelMixin):
|
||||
istartswith = models.ManyToManyField(SearchFields, related_name="istartswith_fields", blank=True)
|
||||
trigram = models.ManyToManyField(SearchFields, related_name="trigram_fields", blank=True)
|
||||
fulltext = models.ManyToManyField(SearchFields, related_name="fulltext_fields", blank=True)
|
||||
trigram_threshold = models.DecimalField(default=0.1, decimal_places=2, max_digits=3)
|
||||
|
||||
|
||||
class UserFile(ExportModelOperationsMixin('user_files'), models.Model, PermissionModelMixin):
|
||||
|
@ -21,27 +21,31 @@
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist" style="margin-bottom: 2vh">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link {% if active_tab == 'account' %} active {% endif %}" id="account-tab" data-toggle="tab" href="#account" role="tab"
|
||||
<a class="nav-link {% if active_tab == 'account' %} active {% endif %}" id="account-tab" data-toggle="tab"
|
||||
href="#account" role="tab"
|
||||
aria-controls="account"
|
||||
aria-selected="{% if active_tab == 'account' %} 'true' {% else %} 'false' {% endif %}">
|
||||
{% trans 'Account' %}</a>
|
||||
{% trans 'Account' %}</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link {% if active_tab == 'prefernces' %} active {% endif %}" id="preferences-tab" data-toggle="tab" href="#preferences" role="tab"
|
||||
<a class="nav-link {% if active_tab == 'prefernces' %} active {% endif %}" id="preferences-tab"
|
||||
data-toggle="tab" href="#preferences" role="tab"
|
||||
aria-controls="preferences"
|
||||
aria-selected="{% if active_tab == 'prefernces' %} 'true' {% else %} 'false' {% endif %}">
|
||||
{% trans 'Preferences' %}</a>
|
||||
{% trans 'Preferences' %}</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link {% if active_tab == 'api' %} active {% endif %}" id="api-tab" data-toggle="tab" href="#api" role="tab"
|
||||
aria-controls="api"
|
||||
aria-selected="{% if active_tab == 'api' %} 'true' {% else %} 'false' {% endif %}">
|
||||
<a class="nav-link {% if active_tab == 'api' %} active {% endif %}" id="api-tab" data-toggle="tab"
|
||||
href="#api" role="tab"
|
||||
aria-controls="api"
|
||||
aria-selected="{% if active_tab == 'api' %} 'true' {% else %} 'false' {% endif %}">
|
||||
{% trans 'API-Settings' %}</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link {% if active_tab == 'search' %} active {% endif %}" id="search-tab" data-toggle="tab" href="#search" role="tab"
|
||||
aria-controls="search"
|
||||
aria-selected="{% if active_tab == 'search' %} 'true' {% else %} 'false' {% endif %}">
|
||||
<a class="nav-link {% if active_tab == 'search' %} active {% endif %}" id="search-tab" data-toggle="tab"
|
||||
href="#search" role="tab"
|
||||
aria-controls="search"
|
||||
aria-selected="{% if active_tab == 'search' %} 'true' {% else %} 'false' {% endif %}">
|
||||
{% trans 'Search-Settings' %}</a>
|
||||
</li>
|
||||
|
||||
@ -49,7 +53,8 @@
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane {% if active_tab == 'account' %} active {% endif %}" id="account" role="tabpanel" aria-labelledby="account-tab">
|
||||
<div class="tab-pane {% if active_tab == 'account' %} active {% endif %}" id="account" role="tabpanel"
|
||||
aria-labelledby="account-tab">
|
||||
<h4>{% trans 'Name Settings' %}</h4>
|
||||
<form action="." method="post">
|
||||
{% csrf_token %}
|
||||
@ -60,8 +65,8 @@
|
||||
|
||||
<h4>{% trans 'Account Settings' %}</h4>
|
||||
|
||||
<a href="{% url 'account_email'%}" class="btn btn-primary">{% trans 'Emails' %}</a>
|
||||
<a href="{% url 'account_change_password'%}" class="btn btn-primary">{% trans 'Password' %}</a>
|
||||
<a href="{% url 'account_email' %}" class="btn btn-primary">{% trans 'Emails' %}</a>
|
||||
<a href="{% url 'account_change_password' %}" class="btn btn-primary">{% trans 'Password' %}</a>
|
||||
|
||||
<a href="{% url 'socialaccount_connections' %}" class="btn btn-primary">{% trans 'Social' %}</a>
|
||||
<br/>
|
||||
@ -70,7 +75,8 @@
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane {% if active_tab == 'preferences' %} active {% endif %}" id="preferences" role="tabpanel" aria-labelledby="preferences-tab">
|
||||
<div class="tab-pane {% if active_tab == 'preferences' %} active {% endif %}" id="preferences" role="tabpanel"
|
||||
aria-labelledby="preferences-tab">
|
||||
|
||||
|
||||
<div class="row">
|
||||
@ -121,7 +127,8 @@
|
||||
|
||||
|
||||
</div>
|
||||
<div class="tab-pane {% if active_tab == 'api' %} active {% endif %}" id="api" role="tabpanel" aria-labelledby="api-tab">
|
||||
<div class="tab-pane {% if active_tab == 'api' %} active {% endif %}" id="api" role="tabpanel"
|
||||
aria-labelledby="api-tab">
|
||||
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
@ -153,7 +160,8 @@
|
||||
|
||||
|
||||
</div>
|
||||
<div class="tab-pane {% if active_tab == 'search' %} active {% endif %}" id="search" role="tabpanel" aria-labelledby="search-tab">
|
||||
<div class="tab-pane {% if active_tab == 'search' %} active {% endif %}" id="search" role="tabpanel"
|
||||
aria-labelledby="search-tab">
|
||||
<h4>{% trans 'Search Settings' %}</h4>
|
||||
<form action="." method="post">
|
||||
{% csrf_token %}
|
||||
@ -166,11 +174,17 @@
|
||||
</div>
|
||||
|
||||
<script type="application/javascript">
|
||||
$(function () {
|
||||
$('#id_search-trigram_threshold').get(0).type = 'range';
|
||||
})
|
||||
|
||||
|
||||
function copyToken() {
|
||||
let token = $('#id_token');
|
||||
token.select();
|
||||
document.execCommand("copy");
|
||||
}
|
||||
|
||||
// Javascript to enable link to tab
|
||||
var hash = location.hash.replace(/^#/, ''); // ^ means starting, meaning only match the first hash
|
||||
if (hash) {
|
||||
|
@ -366,6 +366,7 @@ def user_settings(request):
|
||||
sp.istartswith.set(search_form.cleaned_data['istartswith'])
|
||||
sp.trigram.set(search_form.cleaned_data['trigram'])
|
||||
sp.fulltext.set(search_form.cleaned_data['fulltext'])
|
||||
sp.trigram_threshold = search_form.cleaned_data['trigram_threshold']
|
||||
|
||||
sp.save()
|
||||
if up:
|
||||
|
Loading…
Reference in New Issue
Block a user