From 1c8a5c3764d9637b877f498ac248dacc4bf59b44 Mon Sep 17 00:00:00 2001 From: j Date: Tue, 15 Jan 2019 14:08:42 +0530 Subject: [PATCH] fulltext search in macosx --- config.json | 6 ++++++ oml/fulltext.py | 28 ++++++++++++++++++++++++++++ oml/queryparser.py | 17 +++++++++++++++-- oml/settings.py | 8 ++++++++ oml/user/api.py | 2 ++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 oml/fulltext.py diff --git a/config.json b/config.json index 813dff5..6c410b3 100644 --- a/config.json +++ b/config.json @@ -241,6 +241,12 @@ "format": {"type": "boolean", "args": []}, "sort": true }, + { + "id": "fulltext", + "title": "Full Text", + "find": true, + "type": "text" + }, { "id": "random", "title": "Random", diff --git a/oml/fulltext.py b/oml/fulltext.py new file mode 100644 index 0000000..579427d --- /dev/null +++ b/oml/fulltext.py @@ -0,0 +1,28 @@ +import logging +import os +import subprocess +import sys + + +logger = logging.getLogger(__name__) + +def find_fulltext_macos(query): + import settings + from item.models import File + prefix = os.path.join(os.path.expanduser(settings.preferences['libraryPath']), 'Books/') + cmd = ["mdfind", "-onlyin", prefix, query] + books = subprocess.check_output(cmd).decode().strip().split('\n') + books = [path[len(prefix):] for path in books] + ids = [b[0] for b in File.query.filter(operators.in_op(File.path, books)).values('sha1')] + return ids + +def find_fulltext(query): + ids = [] + if sys.platform == 'darwin': + ids = find_fulltext_macos(query) + else: + logger.debug('missing fulltext search implementation for %s', sys.platform) + return ids + +def platform_supported(): + return sys.platform == 'darwin' diff --git a/oml/queryparser.py b/oml/queryparser.py index 6ad4a5d..27acd42 100644 --- a/oml/queryparser.py +++ b/oml/queryparser.py @@ -9,6 +9,7 @@ from sqlalchemy.sql.expression import text import utils import settings +from fulltext import find_fulltext import logging logger = logging.getLogger(__name__) @@ -25,7 +26,7 @@ def get_operator(op, type='str'): '$': operators.endswith_op, '&': operators.in_op, }, - 'int': { + 'int': { '==': operators.eq, '>': operators.gt, '>=': operators.ge, @@ -65,7 +66,7 @@ class Parser(object): ... ''' #logger.debug('parse_condition %s', condition) - if not 'value' in condition: + if 'value' not in condition: return None k = condition.get('key', '*') if not k: @@ -122,6 +123,18 @@ class Parser(object): in_op = operators.notin_op if exclude else operators.in_op q = in_op(self._model.id, ids) return q + elif k == 'fulltext': + ids = find_fulltext(v) + if ids: + in_op = operators.notin_op if exclude else operators.in_op + q = in_op(self._model.id, ids) + else: + # nothing + q = operators.eq(self._model.id, -1) + if exclude: + q = ~q + return q + elif key_type in ("string", "text"): if isinstance(v, str): v = unicodedata.normalize('NFKD', v).lower() diff --git a/oml/settings.py b/oml/settings.py index c66722f..345182a 100644 --- a/oml/settings.py +++ b/oml/settings.py @@ -6,6 +6,7 @@ import os from oml.pdict import pdict from oml.utils import get_user_id +from oml import fulltext base_dir = os.path.normpath(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')) static_path = os.path.join(base_dir, 'static') @@ -90,3 +91,10 @@ DEBUG_HTTP = server.get('debug_http', False) DEBUG_API = server.get('debug_api', False) DB_VERSION = 13 + + +FULLTEXT_SUPPORT = fulltext.platform_supported() + +if not FULLTEXT_SUPPORT: + config['itemKeys'] = [k for k in config['itemKeys'] if k['id'] != 'fulltext'] + diff --git a/oml/user/api.py b/oml/user/api.py index 242b5fa..63216e9 100644 --- a/oml/user/api.py +++ b/oml/user/api.py @@ -35,6 +35,8 @@ def init(data): if os.path.exists(settings.oml_data_path): with open(settings.oml_data_path) as fd: config = json.load(fd) + if not settings.FULLTEXT_SUPPORT: + config['itemKeys'] = [k for k in config['itemKeys'] if k['id'] != 'fulltext'] else: config = {} response['config'] = config