Skip to content
Merged
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
21 changes: 12 additions & 9 deletions openedx/core/djangoapps/content/search/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,23 +345,26 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None:
status_cb("Indexing libraries...")
for lib_key in lib_keys:
status_cb(f"{num_contexts_done + 1}/{num_contexts}. Now indexing library {lib_key}")
try:
docs = []
for component in lib_api.get_library_components(lib_key):
docs = []
for component in lib_api.get_library_components(lib_key):
try:
metadata = lib_api.LibraryXBlockMetadata.from_component(lib_key, component)
doc = {}
doc.update(searchable_doc_for_library_block(metadata))
doc.update(searchable_doc_tags(metadata.usage_key))
docs.append(doc)

except Exception as err: # pylint: disable=broad-except
status_cb(f"Error indexing library component {component}: {err}")
finally:
num_blocks_done += 1
if docs:
if docs:
try:
# Add all the docs in this library at once (usually faster than adding one at a time):
_wait_for_meili_task(client.index(temp_index_name).add_documents(docs))
except Exception as err: # pylint: disable=broad-except
status_cb(f"Error indexing library {lib_key}: {err}")
finally:
num_contexts_done += 1
except (TypeError, KeyError, MeilisearchError) as err:
status_cb(f"Error indexing library {lib_key}: {err}")

num_contexts_done += 1

############## Courses ##############
status_cb("Indexing courses...")
Expand Down
82 changes: 68 additions & 14 deletions openedx/core/djangoapps/content/search/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def setUp(self):
)
lib_access, _ = SearchAccess.objects.get_or_create(context_key=self.library.key)
# Populate it with a problem:
self.problem = library_api.create_library_block(self.library.key, "problem", "p1")
self.doc_problem = {
self.problem1 = library_api.create_library_block(self.library.key, "problem", "p1")
self.doc_problem1 = {
"id": "lborg1libproblemp1-a698218e",
"usage_key": "lb:org1:lib:problem:p1",
"block_id": "p1",
Expand All @@ -133,6 +133,20 @@ def setUp(self):
"type": "library_block",
"access_id": lib_access.id,
}
self.problem2 = library_api.create_library_block(self.library.key, "problem", "p2")
self.doc_problem2 = {
"id": "lborg1libproblemp2-b2f65e29",
"usage_key": "lb:org1:lib:problem:p2",
"block_id": "p2",
"display_name": "Blank Problem",
"block_type": "problem",
"context_key": "lib:org1:lib",
"org": "org1",
"breadcrumbs": [{"display_name": "Library"}],
"content": {"problem_types": [], "capa_content": " "},
"type": "library_block",
"access_id": lib_access.id,
}

# Create a couple of taxonomies with tags
self.taxonomyA = tagging_api.create_taxonomy(name="A", export_id="A")
Expand All @@ -159,14 +173,52 @@ def test_reindex_meilisearch(self, mock_meilisearch):
doc_sequential["tags"] = {}
doc_vertical = copy.deepcopy(self.doc_vertical)
doc_vertical["tags"] = {}
doc_problem = copy.deepcopy(self.doc_problem)
doc_problem["tags"] = {}
doc_problem1 = copy.deepcopy(self.doc_problem1)
doc_problem1["tags"] = {}
doc_problem2 = copy.deepcopy(self.doc_problem2)
doc_problem2["tags"] = {}

api.rebuild_index()
mock_meilisearch.return_value.index.return_value.add_documents.assert_has_calls(
[
call([doc_sequential, doc_vertical]),
call([doc_problem]),
call([doc_problem1, doc_problem2]),
],
any_order=True,
)

@override_settings(MEILISEARCH_ENABLED=True)
def test_reindex_meilisearch_library_block_error(self, mock_meilisearch):

# Add tags field to doc, since reindex calls includes tags
doc_sequential = copy.deepcopy(self.doc_sequential)
doc_sequential["tags"] = {}
doc_vertical = copy.deepcopy(self.doc_vertical)
doc_vertical["tags"] = {}
doc_problem2 = copy.deepcopy(self.doc_problem2)
doc_problem2["tags"] = {}

orig_from_component = library_api.LibraryXBlockMetadata.from_component

def mocked_from_component(lib_key, component):
# Simulate an error when processing problem 1
if component.key == 'xblock.v1:problem:p1':
raise Exception('Error')

return orig_from_component(lib_key, component)

with patch.object(
library_api.LibraryXBlockMetadata,
"from_component",
new=mocked_from_component,
):
api.rebuild_index()

mock_meilisearch.return_value.index.return_value.add_documents.assert_has_calls(
[
call([doc_sequential, doc_vertical]),
# Problem 1 should not be indexed
call([doc_problem2]),
],
any_order=True,
)
Expand Down Expand Up @@ -245,9 +297,9 @@ def test_index_library_block_metadata(self, mock_meilisearch):
"""
Test indexing a Library Block.
"""
api.upsert_library_block_index_doc(self.problem.usage_key)
api.upsert_library_block_index_doc(self.problem1.usage_key)

mock_meilisearch.return_value.index.return_value.update_documents.assert_called_once_with([self.doc_problem])
mock_meilisearch.return_value.index.return_value.update_documents.assert_called_once_with([self.doc_problem1])

@override_settings(MEILISEARCH_ENABLED=True)
def test_index_library_block_tags(self, mock_meilisearch):
Expand All @@ -256,19 +308,19 @@ def test_index_library_block_tags(self, mock_meilisearch):
"""

# Tag XBlock (these internally call `upsert_block_tags_index_docs`)
tagging_api.tag_object(str(self.problem.usage_key), self.taxonomyA, ["one", "two"])
tagging_api.tag_object(str(self.problem.usage_key), self.taxonomyB, ["three", "four"])
tagging_api.tag_object(str(self.problem1.usage_key), self.taxonomyA, ["one", "two"])
tagging_api.tag_object(str(self.problem1.usage_key), self.taxonomyB, ["three", "four"])

# Build expected docs with tags at each stage
doc_problem_with_tags1 = {
"id": self.doc_problem["id"],
"id": self.doc_problem1["id"],
"tags": {
'taxonomy': ['A'],
'level0': ['A > one', 'A > two']
}
}
doc_problem_with_tags2 = {
"id": self.doc_problem["id"],
"id": self.doc_problem1["id"],
"tags": {
'taxonomy': ['A', 'B'],
'level0': ['A > one', 'A > two', 'B > four', 'B > three']
Expand All @@ -288,10 +340,10 @@ def test_delete_index_library_block(self, mock_meilisearch):
"""
Test deleting a Library Block doc from the index.
"""
api.delete_index_doc(self.problem.usage_key)
api.delete_index_doc(self.problem1.usage_key)

mock_meilisearch.return_value.index.return_value.delete_document.assert_called_once_with(
self.doc_problem['id']
self.doc_problem1['id']
)

@override_settings(MEILISEARCH_ENABLED=True)
Expand All @@ -301,4 +353,6 @@ def test_index_content_library_metadata(self, mock_meilisearch):
"""
api.upsert_content_library_index_docs(self.library.key)

mock_meilisearch.return_value.index.return_value.update_documents.assert_called_once_with([self.doc_problem])
mock_meilisearch.return_value.index.return_value.update_documents.assert_called_once_with(
[self.doc_problem1, self.doc_problem2]
)