mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-24 12:41:02 +02:00
Feature/librarian (#310)
* Add fields to library schema * Added list function, incomplete * Librarian list operation
This commit is contained in:
parent
5575e885e5
commit
f1559c5944
6 changed files with 177 additions and 27 deletions
|
|
@ -1,11 +1,21 @@
|
||||||
|
|
||||||
from pulsar.schema import Record, Bytes, String, Array
|
from pulsar.schema import Record, Bytes, String, Array, Long
|
||||||
from . types import Triple
|
from . types import Triple
|
||||||
from . topic import topic
|
from . topic import topic
|
||||||
from . types import Error
|
from . types import Error
|
||||||
from . metadata import Metadata
|
from . metadata import Metadata
|
||||||
from . documents import Document, TextDocument
|
from . documents import Document, TextDocument
|
||||||
|
|
||||||
|
# add
|
||||||
|
# -> (id, document)
|
||||||
|
# <- ()
|
||||||
|
# <- (error)
|
||||||
|
|
||||||
|
# list
|
||||||
|
# -> (user, collection?)
|
||||||
|
# <- (info)
|
||||||
|
# <- (error)
|
||||||
|
|
||||||
# add(Metadata, Bytes) : error?
|
# add(Metadata, Bytes) : error?
|
||||||
# copy(id, user, collection)
|
# copy(id, user, collection)
|
||||||
# move(id, user, collection)
|
# move(id, user, collection)
|
||||||
|
|
@ -17,19 +27,25 @@ from . documents import Document, TextDocument
|
||||||
# search(<key,op,value>[]) : id[]
|
# search(<key,op,value>[]) : id[]
|
||||||
|
|
||||||
class DocumentPackage(Record):
|
class DocumentPackage(Record):
|
||||||
metadata = Array(Triple())
|
id = String()
|
||||||
document = Bytes()
|
document = Bytes()
|
||||||
kind = String()
|
kind = String()
|
||||||
user = String()
|
user = String()
|
||||||
collection = String()
|
collection = String()
|
||||||
title = String()
|
title = String()
|
||||||
comments = String()
|
comments = String()
|
||||||
|
time = Long()
|
||||||
|
metadata = Array(Triple())
|
||||||
|
|
||||||
class DocumentInfo(Record):
|
class DocumentInfo(Record):
|
||||||
metadata = Array(Triple())
|
id = String()
|
||||||
kind = String()
|
kind = String()
|
||||||
user = String()
|
user = String()
|
||||||
collection = String()
|
collection = String()
|
||||||
|
title = String()
|
||||||
|
comments = String()
|
||||||
|
time = Long()
|
||||||
|
metadata = Array(Triple())
|
||||||
|
|
||||||
class Criteria(Record):
|
class Criteria(Record):
|
||||||
key = String()
|
key = String()
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,10 @@ class LibrarianRequestor(ServiceRequestor):
|
||||||
response["document"] = serialize_document_package(message.document)
|
response["document"] = serialize_document_package(message.document)
|
||||||
|
|
||||||
if message.info:
|
if message.info:
|
||||||
response["info"] = serialize_document_info(message.info)
|
response["info"] = [
|
||||||
|
serialize_document_info(v)
|
||||||
|
for v in message.info
|
||||||
|
]
|
||||||
|
|
||||||
return response, True
|
return response, True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ def serialize_document_package(message):
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
|
if message.id:
|
||||||
|
ret["id"] = message.id
|
||||||
|
|
||||||
if message.metadata:
|
if message.metadata:
|
||||||
ret["metadata"] = serialize_subgraph(message.metdata)
|
ret["metadata"] = serialize_subgraph(message.metdata)
|
||||||
|
|
||||||
|
|
@ -108,8 +111,8 @@ def serialize_document_info(message):
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
if message.metadata:
|
if message.id:
|
||||||
ret["metadata"] = serialize_subgraph(message.metdata)
|
ret["id"] = message.id
|
||||||
|
|
||||||
if message.kind:
|
if message.kind:
|
||||||
ret["kind"] = message.kind
|
ret["kind"] = message.kind
|
||||||
|
|
@ -120,25 +123,45 @@ def serialize_document_info(message):
|
||||||
if message.collection:
|
if message.collection:
|
||||||
ret["collection"] = message.collection
|
ret["collection"] = message.collection
|
||||||
|
|
||||||
|
if message.title:
|
||||||
|
ret["title"] = message.title
|
||||||
|
|
||||||
|
if message.comments:
|
||||||
|
ret["comments"] = message.comments
|
||||||
|
|
||||||
|
if message.time:
|
||||||
|
ret["time"] = message.time
|
||||||
|
|
||||||
|
if message.metadata:
|
||||||
|
ret["metadata"] = serialize_subgraph(message.metadata)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def to_document_package(x):
|
def to_document_package(x):
|
||||||
|
|
||||||
return DocumentPackage(
|
return DocumentPackage(
|
||||||
metadata = to_subgraph(x["metadata"]),
|
id = x.get("id", None),
|
||||||
document = x.get("document", None),
|
|
||||||
kind = x.get("kind", None),
|
kind = x.get("kind", None),
|
||||||
user = x.get("user", None),
|
user = x.get("user", None),
|
||||||
collection = x.get("collection", None),
|
collection = x.get("collection", None),
|
||||||
|
title = x.get("title", None),
|
||||||
|
comments = x.get("comments", None),
|
||||||
|
time = x.get("time", None),
|
||||||
|
document = x.get("document", None),
|
||||||
|
metadata = to_subgraph(x["metadata"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_document_info(x):
|
def to_document_info(x):
|
||||||
|
|
||||||
return DocumentInfo(
|
return DocumentInfo(
|
||||||
metadata = to_subgraph(x["metadata"]),
|
id = x.get("id", None),
|
||||||
kind = x.get("kind", None),
|
kind = x.get("kind", None),
|
||||||
user = x.get("user", None),
|
user = x.get("user", None),
|
||||||
collection = x.get("collection", None),
|
collection = x.get("collection", None),
|
||||||
|
title = x.get("title", None),
|
||||||
|
comments = x.get("comments", None),
|
||||||
|
time = x.get("time", None),
|
||||||
|
metadata = to_subgraph(x["metadata"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_criteria(x):
|
def to_criteria(x):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .. schema import LibrarianRequest, LibrarianResponse, Error
|
from .. schema import LibrarianRequest, LibrarianResponse, Error, Triple
|
||||||
from .. knowledge import hash
|
from .. knowledge import hash
|
||||||
from .. exceptions import RequestError
|
from .. exceptions import RequestError
|
||||||
from . table_store import TableStore
|
from . table_store import TableStore
|
||||||
|
|
@ -26,7 +26,7 @@ class Librarian:
|
||||||
self.load_document = load_document
|
self.load_document = load_document
|
||||||
self.load_text = load_text
|
self.load_text = load_text
|
||||||
|
|
||||||
async def add(self, id, document):
|
async def add(self, document):
|
||||||
|
|
||||||
if document.kind not in (
|
if document.kind not in (
|
||||||
"text/plain", "application/pdf"
|
"text/plain", "application/pdf"
|
||||||
|
|
@ -41,9 +41,9 @@ class Librarian:
|
||||||
self.table_store.add(object_id, document)
|
self.table_store.add(object_id, document)
|
||||||
|
|
||||||
if document.kind == "application/pdf":
|
if document.kind == "application/pdf":
|
||||||
await self.load_document(id, document)
|
await self.load_document(document)
|
||||||
elif document.kind == "text/plain":
|
elif document.kind == "text/plain":
|
||||||
await self.load_text(id, document)
|
await self.load_text(document)
|
||||||
|
|
||||||
print("Add complete", flush=True)
|
print("Add complete", flush=True)
|
||||||
|
|
||||||
|
|
@ -53,6 +53,20 @@ class Librarian:
|
||||||
info = None,
|
info = None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def list(self, user, collection):
|
||||||
|
|
||||||
|
print("list")
|
||||||
|
|
||||||
|
info = self.table_store.list(user, collection)
|
||||||
|
|
||||||
|
print(">>", info)
|
||||||
|
|
||||||
|
return LibrarianResponse(
|
||||||
|
error = None,
|
||||||
|
document = None,
|
||||||
|
info = info,
|
||||||
|
)
|
||||||
|
|
||||||
def handle_triples(self, m):
|
def handle_triples(self, m):
|
||||||
self.table_store.add_triples(m)
|
self.table_store.add_triples(m)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,11 +223,11 @@ class Processor(ConsumerProducer):
|
||||||
|
|
||||||
self.librarian.handle_document_embeddings(msg)
|
self.librarian.handle_document_embeddings(msg)
|
||||||
|
|
||||||
async def load_document(self, id, document):
|
async def load_document(self, document):
|
||||||
|
|
||||||
doc = Document(
|
doc = Document(
|
||||||
metadata = Metadata(
|
metadata = Metadata(
|
||||||
id = id,
|
id = document.id,
|
||||||
metadata = document.metadata,
|
metadata = document.metadata,
|
||||||
user = document.user,
|
user = document.user,
|
||||||
collection = document.collection
|
collection = document.collection
|
||||||
|
|
@ -237,14 +237,14 @@ class Processor(ConsumerProducer):
|
||||||
|
|
||||||
self.document_load.send(None, doc)
|
self.document_load.send(None, doc)
|
||||||
|
|
||||||
async def load_text(self, id, document):
|
async def load_text(self, document):
|
||||||
|
|
||||||
text = base64.b64decode(document.document)
|
text = base64.b64decode(document.document)
|
||||||
text = text.decode("utf-8")
|
text = text.decode("utf-8")
|
||||||
|
|
||||||
doc = TextDocument(
|
doc = TextDocument(
|
||||||
metadata = Metadata(
|
metadata = Metadata(
|
||||||
id = id,
|
id = document.id,
|
||||||
metadata = document.metadata,
|
metadata = document.metadata,
|
||||||
user = document.user,
|
user = document.user,
|
||||||
collection = document.collection
|
collection = document.collection
|
||||||
|
|
@ -259,19 +259,33 @@ class Processor(ConsumerProducer):
|
||||||
if v.operation is None:
|
if v.operation is None:
|
||||||
raise RequestError("Null operation")
|
raise RequestError("Null operation")
|
||||||
|
|
||||||
|
print("op", v.operation)
|
||||||
|
|
||||||
if v.operation == "add":
|
if v.operation == "add":
|
||||||
if (
|
if (
|
||||||
v.id and v.document and v.document.metadata and
|
v.document and v.document.id and v.document.metadata and
|
||||||
v.document.document and v.document.kind
|
v.document.document and v.document.kind
|
||||||
):
|
):
|
||||||
return partial(
|
return partial(
|
||||||
self.librarian.add,
|
self.librarian.add,
|
||||||
id = v.id,
|
|
||||||
document = v.document,
|
document = v.document,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise RequestError("Invalid call")
|
raise RequestError("Invalid call")
|
||||||
|
|
||||||
|
if v.operation == "list":
|
||||||
|
print("list", v)
|
||||||
|
print(v.user)
|
||||||
|
if v.user:
|
||||||
|
return partial(
|
||||||
|
self.librarian.list,
|
||||||
|
user = v.user,
|
||||||
|
collection = v.collection,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("BROK")
|
||||||
|
raise RequestError("Invalid call")
|
||||||
|
|
||||||
raise RequestError("Invalid operation: " + v.operation)
|
raise RequestError("Invalid operation: " + v.operation)
|
||||||
|
|
||||||
async def handle(self, msg):
|
async def handle(self, msg):
|
||||||
|
|
@ -298,6 +312,7 @@ class Processor(ConsumerProducer):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = await func()
|
resp = await func()
|
||||||
|
print("->", resp)
|
||||||
except RequestError as e:
|
except RequestError as e:
|
||||||
resp = LibrarianResponse(
|
resp = LibrarianResponse(
|
||||||
error = Error(
|
error = Error(
|
||||||
|
|
@ -318,7 +333,7 @@ class Processor(ConsumerProducer):
|
||||||
await self.send(resp, properties={"id": id})
|
await self.send(resp, properties={"id": id})
|
||||||
return
|
return
|
||||||
|
|
||||||
print("Send response...", flush=True)
|
print("Send response..!.", flush=True)
|
||||||
|
|
||||||
await self.send(resp, properties={"id": id})
|
await self.send(resp, properties={"id": id})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from .. schema import LibrarianRequest, LibrarianResponse, Error
|
from .. schema import LibrarianRequest, LibrarianResponse
|
||||||
|
from .. schema import DocumentInfo, Error, Triple, Value
|
||||||
from .. knowledge import hash
|
from .. knowledge import hash
|
||||||
from .. exceptions import RequestError
|
from .. exceptions import RequestError
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ class TableStore:
|
||||||
CREATE TABLE IF NOT EXISTS document (
|
CREATE TABLE IF NOT EXISTS document (
|
||||||
user text,
|
user text,
|
||||||
collection text,
|
collection text,
|
||||||
id uuid,
|
id text,
|
||||||
time timestamp,
|
time timestamp,
|
||||||
title text,
|
title text,
|
||||||
comments text,
|
comments text,
|
||||||
|
|
@ -157,6 +158,20 @@ class TableStore:
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
self.list_document_stmt = self.cassandra.prepare("""
|
||||||
|
SELECT
|
||||||
|
id, kind, user, collection, title, comments, time, metadata
|
||||||
|
FROM document
|
||||||
|
WHERE user = ?
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.list_document_by_collection_stmt = self.cassandra.prepare("""
|
||||||
|
SELECT
|
||||||
|
id, kind, user, collection, title, comments, time, metadata
|
||||||
|
FROM document
|
||||||
|
WHERE user = ? AND collection = ?
|
||||||
|
""")
|
||||||
|
|
||||||
self.insert_triples_stmt = self.cassandra.prepare("""
|
self.insert_triples_stmt = self.cassandra.prepare("""
|
||||||
INSERT INTO triples
|
INSERT INTO triples
|
||||||
(
|
(
|
||||||
|
|
@ -192,10 +207,9 @@ class TableStore:
|
||||||
raise RequestError("Invalid document kind: " + document.kind)
|
raise RequestError("Invalid document kind: " + document.kind)
|
||||||
|
|
||||||
# Create random doc ID
|
# Create random doc ID
|
||||||
doc_id = uuid.uuid4()
|
|
||||||
when = int(time.time() * 1000)
|
when = int(time.time() * 1000)
|
||||||
|
|
||||||
print("Adding", object_id, doc_id)
|
print("Adding", document.id, object_id)
|
||||||
|
|
||||||
metadata = [
|
metadata = [
|
||||||
(
|
(
|
||||||
|
|
@ -205,8 +219,6 @@ class TableStore:
|
||||||
for v in document.metadata
|
for v in document.metadata
|
||||||
]
|
]
|
||||||
|
|
||||||
# FIXME: doc_id should be the user-supplied ID???
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -214,7 +226,7 @@ class TableStore:
|
||||||
resp = self.cassandra.execute(
|
resp = self.cassandra.execute(
|
||||||
self.insert_document_stmt,
|
self.insert_document_stmt,
|
||||||
(
|
(
|
||||||
doc_id, document.user, document.collection,
|
document.id, document.user, document.collection,
|
||||||
document.kind, object_id, when,
|
document.kind, object_id, when,
|
||||||
document.title, document.comments,
|
document.title, document.comments,
|
||||||
metadata
|
metadata
|
||||||
|
|
@ -275,6 +287,73 @@ class TableStore:
|
||||||
print(f"{e}, retry...", flush=True)
|
print(f"{e}, retry...", flush=True)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
def list(self, user, collection=None):
|
||||||
|
|
||||||
|
print("LIST")
|
||||||
|
while True:
|
||||||
|
|
||||||
|
print("TRY")
|
||||||
|
|
||||||
|
print(self.list_document_stmt)
|
||||||
|
try:
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
resp = self.cassandra.execute(
|
||||||
|
self.list_document_by_collection_stmt,
|
||||||
|
(user, collection)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
resp = self.cassandra.execute(
|
||||||
|
self.list_document_stmt,
|
||||||
|
(user,)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Exception:", type(e))
|
||||||
|
print(f"{e}, retry...", flush=True)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
print("OK2")
|
||||||
|
|
||||||
|
info = [
|
||||||
|
DocumentInfo(
|
||||||
|
id = row[0],
|
||||||
|
kind = row[1],
|
||||||
|
user = row[2],
|
||||||
|
collection = row[3],
|
||||||
|
title = row[4],
|
||||||
|
comments = row[5],
|
||||||
|
time = int(1000 * row[6].timestamp()),
|
||||||
|
metadata = [
|
||||||
|
Triple(
|
||||||
|
s=Value(value=m[0], is_uri=m[1]),
|
||||||
|
p=Value(value=m[2], is_uri=m[3]),
|
||||||
|
o=Value(value=m[4], is_uri=m[5])
|
||||||
|
)
|
||||||
|
for m in row[7]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
for row in resp
|
||||||
|
]
|
||||||
|
|
||||||
|
print("OK3")
|
||||||
|
|
||||||
|
print(info[0])
|
||||||
|
|
||||||
|
print(info[0].user)
|
||||||
|
print(info[0].time)
|
||||||
|
print(info[0].kind)
|
||||||
|
print(info[0].collection)
|
||||||
|
print(info[0].title)
|
||||||
|
print(info[0].comments)
|
||||||
|
print(info[0].metadata)
|
||||||
|
print(info[0].metadata)
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
def add_graph_embeddings(self, m):
|
def add_graph_embeddings(self, m):
|
||||||
|
|
||||||
when = int(time.time() * 1000)
|
when = int(time.time() * 1000)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue