Find a knowledge base article by words from different fields
This commit is contained in:
@@ -43,6 +43,39 @@ def _visible_articles():
|
||||
KnowledgeBase.appid.notin_(retired)))
|
||||
|
||||
|
||||
def _search_clause(search):
|
||||
"""Articles containing EVERY word of the search, each word in the title, the
|
||||
keywords, or the topic's name, in any order.
|
||||
|
||||
A single `ilike('%CMM Community%')` needs the whole phrase contiguous in ONE
|
||||
field. An article tagged 'community' under the CMM topic has 'CMM' only in
|
||||
its topic's name and 'Community' only in its keywords, so it matched nothing
|
||||
and the search came back empty. Splitting the query and requiring each word
|
||||
somewhere is what people mean when they type two words.
|
||||
|
||||
Kept in step with `_word_match` in shopdb/core/api/search.py, which does the
|
||||
same job for global search. Two copies because the core helper is internal
|
||||
and not on the plugin contract surface (ADR-001).
|
||||
|
||||
Active applications only for the topic. A retired application is not a topic
|
||||
anyone should be offered: matching its name surfaced its articles and printed
|
||||
the retired app as their subject, which reads as though it were still in
|
||||
service.
|
||||
"""
|
||||
words = [w for w in search.split() if w] or ['']
|
||||
return db.and_(*[
|
||||
db.or_(
|
||||
KnowledgeBase.shortdescription.ilike(f'%{w}%'),
|
||||
KnowledgeBase.keywords.ilike(f'%{w}%'),
|
||||
KnowledgeBase.appid.in_(
|
||||
db.session.query(Application.appid).filter(
|
||||
Application.appname.ilike(f'%{w}%'),
|
||||
Application.isactive.is_(True))),
|
||||
)
|
||||
for w in words
|
||||
])
|
||||
|
||||
|
||||
@knowledgebase_bp.route('', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
def list_articles():
|
||||
@@ -51,26 +84,11 @@ def list_articles():
|
||||
|
||||
query = _visible_articles()
|
||||
|
||||
# Search: title, keywords, and the topic (its Application's name). The topic
|
||||
# is matched via an appid subquery instead of a join so it does not collide
|
||||
# with the sort=='topic' join below; articles with no app just miss that
|
||||
# clause and still match on title/keywords.
|
||||
# The topic is matched via an appid subquery instead of a join so it does not
|
||||
# collide with the sort=='topic' join below; articles with no app just miss
|
||||
# that clause and still match on title/keywords.
|
||||
if search := request.args.get('search'):
|
||||
like = f'%{search}%'
|
||||
# Active applications only. A retired application is not a topic anyone
|
||||
# should be offered: matching its name surfaced its articles and printed
|
||||
# the retired app as their subject, which reads as though it were still
|
||||
# in service.
|
||||
topic_appids = db.session.query(Application.appid).filter(
|
||||
Application.appname.ilike(like),
|
||||
Application.isactive.is_(True))
|
||||
query = query.filter(
|
||||
db.or_(
|
||||
KnowledgeBase.shortdescription.ilike(like),
|
||||
KnowledgeBase.keywords.ilike(like),
|
||||
KnowledgeBase.appid.in_(topic_appids)
|
||||
)
|
||||
)
|
||||
query = query.filter(_search_clause(search))
|
||||
|
||||
# Filter by topic/application
|
||||
if appid := request.args.get('appid'):
|
||||
|
||||
Reference in New Issue
Block a user