validity parsing
parent
71f525e80c
commit
f1ceb27ac6
@ -1,7 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="CPP_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/cozopy/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/cozorocks/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/cozoserver/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
@ -1,141 +0,0 @@
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
db_test_flights
|
@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "cozopy"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
name = "cozopy"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0.81"
|
||||
anyhow = "1.0.58"
|
||||
env_logger = "0.9.0"
|
||||
pyo3 = { version = "0.16.5", features = ["extension-module"] }
|
||||
cozo = { path = ".." }
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,132 +0,0 @@
|
||||
import json
|
||||
import time
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from cozo import *
|
||||
|
||||
|
||||
def remove_nan(d):
|
||||
return {k: v for (k, v) in d.items() if v is not None and v == v}
|
||||
|
||||
|
||||
# put airport {
|
||||
# iata: string,
|
||||
# icao: string indexed,
|
||||
# city: string identity,
|
||||
# country: ref
|
||||
# }
|
||||
# put whatever: string;
|
||||
# retract attr
|
||||
|
||||
def insert_data(destroy_on_exit):
|
||||
db = CozoDb('db_test_flights', destroy_on_exit=destroy_on_exit)
|
||||
try:
|
||||
payload = [
|
||||
*DefAttrs('country')
|
||||
.code(Typing.string, index=Indexing.identity)
|
||||
.desc(Typing.string)(),
|
||||
|
||||
*DefAttrs('continent')
|
||||
.code(Typing.string, index=Indexing.identity)
|
||||
.desc(Typing.string)(),
|
||||
|
||||
*DefAttrs('airport')
|
||||
.iata(Typing.string, index=Indexing.identity)
|
||||
.icao(Typing.string, index=Indexing.indexed)
|
||||
.city(Typing.string, index=Indexing.indexed)
|
||||
.desc(Typing.string)
|
||||
.region(Typing.string, index=Indexing.indexed)
|
||||
.country(Typing.ref)
|
||||
.runways(Typing.int)
|
||||
.longest(Typing.int)
|
||||
.altitude(Typing.int)
|
||||
.lat(Typing.float)
|
||||
.lon(Typing.float)(),
|
||||
|
||||
*DefAttrs('route')
|
||||
.src(Typing.ref)
|
||||
.dst(Typing.ref)
|
||||
.distance(Typing.int)(),
|
||||
|
||||
*DefAttrs('geo')
|
||||
.contains(Typing.ref)(),
|
||||
]
|
||||
start_time = time.time()
|
||||
tx_res = db.tx_attr(payload)['results']
|
||||
end_time = time.time()
|
||||
|
||||
print(f'{len(tx_res)} attributes added in {(end_time - start_time) * 1000:.3f}ms')
|
||||
|
||||
insertions = []
|
||||
nodes = pd.read_csv('air-routes-latest-nodes.csv', index_col=0)
|
||||
|
||||
continents = nodes[nodes['~label'] == 'continent']
|
||||
for tuple in continents.itertuples():
|
||||
put_payload = remove_nan(
|
||||
{'_temp_id': str(tuple.Index), 'continent.code': tuple._3, 'continent.desc': tuple._5})
|
||||
insertions.append(Put(put_payload))
|
||||
|
||||
country_idx = {}
|
||||
countries = nodes[nodes['~label'] == 'country']
|
||||
for tuple in countries.itertuples():
|
||||
put_payload = remove_nan({'_temp_id': str(tuple.Index), 'country.code': tuple._3, 'country.desc': tuple._5})
|
||||
country_idx[tuple._3] = str(tuple.Index)
|
||||
insertions.append(Put(put_payload))
|
||||
|
||||
airports = nodes[nodes['~label'] == 'airport']
|
||||
for tuple in airports.itertuples():
|
||||
put_payload = remove_nan({
|
||||
'_temp_id': str(tuple.Index),
|
||||
'airport.iata': tuple._3,
|
||||
'airport.icao': None if tuple._4 == 'none' else tuple._4,
|
||||
'airport.desc': tuple._5,
|
||||
'airport.region': tuple._6,
|
||||
'airport.runways': int(tuple._7),
|
||||
'airport.longest': int(tuple._8),
|
||||
'airport.altitude': int(tuple._9),
|
||||
'airport.country': country_idx[tuple._10],
|
||||
'airport.city': tuple._11,
|
||||
'airport.lat': tuple._12,
|
||||
'airport.lon': tuple._13
|
||||
})
|
||||
insertions.append(Put(put_payload))
|
||||
|
||||
edges = pd.read_csv('air-routes-latest-edges.csv', index_col=0)
|
||||
|
||||
for tuple in edges[edges['~label'] == 'route'].itertuples():
|
||||
payload = remove_nan(
|
||||
{'route.src': str(tuple._1), 'route.dst': str(tuple._2), 'route.distance': int(tuple._4)})
|
||||
insertions.append(Put(payload))
|
||||
|
||||
for tuple in edges[edges['~label'] == 'contains'].itertuples():
|
||||
payload = remove_nan({'_temp_id': str(tuple._1), 'geo.contains': str(tuple._2)})
|
||||
insertions.append(Put(payload))
|
||||
|
||||
start_time = time.time()
|
||||
d_res = db.tx(insertions)['results']
|
||||
end_time = time.time()
|
||||
print(f'{len(d_res)} node data added in {(end_time - start_time) * 1000:.3f}ms')
|
||||
print(f'{len(d_res) / (end_time - start_time):.0f} insertions per second')
|
||||
with open('air-routes-data.json', 'w', encoding='utf-8') as f:
|
||||
for row in insertions:
|
||||
json.dump(row['put'], f, ensure_ascii=False)
|
||||
f.write('\n')
|
||||
except Exception as e:
|
||||
print(f'data already exists? {e}')
|
||||
return db
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
db = insert_data(False)
|
||||
start_time = time.time()
|
||||
# res = db.run([Q(['?c', '?code', '?desc'],
|
||||
# Disj(T.country.code('?c', 'CU'),
|
||||
# Unify('?c', 10000239)),
|
||||
# T.country.code('?c', '?code'),
|
||||
# T.country.desc('?c', '?desc'))])
|
||||
res = db.run([Q([Max('?n')],
|
||||
T.route.distance('?a', '?n'))])
|
||||
end_time = time.time()
|
||||
print(json.dumps(res, indent=2))
|
||||
print(f'{len(res)} results fetched in {(end_time - start_time) * 1000:.3f}ms')
|
@ -1,250 +0,0 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
|
||||
from cozopy import CozoDbPy
|
||||
|
||||
|
||||
class CozoDb:
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.inner = CozoDbPy(*args, **kwargs)
|
||||
|
||||
def tx_attr(self, payload):
|
||||
return json.loads(self.inner.transact_attributes(json.dumps({'attrs': payload}, ensure_ascii=False)))
|
||||
|
||||
def tx(self, payload):
|
||||
return json.loads(self.inner.transact_triples(json.dumps({'tx': payload}, ensure_ascii=False)))
|
||||
|
||||
def run(self, q, out=None, limit=None, offset=None, sort=None):
|
||||
payload = {'q': q}
|
||||
if out is not None:
|
||||
payload['out'] = out
|
||||
if limit is not None:
|
||||
payload['limit'] = limit
|
||||
if offset is not None:
|
||||
payload['offset'] = offset
|
||||
if sort is not None:
|
||||
payload['sort'] = [convert_sort_arg(arg) for arg in sort]
|
||||
return json.loads(self.inner.run_query(json.dumps(payload, ensure_ascii=False)))
|
||||
|
||||
|
||||
def convert_sort_arg(arg):
|
||||
if arg.startswith('+'):
|
||||
return {arg[1:]: 'asc'}
|
||||
elif arg.startswith('-'):
|
||||
return {arg[1:]: 'desc'}
|
||||
else:
|
||||
return {arg: 'asc'}
|
||||
|
||||
|
||||
class Typing(str, Enum):
|
||||
ref = 'ref'
|
||||
component = 'component'
|
||||
bool = 'bool'
|
||||
int = 'int'
|
||||
float = 'float'
|
||||
string = 'string'
|
||||
name = 'name'
|
||||
uuid = 'uuid'
|
||||
timestamp = 'timestamp'
|
||||
bytes = 'bytes'
|
||||
list = 'list'
|
||||
|
||||
|
||||
class Cardinality(str, Enum):
|
||||
one = 'one'
|
||||
many = 'many'
|
||||
|
||||
|
||||
class Indexing(str, Enum):
|
||||
none = 'none'
|
||||
indexed = 'indexed'
|
||||
unique = 'unique'
|
||||
identity = 'identity'
|
||||
|
||||
|
||||
def Attribute(name, typing, id, cardinality, index, history):
|
||||
ret = {
|
||||
'name': name,
|
||||
'type': typing,
|
||||
'cardinality': cardinality,
|
||||
'index': index,
|
||||
'history': history
|
||||
}
|
||||
if id is not None:
|
||||
ret['id'] = id
|
||||
return ret
|
||||
|
||||
|
||||
def PutAttr(name, typing, id=None, cardinality=Cardinality.one, index=Indexing.none, history=False):
|
||||
return {
|
||||
'put': Attribute(name, typing, id, cardinality, index, history)
|
||||
}
|
||||
|
||||
|
||||
def RetractAttr(name, typing, id, cardinality, index, history):
|
||||
return {
|
||||
'retract': Attribute(name, typing, id, cardinality, index, history)
|
||||
}
|
||||
|
||||
|
||||
def Put(d):
|
||||
return {'put': d}
|
||||
|
||||
|
||||
def Retract(d):
|
||||
return {'retract': d}
|
||||
|
||||
|
||||
def Pull(variable, spec):
|
||||
return {'pull': variable, 'spec': spec}
|
||||
|
||||
|
||||
class DefAttrs:
|
||||
def __init__(self, prefix):
|
||||
self.prefix = prefix
|
||||
self.attrs = []
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.attrs
|
||||
|
||||
def __getattr__(self, item):
|
||||
return DefAttributesHelper(self, item)
|
||||
|
||||
|
||||
class DefAttributesHelper:
|
||||
def __init__(self, parent, name):
|
||||
self.parent = parent
|
||||
self.name = name
|
||||
|
||||
def __call__(self, typing, id=None, cardinality=Cardinality.one, index=Indexing.none, history=False):
|
||||
name = f'{self.parent.prefix}.{self.name}'
|
||||
self.parent.attrs.append({
|
||||
'put': Attribute(name, typing, id, cardinality, index, history)
|
||||
})
|
||||
return self.parent
|
||||
|
||||
|
||||
class TripleClass:
|
||||
def __init__(self, attr_name):
|
||||
self._attr_name = attr_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._attr_name is None:
|
||||
return self.__class__(name)
|
||||
else:
|
||||
return self.__class__(self._attr_name + '.' + name)
|
||||
|
||||
def __call__(self, entity, value):
|
||||
if self._attr_name is None:
|
||||
raise Exception("you need to set the triple attribute first")
|
||||
return [entity, self._attr_name, value]
|
||||
|
||||
|
||||
T = TripleClass(None)
|
||||
|
||||
|
||||
class RuleClass:
|
||||
def __init__(self, rule_name):
|
||||
self._rule_name = rule_name
|
||||
self._at = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._rule_name is None:
|
||||
return self.__class__(name)
|
||||
elif name == 'at':
|
||||
def closure(time):
|
||||
self._at = time
|
||||
return self
|
||||
|
||||
return closure
|
||||
else:
|
||||
raise Exception("cannot nest rule name")
|
||||
|
||||
def __call__(self, *args):
|
||||
if self._rule_name is None:
|
||||
raise Exception("you need to set the rule name first")
|
||||
ret = {'rule': self._rule_name, 'args': list(args)}
|
||||
if self._at:
|
||||
ret['at'] = self._at
|
||||
return ret
|
||||
|
||||
|
||||
R = RuleClass(None)
|
||||
Q = RuleClass('?')
|
||||
|
||||
|
||||
class OpClass:
|
||||
def __init__(self, op_name):
|
||||
self._op_name = op_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._op_name is None:
|
||||
return self.__class__(name)
|
||||
else:
|
||||
raise Exception("cannot nest op name")
|
||||
|
||||
def __call__(self, *args):
|
||||
if self._op_name is None:
|
||||
raise Exception("you need to set the op name first")
|
||||
ret = {'op': self._op_name, 'args': list(args)}
|
||||
return ret
|
||||
|
||||
|
||||
Gt = OpClass('Gt')
|
||||
Lt = OpClass('Lt')
|
||||
Ge = OpClass('Ge')
|
||||
Le = OpClass('Le')
|
||||
Eq = OpClass('Eq')
|
||||
Neq = OpClass('Neq')
|
||||
Add = OpClass('Add')
|
||||
Sub = OpClass('Sub')
|
||||
Mul = OpClass('Mul')
|
||||
Div = OpClass('Div')
|
||||
StrCat = OpClass('StrCat')
|
||||
|
||||
|
||||
class AggrClass:
|
||||
def __init__(self, aggr_name):
|
||||
self._aggr_name = aggr_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._aggr_name is None:
|
||||
return self.__class__(name)
|
||||
else:
|
||||
raise Exception("cannot nest aggr name")
|
||||
|
||||
def __call__(self, symb):
|
||||
if self._aggr_name is None:
|
||||
raise Exception("you need to set the predicate name first")
|
||||
ret = {'aggr': self._aggr_name, 'symb': symb}
|
||||
return ret
|
||||
|
||||
|
||||
Count = AggrClass('count')
|
||||
Min = AggrClass('min')
|
||||
Max = AggrClass('max')
|
||||
|
||||
|
||||
def Const(item):
|
||||
return {'const': item}
|
||||
|
||||
|
||||
def Conj(*items):
|
||||
return {'conj': items}
|
||||
|
||||
|
||||
def Disj(*items):
|
||||
return {'disj': items}
|
||||
|
||||
|
||||
def NotExists(item):
|
||||
return {'not_exists': item}
|
||||
|
||||
|
||||
def Unify(binding, expr):
|
||||
return {'unify': binding, 'expr': expr}
|
||||
|
||||
|
||||
__all__ = ['Gt', 'Lt', 'Ge', 'Le', 'Eq', 'Neq', 'Add', 'Sub', 'Mul', 'Div', 'Q', 'T', 'R', 'Const', 'Conj', 'Disj',
|
||||
'NotExists', 'CozoDb', 'Typing', 'Cardinality', 'Indexing', 'PutAttr', 'RetractAttr', 'Attribute', 'Put',
|
||||
'Retract', 'Pull', 'StrCat', 'Unify', 'DefAttrs', 'Count', 'Min', 'Max']
|
@ -1,84 +0,0 @@
|
||||
from cozo import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
db = CozoDb('_test', destroy_on_exit=True)
|
||||
res = db.tx_attr(
|
||||
DefAttrs('person')
|
||||
.idd(Typing.string, index=Indexing.identity)
|
||||
.first_name(Typing.string, index=Indexing.indexed)
|
||||
.last_name(Typing.string, index=Indexing.indexed)
|
||||
.age(Typing.int, index=Indexing.indexed)
|
||||
.friend(Typing.ref, cardinality=Cardinality.many, index=Indexing.indexed)
|
||||
.weight(Typing.float, index=Indexing.indexed)
|
||||
.covid(Typing.bool)()
|
||||
)
|
||||
print(res)
|
||||
print(db.tx_attr([
|
||||
PutAttr('person.id', Typing.string, id=res['results'][0][0], cardinality=Cardinality.one,
|
||||
index=Indexing.identity),
|
||||
RetractAttr('person.covid', Typing.bool, id=res['results'][-1][0], cardinality=Cardinality.one, history=False,
|
||||
index=Indexing.indexed),
|
||||
]))
|
||||
print(db.tx([
|
||||
Put({'_temp_id': 'alice',
|
||||
'person.first_name': 'Alice',
|
||||
'person.age': 7,
|
||||
'person.last_name': 'Amorist',
|
||||
'person.id': 'alice_amorist',
|
||||
'person.weight': 25,
|
||||
'person.friend': 'eve'}),
|
||||
Put({'_temp_id': 'bob',
|
||||
'person.first_name': 'Bob',
|
||||
'person.age': 70,
|
||||
'person.last_name': 'Wonderland',
|
||||
'person.id': 'bob_wonderland',
|
||||
'person.weight': 100,
|
||||
'person.friend': 'alice'
|
||||
}),
|
||||
Put({'_temp_id': 'eve',
|
||||
'person.first_name': 'Eve',
|
||||
'person.age': 18,
|
||||
'person.last_name': 'Faking',
|
||||
'person.id': 'eve_faking',
|
||||
'person.weight': 50,
|
||||
'person.friend': [
|
||||
'alice',
|
||||
'bob',
|
||||
{'person.first_name': 'Charlie',
|
||||
'person.age': 22,
|
||||
'person.last_name': 'Goodman',
|
||||
'person.id': 'charlie_goodman',
|
||||
'person.weight': 120,
|
||||
'person.friend': 'eve'}
|
||||
]
|
||||
}),
|
||||
Put({'_temp_id': 'david',
|
||||
'person.first_name': 'David',
|
||||
'person.age': 7,
|
||||
'person.last_name': 'Dull',
|
||||
'person.id': 'david_dull',
|
||||
'person.weight': 25,
|
||||
'person.friend': {
|
||||
'_temp_id': 'george',
|
||||
'person.first_name': 'George',
|
||||
'person.age': 7,
|
||||
'person.last_name': 'Geomancer',
|
||||
'person.id': 'george_geomancer',
|
||||
'person.weight': 25,
|
||||
'person.friend': 'george'}
|
||||
}),
|
||||
]))
|
||||
res = db.run([
|
||||
R.ff(['?a', '?b'],
|
||||
T.person.friend('?a', '?b')),
|
||||
R.ff(['?a', '?b'],
|
||||
T.person.friend('?a', '?c'),
|
||||
R.ff('?c', '?b')),
|
||||
Q(['?a'],
|
||||
T.person.first_name('?a', '?n'),
|
||||
T.person.first_name('?alice', 'Alice'),
|
||||
NotExists(R.ff('?a', '?alice'))
|
||||
),
|
||||
],
|
||||
out={'friend': Pull('?a', ['person.first_name'])})
|
||||
print(res)
|
@ -1,69 +0,0 @@
|
||||
// #[cfg(not(target_env = "msvc"))]
|
||||
// use tikv_jemallocator::Jemalloc;
|
||||
//
|
||||
// #[cfg(not(target_env = "msvc"))]
|
||||
// #[global_allocator]
|
||||
// static GLOBAL: Jemalloc = Jemalloc;
|
||||
//
|
||||
use pyo3::exceptions::PyException;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use cozo::{Db, DbBuilder};
|
||||
|
||||
#[pyclass(extends = PyException)]
|
||||
struct ErrorBridge(cozo::Error);
|
||||
|
||||
trait PyResultExt<T> {
|
||||
fn into_py_res(self) -> PyResult<T>;
|
||||
}
|
||||
|
||||
impl<T> PyResultExt<T> for anyhow::Result<T> {
|
||||
fn into_py_res(self) -> PyResult<T> {
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(e) => Err(PyException::new_err(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct CozoDbPy {
|
||||
db: Db,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl CozoDbPy {
|
||||
#[new]
|
||||
#[args(create_if_missing = true, destroy_on_exit = false)]
|
||||
fn new(path: &str, create_if_missing: bool, destroy_on_exit: bool) -> PyResult<Self> {
|
||||
let _ = env_logger::try_init();
|
||||
let builder = DbBuilder::default()
|
||||
.path(path)
|
||||
.create_if_missing(create_if_missing)
|
||||
.destroy_on_exit(destroy_on_exit);
|
||||
let db = Db::build(builder).into_py_res()?;
|
||||
Ok(Self { db })
|
||||
}
|
||||
pub fn transact_attributes(&self, py: Python<'_>, payload: &str) -> PyResult<String> {
|
||||
let payload: serde_json::Value = serde_json::from_str(payload).unwrap();
|
||||
let ret = py.allow_threads(|| self.db.transact_attributes(&payload).into_py_res())?;
|
||||
Ok(ret.to_string())
|
||||
}
|
||||
pub fn transact_triples(&self, py: Python<'_>, payload: &str) -> PyResult<String> {
|
||||
let payload: serde_json::Value = serde_json::from_str(payload).unwrap();
|
||||
let ret = py.allow_threads(|| self.db.transact_triples(&payload).into_py_res())?;
|
||||
Ok(ret.to_string())
|
||||
}
|
||||
pub fn run_query(&self, py: Python<'_>, payload: &str) -> PyResult<String> {
|
||||
let payload: serde_json::Value = serde_json::from_str(payload).unwrap();
|
||||
let ret = py.allow_threads(|| self.db.run_query(&payload).into_py_res())?;
|
||||
Ok(ret.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn cozopy(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||
m.add_class::<CozoDbPy>()?;
|
||||
m.add_class::<ErrorBridge>()?;
|
||||
Ok(())
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "cozohttp"
|
||||
name = "cozoserver"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
Loading…
Reference in New Issue