Find a knowledge base article by words from different fields
This commit is contained in:
@@ -31,7 +31,7 @@ search_bp = Blueprint('search', __name__)
|
||||
# cannot reorder. Relevance is applied afterwards in Python, over a stable set.
|
||||
|
||||
|
||||
def _word_match(query, *columns):
|
||||
def _word_match(query, *columns, extra=None):
|
||||
"""SQL clause matching rows that contain EVERY word of the query, each word
|
||||
in any of the columns, in any order.
|
||||
|
||||
@@ -39,14 +39,25 @@ def _word_match(query, *columns):
|
||||
multi-word search returned nothing. This splits the query into words and ANDs
|
||||
them (OR across the columns per word): 'CSF Roles' matches a row with 'CSF'
|
||||
and 'Roles' anywhere in the searched fields.
|
||||
|
||||
`extra` is an optional callable taking one word and returning a further
|
||||
clause to OR in for that word. Use it where a word may be satisfied by a
|
||||
RELATED row rather than a column of this table - a knowledge base article
|
||||
matching on its topic's name, say. Splitting per word matters there: without
|
||||
it a word can only ever be matched by the columns, so a query naming the
|
||||
topic and a keyword ('CMM Community') matches neither side alone.
|
||||
"""
|
||||
words = [w for w in query.split() if w]
|
||||
if not words:
|
||||
words = ['']
|
||||
return db.and_(*[
|
||||
db.or_(*[c.ilike(f'%{w}%') for c in columns])
|
||||
for w in words
|
||||
])
|
||||
|
||||
def per_word(word):
|
||||
clauses = [c.ilike(f'%{word}%') for c in columns]
|
||||
if extra is not None:
|
||||
clauses.append(extra(word))
|
||||
return db.or_(*clauses)
|
||||
|
||||
return db.and_(*[per_word(w) for w in words])
|
||||
|
||||
|
||||
def _require_enabled(name):
|
||||
@@ -253,6 +264,10 @@ def _search_knowledgebase(query, search_term):
|
||||
subject, which reads as though it were still in service.
|
||||
|
||||
A null topic still matches. Not every article is about an application.
|
||||
|
||||
The topic's NAME is searched too, per word. An article tagged 'community'
|
||||
under the CMM topic has neither word in both of its own columns, so a search
|
||||
for 'CMM Community' found it in neither the plugin's listing nor here.
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
@@ -260,12 +275,24 @@ def _search_knowledgebase(query, search_term):
|
||||
from plugins.knowledgebase.models import KnowledgeBase
|
||||
retired = db.session.query(Application.appid).filter(
|
||||
Application.isactive.is_(False))
|
||||
|
||||
def topic_named(word):
|
||||
"""Articles whose topic is an ACTIVE application named like word.
|
||||
|
||||
Active only, for the same reason the retired filter above exists: a
|
||||
decommissioned application is not a topic anyone should be offered.
|
||||
"""
|
||||
return KnowledgeBase.appid.in_(
|
||||
db.session.query(Application.appid).filter(
|
||||
Application.appname.ilike(f'%{word}%'),
|
||||
Application.isactive.is_(True)))
|
||||
|
||||
kb_articles = KnowledgeBase.query.filter(
|
||||
KnowledgeBase.isactive == True,
|
||||
db.or_(KnowledgeBase.appid.is_(None),
|
||||
KnowledgeBase.appid.notin_(retired)),
|
||||
_word_match(query, KnowledgeBase.shortdescription,
|
||||
KnowledgeBase.keywords)
|
||||
KnowledgeBase.keywords, extra=topic_named)
|
||||
).order_by(KnowledgeBase.clicks.desc(),
|
||||
KnowledgeBase.linkid).limit(20).all()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user