d89c79b92d
Adds support for full text search in peewee with the creation of two new field types: `FullIndexedCharField` and `FullIndexedTextField`. Note that this change depends upon https://github.com/zzzeek/sqlalchemy/pull/339 [Delivers #137453279] [Delivers #137453317]
108 lines
2.8 KiB
Python
108 lines
2.8 KiB
Python
import base64
|
|
import resumablehashlib
|
|
import json
|
|
|
|
from peewee import TextField, CharField, Clause
|
|
from data.text import prefix_search
|
|
|
|
|
|
class _ResumableSHAField(TextField):
|
|
def _create_sha(self):
|
|
raise NotImplementedError
|
|
|
|
def db_value(self, value):
|
|
if value is None:
|
|
return None
|
|
|
|
sha_state = value.state()
|
|
|
|
# One of the fields is a byte string, let's base64 encode it to make sure
|
|
# we can store and fetch it regardless of default collocation.
|
|
sha_state[3] = base64.b64encode(sha_state[3])
|
|
|
|
return json.dumps(sha_state)
|
|
|
|
def python_value(self, value):
|
|
if value is None:
|
|
return None
|
|
|
|
sha_state = json.loads(value)
|
|
|
|
# We need to base64 decode the data bytestring.
|
|
sha_state[3] = base64.b64decode(sha_state[3])
|
|
to_resume = self._create_sha()
|
|
to_resume.set_state(sha_state)
|
|
return to_resume
|
|
|
|
|
|
class ResumableSHA256Field(_ResumableSHAField):
|
|
def _create_sha(self):
|
|
return resumablehashlib.sha256()
|
|
|
|
|
|
class ResumableSHA1Field(_ResumableSHAField):
|
|
def _create_sha(self):
|
|
return resumablehashlib.sha1()
|
|
|
|
|
|
class JSONField(TextField):
|
|
def db_value(self, value):
|
|
return json.dumps(value)
|
|
|
|
def python_value(self, value):
|
|
if value is None or value == "":
|
|
return {}
|
|
return json.loads(value)
|
|
|
|
|
|
class Base64BinaryField(TextField):
|
|
def db_value(self, value):
|
|
if value is None:
|
|
return None
|
|
return base64.b64encode(value)
|
|
|
|
def python_value(self, value):
|
|
if value is None:
|
|
return None
|
|
return base64.b64decode(value)
|
|
|
|
|
|
def _add_fulltext(field_class):
|
|
""" Adds support for full text indexing and lookup to the given field class. """
|
|
class indexed_class(field_class):
|
|
# Marker used by SQLAlchemy translation layer to add the proper index for full text searching.
|
|
__fulltext__ = True
|
|
|
|
def __init__(self, match_function, *args, **kwargs):
|
|
field_class.__init__(self, *args, **kwargs)
|
|
self.match_function = match_function
|
|
|
|
def match(self, query):
|
|
return self.match_function(self, query)
|
|
|
|
def match_prefix(self, query):
|
|
return prefix_search(self, query)
|
|
|
|
def __mod__(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
def __pow__(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
def __contains__(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
def contains(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
def startswith(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
def endswith(self, _):
|
|
raise Exception('Unsafe operation: Use `match` or `match_prefix`')
|
|
|
|
return indexed_class
|
|
|
|
|
|
FullIndexedCharField = _add_fulltext(CharField)
|
|
FullIndexedTextField = _add_fulltext(TextField)
|