partial improvements to search

This commit is contained in:
Sam
2021-03-02 13:17:37 +00:00
parent 5bab1d75cb
commit 1338570c05
2 changed files with 175 additions and 106 deletions

View File

@@ -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

View File

@@ -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)