Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement apexsearch #281

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 3 additions & 3 deletions frontend/src/pages/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@
class="block overflow-hidden rounded px-2.5 py-3 hover:bg-gray-100"
>
<div class="flex items-center">
<div class="text-base font-medium" v-html="item.title" />
<div class="text-base font-medium" v-html="item.highlighted_title || item.title" />
<span class="px-1 leading-none text-gray-600"> &middot; </span>
<div class="text-sm text-gray-600">
{{ timestamp(item) }}
</div>
</div>
<div
v-if="item.content"
v-if="item.highlighted_content"
class="mt-1 text-p-base text-gray-700"
v-html="trimContent(item.content)"
v-html="trimContent(item.highlighted_content)"
></div>
</router-link>
</div>
Expand Down
42 changes: 17 additions & 25 deletions gameplan/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import frappe
from frappe.utils import validate_email_address, split_emails, cstr
from gameplan.utils import validate_type
from gameplan.search import GameplanSearch


@frappe.whitelist(allow_guest=True)
Expand Down Expand Up @@ -323,39 +324,29 @@ def oauth_providers():


@frappe.whitelist()
def search(query, start=0):
from gameplan.search import GameplanSearch

def search(query):
search = GameplanSearch()
query = search.clean_query(query)

query_parts = query.split(" ")
if len(query_parts) == 1 and not query_parts[0].endswith("*"):
query = f"{query_parts[0]}*"
if len(query_parts) > 1:
query = " ".join([f"%%{q}%%" for q in query_parts])

result = search.search(
f"@title|content:({query})", start=start, sort_by="modified desc", highlight=True, with_payloads=True
)
result = search.search(query)

comments_by_doctype = {}
grouped_results = {}
for d in result.docs:
doctype, name = d.id.split(":")
for d in result["results"]:
d = frappe._dict(d)
doctype, name = d.id.split("-")
d.doctype = doctype
d.name = name
del d.id
if doctype == "GP Comment":
comments_by_doctype.setdefault(d.payload["reference_doctype"], []).append(d)
comments_by_doctype.setdefault(d.fields["reference_doctype"], []).append(d)
else:
d.project = d.payload.get("project")
d.team = d.payload.get("team")
del d.payload
d.project = d.fields.get("project")
d.team = d.fields.get("team")
del d.fields
grouped_results.setdefault(doctype, []).append(d)

discussion_names = [d.payload["reference_name"] for d in comments_by_doctype.get("GP Discussion", [])]
task_names = [d.payload["reference_name"] for d in comments_by_doctype.get("GP Task", [])]
discussion_names = [d.fields["reference_name"] for d in comments_by_doctype.get("GP Discussion", [])]
task_names = [d.fields["reference_name"] for d in comments_by_doctype.get("GP Task", [])]

