Source code for bento_meta.mdb.searchable

"""
mdb.searchable
Subclass of `class:bento_meta.mdb.MDB` to support searching fulltext indices on an MDB
Note: certain fulltext indexes on certain MDB nodes and properties must be present in 
the Neo4j instance: 
- entityHandles
- termValue
- termDefn
- termValueDefn

"""
from bento_meta.mdb import read_txn, read_txn_data, read_txn_value
from bento_meta.mdb import MDB
from pdb import set_trace
[docs]class SearchableMDB(MDB): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.ftindexes = {} with self.driver.session() as s: result = s.run("call db.indexes") for rec in result: if rec['type'] == 'FULLTEXT': self.ftindexes[rec['name']] = { "entity_type":rec['entityType'], "entities":rec['labelsOrTypes'], "properties":rec['properties'], }
[docs] def available_indexes(self): """Fulltext indexes present in database. Returns { <index_name> : { entity_type:<NODE|RELATIONSHIP>, entities:[<labels>], properties:[ [<props>] ] } } """ return self.ftindexes
[docs] @read_txn_data def query_index(self, index, qstring, skip=None, limit=None): """Query a named fulltext index of nodes or relationships. Returns [ {ent:{}, label:<label>, score:<lucene score>} ].""" if index not in self.ftindexes: raise RuntimeError("Index with name '{}' not found".format(index)) tipe = "" if self.ftindexes[index]['entity_type'] == "NODE": tipe = "queryNodes" elif self.ftindexes[index]['entity_type'] == "RELATIONSHIP": tipe = "queryRelationships" else: raise RuntimeError("Wha?") thing = self.ftindexes[index]['entity_type'].lower() qry = ( "call db.index.fulltext.{tipe}($name, $query) " "yield {thing}, score " "return {thing} as ent, head(labels({thing})) as label, score " "{skip} {limit} " ).format(tipe=tipe, thing=thing, skip="SKIP $skip" if skip else "", limit="LIMIT $limit" if limit else "") parms = {"name": index, "query": qstring} if skip: parms['skip'] = skip if limit: parms['limit'] = limit return (qry, parms)
[docs] def search_entity_handles(self, qstring): """Fulltext search of qstring over node, relationship, and property handles. Returns { node:[ {ent:<entity dict>,score:<lucene score>},... ], relationship:[ <...> ], property:[ <...> ] }""" result = self.query_index('entityHandle', qstring) if not result: return None plural = {"node":"nodes", "relationship":"relationships", "property":"properties"} ret = {"nodes": [], "relationships": [], "properties": []} for item in result: ret[plural[item['label']]].append({"ent": item['ent'], "score": item['score']}) return ret
[docs] def search_terms(self, qstring, search_values=True, search_definitions=True): """Fulltext for qstring over terms, by value, definition, or both (default). Returns [ { ent:<term dict>, score:<lucene score> } ]}""" index = {True: {True: 'termValueDefn', False: 'termDefn'}, False: {True: 'termValue', False: None}} result = self.query_index(index[search_definitions][search_values], qstring) return result