Fakultas Ilmu Komputer UI

Commit 51f3a1e5 authored by Alfian Fuadi Rafli's avatar Alfian Fuadi Rafli
Browse files

[#115] Material: Search/Query By Author & Description

parent 5b62333e
......@@ -50,17 +50,27 @@ class MateriManager(models.Manager):
return SoftDeletionQuerySet(self.model)
def search(self, search_text):
search_vector = search.SearchVector("title", weight="A")
search_vector = None
for field, weight in Materi.SEARCH_INDEX:
if search_vector is None:
search_vector = search.SearchVector(field, weight=weight)
else:
search_vector += search.SearchVector(field, weight=weight)
search_query = search.SearchQuery(search_text)
search_rank = search.SearchRank(search_vector, search_query)
search_result = (
self.get_queryset().filter(_search_vector=search_query).annotate(rank=search_rank).order_by("-rank")
self.get_queryset()
.filter(_search_vector=search_query)
.annotate(rank=search_rank)
.order_by("-rank")
)
return search_result
class SoftDeletionQuerySet(models.query.QuerySet):
def delete(self):
return super(SoftDeletionQuerySet, self).update(deleted_at=timezone.now())
......@@ -95,13 +105,32 @@ class Materi(SoftDeleteModel):
_search_vector = search.SearchVectorField(null=True, editable=False)
SEARCH_INDEX = (
("title", "A"),
("author", "A"),
("publisher", "C"),
("descriptions", "C"),
("uploader", "D"),
)
objects = MateriManager()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
search_index = {field: weight for (field, weight) in Materi.SEARCH_INDEX}
if "update_fields" in kwargs:
is_search_index_updated = bool(
set(search_index.keys()) & set(kwargs["update_fields"])
)
if ("update_fields" not in kwargs) or (is_search_index_updated):
self._search_vector = None
for field, weight in search_index.items():
if self._search_vector is None:
self._search_vector = search.SearchVector(field, weight=weight)
else:
self._search_vector += search.SearchVector(field, weight=weight)
if "update_fields" not in kwargs or "_search_vector" not in kwargs["update_fields"]:
self._search_vector = search.SearchVector("title", weight="A")
self.save(update_fields=["_search_vector"])
@property
......
......@@ -29,16 +29,7 @@ class DafterKatalogService:
@staticmethod
def search_materi(get_search, lst_materi, url):
url = url + "&search={0}".format(get_search)
lst_materi = (
lst_materi.search(get_search)
.filter(
Q(author__icontains=get_search)
| Q(uploader__name__icontains=get_search)
| Q(descriptions__icontains=get_search)
| Q(publisher__icontains=get_search)
)
.distinct()
)
lst_materi = lst_materi.search(get_search)
return lst_materi, url
@staticmethod
......
......@@ -89,52 +89,6 @@ class DaftarKatalogTest(TestCase):
resp = Materi.objects.get(id=materi.id)
self.assertEqual(resp, materi)
def test_materi_model_generate_search_vector_after_save(self):
Materi(title="Eating book").save()
search_vector_new_materi = list(Materi.objects.values_list("_search_vector", flat=True))
expected_search_vector = ["'book':2A 'eat':1A"]
self.assertSequenceEqual(search_vector_new_materi, expected_search_vector)
def test_search_text_on_empty_database(self):
search_query = "test"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_search_text_on_unmatched_data(self):
Materi(title="test satu sekarang").save()
Materi(title="test dua nanti").save()
search_query = "besok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_search_text_return_list_matched_by_rank(self):
materi_2 = Materi(title="ini lumayan cocok lumayan cocok")
materi_2.save()
materi_1 = Materi(title="ini sangat cocok sangat cocok sangat cocok")
materi_1.save()
materi_4 = Materi(title="ini tidak")
materi_4.save()
materi_3 = Materi(title="ini sedikit cocok")
materi_3.save()
search_query = "ini cocok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = [materi_1, materi_2, materi_3]
self.assertSequenceEqual(search_result, expected_search_result)
class DaftarKatalogSortingByJumlahUnduhTest(TestCase):
def setUp(self):
......@@ -3631,6 +3585,151 @@ class MateriRecommendationTest(TestCase):
list = [int(id) for id in re.findall(r"Materi\s(\d+)[^\d]", response.content.decode())]
self.assertEqual(list, [1, 2])
class MateriSearchVectorTest(TestCase):
def setUp(self):
Materi.SEARCH_INDEX = (("title", "A"), ("author", "B"))
def test_search_vector_constructed_on_create(self):
materi = Materi(title="Buku 1", author="Pembuat 1")
materi.save()
search_vector_string = list(
Materi.objects.values_list("_search_vector", flat=True)
)[0]
self.assertGreaterEqual(len(search_vector_string.split(",")), 1)
def test_search_vector_based_on_indexed_attribute(self):
materi = Materi(title="Buku 1", author="Pembuat 1", descriptions="Deskripsi 1")
materi.save()
search_vector_string = list(
Materi.objects.values_list("_search_vector", flat=True)
)[0]
self.assertIn("buku", search_vector_string)
self.assertIn("pembuat", search_vector_string)
def test_search_vector_not_based_on_unindexed_attribute(self):
materi = Materi(title="Buku 1", author="Pembuat 1", descriptions="Deskripsi 1")
materi.save()
search_vector_string = list(
Materi.objects.values_list("_search_vector", flat=True)
)[0]
self.assertNotIn("deskripsi", search_vector_string)
def test_search_vector_reconstructed_on_update_indexed_field(self):
materi = Materi(title="Sebelum reconstruct")
materi.save()
search_vector = list(Materi.objects.values_list("_search_vector", flat=True))[0]
materi.title = "Setelah reconstruct"
materi.save()
search_vector = list(Materi.objects.values_list("_search_vector", flat=True))[0]
self.assertIn("setelah", search_vector)
def test_search_vector_not_reconstructed_on_update_unindexed_field(self):
materi = Materi(descriptions="sebelum reconstruct")
materi.save()
search_vector = list(Materi.objects.values_list("_search_vector", flat=True))[0]
materi.descriptions = "sebelum reconstruct"
materi.save()
search_vector = list(Materi.objects.values_list("_search_vector", flat=True))[0]
self.assertNotIn("setelah", search_vector)
class MateriSearchTest(TestCase):
def test_empty_result_on_empty_table(self):
search_query = "test"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_empty_result_on_unmatched_data(self):
Materi.SEARCH_INDEX = (("title", "A"), ("author", "B"))
Materi(title="buku 1", author="bapak 1").save()
Materi(title="artikel 2", author="ibu 1").save()
search_query = "majalah"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
search_query = "kakak"
search_result = list(Materi.objects.search(search_query))
expected_search_result = []
self.assertSequenceEqual(search_result, expected_search_result)
def test_correct_rank_on_result_tested_by_similiarity_words(self):
Materi.SEARCH_INDEX = (("descriptions", "A"),)
materi_2 = Materi(descriptions="ini lumayan cocok lumayan cocok")
materi_2.save()
materi_1 = Materi(descriptions="ini sangat cocok sangat cocok sangat cocok")
materi_1.save()
materi_4 = Materi(descriptions="ini tidak")
materi_4.save()
materi_3 = Materi(descriptions="ini sedikit cocok")
materi_3.save()
search_query = "ini cocok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = [materi_1, materi_2, materi_3]
self.assertSequenceEqual(search_result, expected_search_result)
def test_correct_rank_on_result_tested_by_weight(self):
Materi.SEARCH_INDEX = (
("title", "A"),
("author", "C"),
("descriptions", "B"),
("publisher", "D"),
)
materi_title = Materi(title="cocok")
materi_title.save()
materi_author = Materi(author="cocok cocok cocok")
materi_author.save()
materi_descriptions = Materi(descriptions="cocok cocok")
materi_descriptions.save()
materi_publisher = Materi(publisher="cocok cocok cocok cocok")
materi_publisher.save()
search_query = "cocok"
search_result = list(Materi.objects.search(search_query))
expected_search_result = [
materi_title,
materi_descriptions,
materi_author,
materi_publisher,
]
self.assertSequenceEqual(search_result, expected_search_result)
class BacaNantiTest(TestCase):
def setUp(self):
self.contributor_credential = {
......
......@@ -190,4 +190,4 @@ EMAIL_PORT = config('EMAIL_PORT', default=587) # use Google Mail SMTP as default
EMAIL_HOST_USER = config('EMAIL_HOST_USER', default="pmplclass2020@gmail.com")
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default="pmpldigipusemail")
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
\ No newline at end of file
EMAIL_USE_SSL = False
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment