Skip to content
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

Improve Search experience in LFS #197

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lfs/search/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib import admin
from lfs.search.models import SearchNotFound

class SearchNotFoundAdmin(admin.ModelAdmin):
list_display = ('creation_date', 'user', 'query', 'session' )
search_fields = ('creation_date','user', 'query', 'session' )

admin.site.register(SearchNotFound, SearchNotFoundAdmin)
31 changes: 30 additions & 1 deletion lfs/search/models.py
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
# just for manage.py test
# django imports
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _


class SearchNotFound(models.Model):
"""
A SearchNotFound is a query from a customer which no product response
A SearchNotFound is only created from a search for a product not found because the livesearch will provide two many queries
with product found and we need only the queries not found to complete our catalog.
**Attributes**
user
The user to which the cart belongs to
session
The session to which the cart belongs to
creation_date
The creation date of the cart

A SearchNotFound can be assigned either to the current logged in User (in case
the shop user is logged in) or to the current session (in case the shop
user is not logged in).
"""
user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True)
session = models.CharField(_(u"Session"), blank=True, max_length=100)
creation_date = models.DateTimeField(_(u"Creation date"), auto_now_add=True)
query = models.CharField("Query not Found", blank=True, max_length=100)

def __unicode__(self):
return u"%s, %s" % (self.query, self.session)
74 changes: 56 additions & 18 deletions lfs/search/views.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import json

# django imports
import re
from django.db.models import Q
from django.core.exceptions import FieldError
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.template.loader import render_to_string
import json

# lfs imports
from lfs.catalog.models import Category
from lfs.catalog.models import Product
from lfs.catalog.settings import STANDARD_PRODUCT, PRODUCT_WITH_VARIANTS, VARIANT

#from lfs.catalog.settings import STANDARD_PRODUCT, PRODUCT_WITH_VARIANTS, VARIANT
from lfs.search.models import SearchNotFound

def livesearch(request, template_name="lfs/search/livesearch_results.html"):
"""
Expand All @@ -23,14 +25,18 @@ def livesearch(request, template_name="lfs/search/livesearch_results.html"):
})
else:
# Products
query = Q(active=True) & \
(Q(name__icontains=q) |
Q(manufacturer__name__icontains=q) |
Q(sku_manufacturer__icontains=q)
) # & \
# Q(sub_type__in=(STANDARD_PRODUCT, PRODUCT_WITH_VARIANTS, VARIANT))
query = Q(active=True) & get_query(q, ['name', 'description'])
temp = Product.objects.filter(query)
if not temp:
# Creates a SearchNotFound for the current session and/or user.
if request.session.session_key is None:
request.session.save()
searchnotfound = SearchNotFound(session=request.session.session_key)
if request.user.is_authenticated():
searchnotfound.user = request.user
searchnotfound.query = q[0:99]
searchnotfound.save()

temp = Product.objects.filter(query)
total = temp.count()
products = temp[0:5]

Expand All @@ -54,14 +60,18 @@ def search(request, template_name="lfs/search/search_results.html"):
q = request.GET.get("q", "")

# Products
query = Q(active=True) & \
(Q(name__icontains=q) |
Q(manufacturer__name__icontains=q) |
Q(sku_manufacturer__icontains=q)
) # & \
# Q(sub_type__in=(STANDARD_PRODUCT, PRODUCT_WITH_VARIANTS, VARIANT))

# TODO add a view in manage to select the attributs of Product modele : manufacturer__name, sku_manufacturer, ...
query = Q(active=True) & get_query(q, ['name', 'description'])
products = Product.objects.filter(query)
if not products:
# Creates a SearchNotFound for the current session and/or user.
if request.session.session_key is None:
request.session.save()
searchnotfound = SearchNotFound(session=request.session.session_key)
if request.user.is_authenticated():
searchnotfound.user = request.user
searchnotfound.query = q
searchnotfound.save()

# Sorting
sorting = request.session.get("sorting")
Expand All @@ -75,3 +85,31 @@ def search(request, template_name="lfs/search/search_results.html"):
"q": q,
"total": total,
}))

def normalize_query(query_string,
findterms=re.compile(ur'"([^"]+)"|(\S+)').findall,
normspace=re.compile(r'\s{2,}').sub):
''' Splits the query string in invidual keywords, getting rid of unecessary spaces
and grouping quoted words together.
'''
return [normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string)]

def get_query(query_string, search_fields):
''' Returns a query, that is a combination of Q objects. That combination
aims to search keywords within a model by testing the given search fields.
'''
query = None # Query to search for every search term
terms = normalize_query(query_string)
for term in terms:
or_query = None # Query to search for a given term in each field
for field_name in search_fields:
q = Q(**{"%s__icontains" % field_name: term})
if or_query is None:
or_query = q
else:
or_query = or_query | q
if query is None:
query = or_query
else:
query = query & or_query
return query