partial improvements to search
This commit is contained in:
@@ -535,6 +535,37 @@ class ProductSearchTest(TestCase):
|
|||||||
# check prices
|
# check prices
|
||||||
self.assertTrue(payload['prices']['min'] <= payload['prices']['max'])
|
self.assertTrue(payload['prices']['min'] <= payload['prices']['max'])
|
||||||
|
|
||||||
|
def test_anon_user_can_search_no_querystring(self):
|
||||||
|
expected_instances = [
|
||||||
|
self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"),
|
||||||
|
self.factory(tags="colores/rojos, tono/brillante"),
|
||||||
|
self.factory(tags="lunares/azules", description="zapatos rojos"),
|
||||||
|
self.factory(tags="lunares/rojos", description="zapatos"),
|
||||||
|
self.factory(attributes='"zapatos de campo", tono/oscuro'),
|
||||||
|
]
|
||||||
|
unexpected_instances = [
|
||||||
|
self.factory(description="chanclas"),
|
||||||
|
self.factory(tags="azules"),
|
||||||
|
]
|
||||||
|
|
||||||
|
q = quote("zapatos rojos")
|
||||||
|
|
||||||
|
url = f"{self.endpoint}?q="
|
||||||
|
# send in request
|
||||||
|
response = self.client.get(url)
|
||||||
|
# check response
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# load response data
|
||||||
|
payload = response.json()
|
||||||
|
# check for expected fields in payload
|
||||||
|
self.assertIsNotNone(payload.get('filters'))
|
||||||
|
self.assertIsNotNone(payload.get('count'))
|
||||||
|
self.assertIsNotNone(payload.get('products'))
|
||||||
|
self.assertIsNotNone(payload.get('prices'))
|
||||||
|
|
||||||
|
# check for object creation
|
||||||
|
self.assertEquals(len(payload['products']), len(expected_instances) + len(unexpected_instances))
|
||||||
|
|
||||||
def test_anon_user_can_paginate_search(self):
|
def test_anon_user_can_paginate_search(self):
|
||||||
expected_instances = [
|
expected_instances = [
|
||||||
self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"),
|
self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"),
|
||||||
@@ -549,10 +580,10 @@ class ProductSearchTest(TestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
q = quote("zapatos rojos")
|
q = quote("zapatos rojos")
|
||||||
limit = 2
|
|
||||||
|
|
||||||
|
# test limit less than available
|
||||||
|
limit = 3
|
||||||
url = f"{self.endpoint}?q={q}&limit={limit}"
|
url = f"{self.endpoint}?q={q}&limit={limit}"
|
||||||
# send in request
|
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
|
||||||
# check response
|
# check response
|
||||||
@@ -562,6 +593,44 @@ class ProductSearchTest(TestCase):
|
|||||||
self.assertEquals(len(payload['products']), limit)
|
self.assertEquals(len(payload['products']), limit)
|
||||||
self.assertEquals(payload['count'], len(expected_instances))
|
self.assertEquals(payload['count'], len(expected_instances))
|
||||||
|
|
||||||
|
# test limit less than available
|
||||||
|
limit = 10
|
||||||
|
url = f"{self.endpoint}?q={q}&limit={limit}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
# check response
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# load response data
|
||||||
|
payload = response.json()
|
||||||
|
self.assertEquals(len(payload['products']), len(expected_instances))
|
||||||
|
self.assertEquals(payload['count'], len(expected_instances))
|
||||||
|
|
||||||
|
# test limit equal to available, offset zero
|
||||||
|
limit = len(expected_instances)
|
||||||
|
offset = 0
|
||||||
|
url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
# check response
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# load response data
|
||||||
|
payload = response.json()
|
||||||
|
self.assertEquals(len(payload['products']), len(expected_instances))
|
||||||
|
self.assertEquals(payload['count'], len(expected_instances))
|
||||||
|
|
||||||
|
# test limit and offset equal to available
|
||||||
|
limit = len(expected_instances)
|
||||||
|
offset = limit
|
||||||
|
url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
# check response
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# load response data
|
||||||
|
payload = response.json()
|
||||||
|
self.assertEquals(len(payload['products']), 0)
|
||||||
|
self.assertEquals(payload['count'], len(expected_instances))
|
||||||
|
|
||||||
def test_anon_user_can_filter_shipping_cost_true(self):
|
def test_anon_user_can_filter_shipping_cost_true(self):
|
||||||
expected_instances = [
|
expected_instances = [
|
||||||
self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00),
|
self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00),
|
||||||
@@ -685,7 +754,6 @@ class ProductSearchTest(TestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
q = quote("zapatos rojos")
|
q = quote("zapatos rojos")
|
||||||
# discount=true
|
|
||||||
url = f"{self.endpoint}?q={q}&category=ropa/nueva"
|
url = f"{self.endpoint}?q={q}&category=ropa/nueva"
|
||||||
# send in request
|
# send in request
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
@@ -815,7 +883,7 @@ class ProductSearchTest(TestCase):
|
|||||||
url = f"{self.endpoint}?q={q}&order=oldest"
|
url = f"{self.endpoint}?q={q}&order=oldest"
|
||||||
# send in request
|
# send in request
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
import ipdb; ipdb.set_trace()
|
||||||
# check response
|
# check response
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
# load response data
|
# load response data
|
||||||
|
|||||||
@@ -90,115 +90,116 @@ def load_coop_products(request):
|
|||||||
|
|
||||||
@api_view(['GET',]) # include allowed methods
|
@api_view(['GET',]) # include allowed methods
|
||||||
def product_search(request):
|
def product_search(request):
|
||||||
"""
|
"""
|
||||||
Takes a string of data, return relevant products
|
Takes a string of data, return relevant products
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
- q: used for search [MANDATORY]
|
- q: used for search [MANDATORY]
|
||||||
- limit: max number of returned instances [OPTIONAL]
|
- limit: max number of returned instances [OPTIONAL]
|
||||||
- offset: where to start counting results [OPTIONAL]
|
- offset: where to start counting results [OPTIONAL]
|
||||||
- shipping_cost: true/false
|
- shipping_cost: true/false
|
||||||
- discount: true/false
|
- discount: true/false
|
||||||
- category: string
|
- category: string
|
||||||
- tags: string
|
- tags: string
|
||||||
- order: string (newest/oldest)
|
- order: string (newest/oldest)
|
||||||
- price_min: int
|
- price_min: int
|
||||||
- price_max: int
|
- price_max: int
|
||||||
|
|
||||||
In the response:
|
In the response:
|
||||||
- filters
|
- filters
|
||||||
- count
|
- count
|
||||||
- products
|
- products
|
||||||
- price_min
|
- price_min
|
||||||
- price_max
|
- price_max
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# capture query params
|
# capture query params
|
||||||
q = request.GET.get('q', None)
|
q = request.GET.get('q', None)
|
||||||
limit = request.GET.get('limit', None)
|
limit = request.GET.get('limit', None)
|
||||||
offset = request.GET.get('offset', None)
|
offset = request.GET.get('offset', None)
|
||||||
shipping_cost = request.GET.get('shipping_cost', None)
|
shipping_cost = request.GET.get('shipping_cost', None)
|
||||||
if shipping_cost is not None:
|
if shipping_cost is not None:
|
||||||
if shipping_cost == 'true':
|
if shipping_cost == 'true':
|
||||||
shipping_cost = True
|
shipping_cost = True
|
||||||
elif shipping_cost == 'false':
|
elif shipping_cost == 'false':
|
||||||
shipping_cost = False
|
shipping_cost = False
|
||||||
else:
|
else:
|
||||||
shipping_cost = None
|
shipping_cost = None
|
||||||
discount = request.GET.get('discount', None)
|
discount = request.GET.get('discount', None)
|
||||||
if discount is not None:
|
if discount is not None:
|
||||||
if discount == 'true':
|
if discount == 'true':
|
||||||
discount = True
|
discount = True
|
||||||
elif discount == 'false':
|
elif discount == 'false':
|
||||||
discount = False
|
discount = False
|
||||||
else:
|
else:
|
||||||
discount = None
|
discount = None
|
||||||
category = request.GET.get('category', None)
|
category = request.GET.get('category', None)
|
||||||
tags = request.GET.get('tags', None)
|
tags = request.GET.get('tags', None)
|
||||||
price_min = request.GET.get('price_min', None)
|
price_min = request.GET.get('price_min', None)
|
||||||
price_max = request.GET.get('price_max', None)
|
price_max = request.GET.get('price_max', None)
|
||||||
order = request.GET.get('order', '')
|
order = request.GET.get('order', '')
|
||||||
|
|
||||||
if q is None:
|
if q is None:
|
||||||
return Response({"errors": {"details": "No query string to parse"}})
|
return Response({"errors": {"details": "No query string to parse"}})
|
||||||
elif q is '':
|
|
||||||
# return everything
|
|
||||||
serializer = ProductSerializer(Product.objects.all(), many=True)
|
|
||||||
products = serializer.data
|
|
||||||
# filters = extract_search_filters(products)
|
|
||||||
return Response(data={"filters": [], "count": len(products), "products": products})
|
|
||||||
try:
|
|
||||||
# we collect our results here
|
|
||||||
result_set = set()
|
|
||||||
# values for response
|
|
||||||
prices = {
|
|
||||||
'min': None,
|
|
||||||
'max': None,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
# we collect our results here
|
||||||
|
result_set = set()
|
||||||
|
# values for response
|
||||||
|
prices = {
|
||||||
|
'min': None,
|
||||||
|
'max': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if q == '':
|
||||||
|
# filter entire queryset
|
||||||
|
products_qs = Product.objects.all()
|
||||||
|
if tags:
|
||||||
|
products_qs = Product.objects.filter(tags=tags)
|
||||||
|
if category:
|
||||||
|
products_qs = Product.objects.filter(category=category)
|
||||||
|
# serialize and list data
|
||||||
|
serializer = ProductSerializer(products_qs, many=True)
|
||||||
|
result_list = [dict(i) for i in serializer.data]
|
||||||
|
else:
|
||||||
# split query string into single words
|
# split query string into single words
|
||||||
chunks = q.split(' ')
|
chunks = q.split(' ')
|
||||||
for chunk in chunks:
|
for chunk in chunks:
|
||||||
product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max)
|
product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max)
|
||||||
# update price values
|
# update price values
|
||||||
if product_set:
|
if product_set:
|
||||||
# import ipdb; ipdb.set_trace()
|
if prices['min'] is None or min_price['price__min'] < prices['min']:
|
||||||
if prices['min'] is None or min_price['price__min'] < prices['min']:
|
prices['min'] = min_price['price__min']
|
||||||
prices['min'] = min_price['price__min']
|
if prices['max'] is None or max_price['price__max'] > prices['max']:
|
||||||
if prices['max'] is None or max_price['price__max'] > prices['max']:
|
prices['max'] = max_price['price__max']
|
||||||
prices['max'] = max_price['price__max']
|
# add to result set
|
||||||
# add to result set
|
result_set.update(product_set)
|
||||||
result_set.update(product_set)
|
# serialize and list data
|
||||||
# TODO: add search for entire phrase ???
|
serializer = SearchResultSerializer(product_set, many=True)
|
||||||
|
result_list = [dict(i) for i in serializer.data]
|
||||||
|
|
||||||
# extract filters from result_set
|
# extract filters from result_set
|
||||||
filters = extract_search_filters(result_set)
|
filters = extract_search_filters(result_set)
|
||||||
|
# order the results
|
||||||
|
if order == 'newest':
|
||||||
|
# order results by created
|
||||||
|
result_list = sorted(result_list, key= lambda x:x['created'], reverse=True)
|
||||||
|
elif order == 'oldest':
|
||||||
|
# order results by created
|
||||||
|
result_list = sorted(result_list, key= lambda x:x['created'], reverse=False)
|
||||||
|
elif q != '':
|
||||||
|
# order results by RANK
|
||||||
|
result_list = sorted(result_list, key= lambda x:x['rank'], reverse=True)
|
||||||
|
|
||||||
result_list = list(result_set)
|
total_results = len(result_list)
|
||||||
if order == 'newest':
|
# RESULTS PAGINATION
|
||||||
# order results by created
|
if limit is not None and offset is not None:
|
||||||
ordered_products = sorted(result_list, key= lambda x:x.created, reverse=True)
|
limit = int(limit)
|
||||||
elif order == 'oldest':
|
offset = int(offset)
|
||||||
# order results by created
|
result_list = result_list[offset:(limit+offset)]
|
||||||
ordered_products = sorted(result_list, key= lambda x:x.created, reverse=False)
|
elif limit is not None:
|
||||||
else:
|
limit = int(limit)
|
||||||
# order results by RANK
|
result_list = result_list[:limit]
|
||||||
ordered_products = sorted(result_list, key= lambda rank:rank.rank, reverse=True)
|
return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices})
|
||||||
|
except Exception as e:
|
||||||
# extract max and min price values
|
return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
serializer = SearchResultSerializer(ordered_products, many=True)
|
|
||||||
product_results = [dict(i) for i in serializer.data]
|
|
||||||
total_results = len(product_results)
|
|
||||||
|
|
||||||
# RESULTS PAGINATION
|
|
||||||
if limit is not None and offset is not None:
|
|
||||||
limit = int(limit)
|
|
||||||
offset = int(offset)
|
|
||||||
product_results = product_results[offset:(limit+offset)]
|
|
||||||
elif limit is not None:
|
|
||||||
limit = int(limit)
|
|
||||||
product_results = product_results[:limit]
|
|
||||||
|
|
||||||
return Response(data={"filters": filters, "count": total_results, "products": product_results, 'prices': prices})
|
|
||||||
except Exception as e:
|
|
||||||
return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user