diff --git a/app/models.py b/app/models.py
index b4d3fda4b9f0563394814cb97af4647a76cfdafd..fc88ba2ece8bdb5371cb8c88ad93e1130f81d3cb 100644
--- a/app/models.py
+++ b/app/models.py
@@ -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
diff --git a/app/services.py b/app/services.py
index d9b68cda0ab63f9e3709193987191dc69c311d10..d5990d1504b661f9aa1b4c88383acf5da9c92438 100644
--- a/app/services.py
+++ b/app/services.py
@@ -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
diff --git a/app/tests.py b/app/tests.py
index 83c8354be11574dd6d62197496718e5e75adfbd3..c71588983703b869f038359fd35eb75e12ff58cd 100644
--- a/app/tests.py
+++ b/app/tests.py
@@ -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 = {
diff --git a/digipus/settings.py b/digipus/settings.py
index a5c78be362117bb65ba17279f5f85e274c8304d8..7b0b547da75fd98b6085d6ba54cd700c68fc4d70 100644
--- a/digipus/settings.py
+++ b/digipus/settings.py
@@ -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