if discussion_names:
for d in frappe.get_all(
Expand All @@ -369,8 +360,9 @@ def search(query, start=0):
d.via_comment = True
d.modified = d.last_post_at
for c in comments_by_doctype.get("GP Discussion", []):
if c.payload["reference_name"] == d.name:
if c.fields["reference_name"] == d.name:
d.content = c.content
d.highlighted_content = c.highlighted_content
grouped_results.setdefault("GP Discussion", []).append(d)

if task_names:
Expand All @@ -382,12 +374,12 @@ def search(query, start=0):
d.content = ""
d.via_comment = True
for c in comments_by_doctype.get("GP Task", []):
if c.payload["reference_name"] == d.name:
if c.fields["reference_name"] == d.name:
d.content = c.content
grouped_results.setdefault("GP Task", []).append(d)

return {
"results": grouped_results,
"total": result.total,
"duration": result.duration,
"total": result["total"],
"duration": result["duration"],
}
4 changes: 2 additions & 2 deletions gameplan/gameplan/doctype/gp_comment/gp_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ def update_discussion_index(self):
if self.reference_doctype in ["GP Discussion", "GP Task"]:
search = GameplanSearch()
if self.deleted_at:
search.remove_doc(self)
search.remove_record(f"{self.doctype}-{self.name}")
else:
search.index_doc(self)
search.reindex_record(self.as_dict(), self.doctype)
4 changes: 3 additions & 1 deletion gameplan/gameplan/doctype/gp_discussion/gp_discussion.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def after_insert(self):

def on_trash(self):
self.update_discussions_count(-1)
search = GameplanSearch()
search.remove_record(f"{self.doctype}-{self.name}")

def validate(self):
self.content = remove_empty_trailing_paragraphs(self.content)
Expand Down Expand Up @@ -75,7 +77,7 @@ def log_title_update(self):
def update_search_index(self):
if self.has_value_changed('title') or self.has_value_changed('content'):
search = GameplanSearch()
search.index_doc(self)
search.reindex_record(self.as_dict(), self.doctype)

def update_participants_count(self):
participants = frappe.db.get_all('GP Comment',
Expand Down
4 changes: 2 additions & 2 deletions gameplan/gameplan/doctype/gp_page/gp_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def on_update(self):
def update_search_index(self):
if self.has_value_changed('title') or self.has_value_changed('content'):
search = GameplanSearch()
search.index_doc(self)
search.reindex_record(self.as_dict(), self.doctype)

def on_trash(self):
search = GameplanSearch()
search.remove_doc(self)
search.remove_record(f"{self.doctype}-{self.name}")
5 changes: 3 additions & 2 deletions gameplan/gameplan/doctype/gp_task/gp_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ def log_value_updates(self):
def update_search_index(self):
if self.has_value_changed('title') or self.has_value_changed('description'):
search = GameplanSearch()
search.index_doc(self)
print(self.as_dict())
search.reindex_record(self.as_dict(), self.doctype)

def on_trash(self):
self.update_tasks_count(-1)
search = GameplanSearch()
search.remove_doc(self)
search.remove_record(f"{self.doctype}-{self.name}")

def update_tasks_count(self, delta=1):
if not self.project:
Expand Down
2 changes: 2 additions & 0 deletions gameplan/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,5 @@
# Recommended only for DocTypes which have limited documents with untranslated names
# For example: Role, Gender, etc.
# translated_search_doctypes = []

search_index_path = '/Users/safwan/frappe-bench/apps/test_index'
safwansamsudeen marked this conversation as resolved.
Show resolved Hide resolved
38 changes: 32 additions & 6 deletions gameplan/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import re

from apexsearch import ApexSearch
import frappe
from frappe.core.utils import html2text
from frappe.utils import cstr, update_progress_bar
Expand All @@ -15,9 +16,34 @@
UNSAFE_CHARS = re.compile(r"[\[\]{}<>+]")

INDEX_BUILD_FLAG = "discussions_index_in_progress"


class GameplanSearch(Search):
SCHEMA = {
"GP Discussion": {
"title": "title",
"content": ["content"],
"fields": ["team", "project", "modified"],
},
"GP Task": {
"title": "title",
"content": ["description"],
"fields": ["team", "project", "modified"],
},
"GP Page": {
"title": "title",
"content": ["content"],
"fields": ["team", "project", "modified"],
},
"GP Comment": {
"content": ["content"],
"fields": ["reference_name", "reference_doctype"]
},
}

class GameplanSearch(ApexSearch):
def __init__(self) -> None:
super().__init__(frappe.get_site_path('indexes', 'apex_search'), SCHEMA)

# Delete after review
class GameplanSearchOld(Search):
def __init__(self) -> None:
schema = [
{"name": "title", "weight": 5},
Expand Down Expand Up @@ -187,7 +213,7 @@ def get_accessible_projects(self):
def build_index():
frappe.cache().set_value(INDEX_BUILD_FLAG, True)
search = GameplanSearch()
search.build_index()
search.build_complete_index(lambda table, fields: frappe.get_all(table, fields=fields))
frappe.cache().set_value(INDEX_BUILD_FLAG, False)


Expand All @@ -198,5 +224,5 @@ def build_index_in_background():

def build_index_if_not_exists():
search = GameplanSearch()
if not search.index_exists() or not frappe.cache.exists(INDEX_BUILD_FLAG):
build_index()
if not search.index_exists or not frappe.cache.exists(INDEX_BUILD_FLAG):
search.build_complete_index(lambda table, fields: frappe.get_all(table, fields=fields))
Loading