|
|
@@ -9,6 +9,8 @@ import mmh3 |
|
|
|
import oursql |
|
|
|
|
|
|
|
from .migration import VERSION, MIGRATIONS |
|
|
|
from ..query.nodes import (String, Regex, Text, Language, Author, Date, Symbol, |
|
|
|
BinaryOp, UnaryOp) |
|
|
|
|
|
|
|
__all__ = ["Database"] |
|
|
|
|
|
|
@@ -51,23 +53,64 @@ class Database(object): |
|
|
|
"Run `python -m bitshift.database.migration`." |
|
|
|
raise RuntimeError(err) |
|
|
|
|
|
|
|
def _search_with_query(self, cursor, query): |
|
|
|
"""Convert a query tree into SQL SELECTs, execute, and return results. |
|
|
|
def _explode_query_tree(self, tree): |
|
|
|
"""Convert a query tree into components of an SQL SELECT statement.""" |
|
|
|
def _parse_node(node): |
|
|
|
if isinstance(node, Text): |
|
|
|
tables |= {"code", "symbols"} |
|
|
|
# (FTS: codelet_name, =: symbol_name, FTS: code_code) vs. node.text (_Literal) |
|
|
|
pass |
|
|
|
elif isinstance(node, Language): |
|
|
|
tables |= {"code"} |
|
|
|
return "(code_lang = ?)", [node.lang] |
|
|
|
elif isinstance(node, Author): |
|
|
|
tables |= {"authors"} |
|
|
|
# (FTS: author_name) vs. node.name (_Literal) |
|
|
|
pass |
|
|
|
elif isinstance(node, Date): |
|
|
|
# read node.type, node.relation |
|
|
|
# (>=/<=: codelet_date_created / codelet_date_modified) vs. node.date (datetime.datetime) |
|
|
|
pass |
|
|
|
elif isinstance(node, Symbol): |
|
|
|
tables |= {"symbols"} |
|
|
|
# (symbol_type, symbol_name) vs. (node.type, node.name) |
|
|
|
pass |
|
|
|
elif isinstance(node, BinaryOp): |
|
|
|
left_cond, left_args = _parse_node(node.left) |
|
|
|
right_cond, right_args = _parse_node(node.right) |
|
|
|
op = node.OPS[node.op] |
|
|
|
cond = "(" + left_cond + " " + op + " " + right_cond + ")" |
|
|
|
return cond, left_args + right_args |
|
|
|
elif isinstance(node, UnaryOp): |
|
|
|
cond, args = _parse_node(node.node) |
|
|
|
return "(" + node.OPS[node.op] + " " + cond + ")", args |
|
|
|
|
|
|
|
tables = set() |
|
|
|
conditional, arglist = _parse_node(tree.root) |
|
|
|
# joins = " ".join(tables) |
|
|
|
|
|
|
|
return conditional, joins, tuple(arglist) |
|
|
|
|
|
|
|
def _search_with_query(self, cursor, query, page): |
|
|
|
"""Execute an SQL query based on a query tree, and return results. |
|
|
|
|
|
|
|
The returned data is a 2-tuple of (list of codelet IDs, estimated |
|
|
|
number of total results). |
|
|
|
""" |
|
|
|
raise NotImplementedError() ## TODO |
|
|
|
|
|
|
|
results = cursor.fetchall() |
|
|
|
ids = NotImplemented ## TODO: extract ids from results |
|
|
|
num_results = NotImplemented ## TODO: num if results else 0 |
|
|
|
|
|
|
|
conditional, joins, args = self._explode_query_tree(query) |
|
|
|
base = "SELECT codelet_id FROM codelets %s WHERE %s LIMIT 10" |
|
|
|
qstring = base % (joins, conditional) |
|
|
|
if page > 1: |
|
|
|
qstring += " OFFSET %d" % ((page - 1) * 10) |
|
|
|
|
|
|
|
cursor.execute(qstring, args) |
|
|
|
ids = [id for id, in cursor.fetchall()] |
|
|
|
num_results = 0 # TODO: NotImplemented |
|
|
|
return ids, num_results |
|
|
|
|
|
|
|
def _get_codelets_from_ids(self, cursor, ids): |
|
|
|
"""Return a list of Codelet objects given a list of codelet IDs.""" |
|
|
|
raise NotImplementedError() ## TODO |
|
|
|
raise NotImplementedError() # TODO |
|
|
|
|
|
|
|
def _decompose_url(self, cursor, url): |
|
|
|
"""Break up a URL into an origin (with a URL base) and a suffix.""" |
|
|
|