replaced metadata search with cid lookup

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2022-12-21 11:05:07 +01:00
parent 9e5b7ac62d
commit 7ad4151984
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
8 changed files with 57 additions and 34 deletions

View File

@ -14,10 +14,10 @@ class MetaData:
metadata: Optional[str] = None metadata: Optional[str] = None
@staticmethod @staticmethod
def from_dict(meta_data_tuple: dict) -> MetaData | None: def from_dict(meta_data: dict) -> MetaData | None:
if meta_data_tuple is None: if meta_data is None:
return None return None
return MetaData(meta_data_tuple["meta_data"]) return MetaData(meta_data)
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {

View File

@ -96,6 +96,12 @@ def get_transactions_by_asset(connection, asset):
raise NotImplementedError raise NotImplementedError
@singledispatch
def get_transactions_by_metadata(connection, metadata: str, limit: int = 1000) -> list[DbTransaction]:
""" Get a transaction by its metadata cid."""
raise NotImplementedError
@singledispatch @singledispatch
def get_transactions(connection, transactions_ids) -> list[DbTransaction]: def get_transactions(connection, transactions_ids) -> list[DbTransaction]:
"""Get a transaction from the transactions table. """Get a transaction from the transactions table.

View File

@ -48,6 +48,11 @@ function init()
{ field = 'assets[*].data', type = 'string', is_nullable = true } { field = 'assets[*].data', type = 'string', is_nullable = true }
} }
}) })
transactions:create_index('transactions_by_metadata_cid', {
if_not_exists = true,
unique = false,
parts = {{ field = 'metadata', type = 'string' }}
})
transactions:create_index('spending_transaction_by_id_and_output_index', { transactions:create_index('spending_transaction_by_id_and_output_index', {
if_not_exists = true, if_not_exists = true,
parts = { parts = {
@ -63,6 +68,21 @@ function init()
} }
}) })
-- Governance
governance = box.schema.create_space('governance', { if_not_exists = true })
governance:format({
{ name = 'id', type = 'string' },
{ name = 'operation', type = 'string' },
{ name = 'version', type = 'string' },
{ name = 'metadata', type = 'string', is_nullable = true },
{ name = 'assets', type = 'array' },
{ name = 'inputs', type = 'array' },
{ name = 'scripts', type = 'map', is_nullable = true }
})
governance:create_index('id', {
if_not_exists = true,
parts = {{ field = 'id', type = 'string' }}
})
-- Outputs -- Outputs
outputs = box.schema.create_space('outputs', { if_not_exists = true }) outputs = box.schema.create_space('outputs', { if_not_exists = true })

View File

@ -56,11 +56,21 @@ def get_transaction(connection, tx_id: str) -> DbTransaction:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_transactions_by_asset(connection, asset: str, limit: int = 1000) -> list[DbTransaction]: def get_transactions_by_asset(connection, asset: str, limit: int = 1000) -> list[DbTransaction]:
txs = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset, limit=limit, index="transactions_by_asset_cid")) txs = connection.run(
connection.space(TARANT_TABLE_TRANSACTION).select(asset, limit=limit, index="transactions_by_asset_cid")
)
tx_ids = [tx[0] for tx in txs] tx_ids = [tx[0] for tx in txs]
return get_complete_transactions_by_ids(connection, tx_ids) return get_complete_transactions_by_ids(connection, tx_ids)
@register_query(TarantoolDBConnection)
def get_transactions_by_metadata(connection, metadata: str, limit: int = 1000) -> list[DbTransaction]:
txs = connection.run(
connection.space(TARANT_TABLE_TRANSACTION).select(metadata, limit=limit, index="transactions_by_metadata_cid")
)
tx_ids = [tx[0] for tx in txs]
return get_complete_transactions_by_ids(connection, tx_ids)
def store_transaction_outputs(connection, output: Output, index: int) -> str: def store_transaction_outputs(connection, output: Output, index: int) -> str:
output_id = uuid4().hex output_id = uuid4().hex
connection.run(connection.space(TARANT_TABLE_OUTPUT).insert(( connection.run(connection.space(TARANT_TABLE_OUTPUT).insert((

View File

@ -470,6 +470,10 @@ class Planetmint(object):
""" """
return backend.query.get_metadata(self.connection, txn_ids) return backend.query.get_metadata(self.connection, txn_ids)
def get_metadata_by_cid(self, metadata_cid, **kwargs) -> list[str]:
metadata_txs = backend.query.get_transactions_by_metadata(self.connection, metadata_cid, **kwargs)
return [tx.metadata.metadata for tx in metadata_txs]
@property @property
def fastquery(self): def fastquery(self):
return fastquery.FastQuery(self.connection) return fastquery.FastQuery(self.connection)

View File

@ -32,7 +32,7 @@ def r(*args, **kwargs):
ROUTES_API_V1 = [ ROUTES_API_V1 = [
r("/", info.ApiV1Index), r("/", info.ApiV1Index),
r("assets/<string:cid>", assets.AssetListApi), r("assets/<string:cid>", assets.AssetListApi),
r("metadata/", metadata.MetadataApi), r("metadata/<string:cid>", metadata.MetadataApi),
r("blocks/<int:block_id>", blocks.BlockApi), r("blocks/<int:block_id>", blocks.BlockApi),
r("blocks/latest", blocks.LatestBlock), r("blocks/latest", blocks.LatestBlock),
r("blocks/", blocks.BlockListApi), r("blocks/", blocks.BlockListApi),

View File

@ -18,33 +18,28 @@ logger = logging.getLogger(__name__)
class MetadataApi(Resource): class MetadataApi(Resource):
def get(self): def get(self, cid):
"""API endpoint to perform a text search on transaction metadata. """API endpoint to perform a text search on transaction metadata.
Args: Args:
search (str): Text search string to query the text index
limit (int, optional): Limit the number of returned documents. limit (int, optional): Limit the number of returned documents.
Return: Return:
A list of metadata that match the query. A list of metadata that match the query.
""" """
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
parser.add_argument("search", type=str, required=True)
parser.add_argument("limit", type=int) parser.add_argument("limit", type=int)
args = parser.parse_args() args = parser.parse_args()
if not args["search"]:
return make_error(400, "text_search cannot be empty")
if not args["limit"]: if not args["limit"]:
del args["limit"] del args["limit"]
pool = current_app.config["bigchain_pool"] pool = current_app.config["bigchain_pool"]
with pool() as planet: with pool() as planet:
args["table"] = "meta_data" metadata = planet.get_metadata_by_cid(cid, **args)
metadata = planet.text_search(**args)
try: try:
return list(metadata) return metadata
except OperationError as e: except OperationError as e:
return make_error(400, "({}): {}".format(type(e).__name__, e)) return make_error(400, "({}): {}".format(type(e).__name__, e))

View File

@ -11,22 +11,11 @@ from ipld import marshal, multihash
METADATA_ENDPOINT = "/api/v1/metadata/" METADATA_ENDPOINT = "/api/v1/metadata/"
def test_get_metadata_with_empty_text_search(client):
res = client.get(METADATA_ENDPOINT + "?search=")
assert res.json == {"status": 400, "message": "text_search cannot be empty"}
assert res.status_code == 400
def test_get_metadata_with_missing_text_search(client):
res = client.get(METADATA_ENDPOINT)
assert res.status_code == 400
@pytest.mark.bdb @pytest.mark.bdb
def test_get_metadata_tendermint(client, b, alice): def test_get_metadata_tendermint(client, b, alice):
assets = [{"data": multihash(marshal({"msg": "abc"}))}] assets = [{"data": multihash(marshal({"msg": "abc"}))}]
# test returns empty list when no assets are found # test returns empty list when no assets are found
res = client.get(METADATA_ENDPOINT + "?search=" + assets[0]["data"]) res = client.get(METADATA_ENDPOINT + assets[0]["data"])
assert res.json == [] assert res.json == []
assert res.status_code == 200 assert res.status_code == 200
@ -40,10 +29,10 @@ def test_get_metadata_tendermint(client, b, alice):
b.store_bulk_transactions([tx]) b.store_bulk_transactions([tx])
# test that metadata is returned # test that metadata is returned
res = client.get(METADATA_ENDPOINT + "?search=" + metadata) res = client.get(METADATA_ENDPOINT + metadata)
assert res.status_code == 200 assert res.status_code == 200
assert len(res.json) == 1 assert len(res.json) == 1
assert res.json[0] == {"meta_data": metadata, "id": tx.id} assert res.json[0] == metadata
@pytest.mark.bdb @pytest.mark.bdb
@ -51,25 +40,24 @@ def test_get_metadata_limit_tendermint(client, b, alice):
# create two assets # create two assets
assets1 = [{"data": multihash(marshal({"msg": "abc 1"}))}] assets1 = [{"data": multihash(marshal({"msg": "abc 1"}))}]
meta1 = multihash(marshal({"key": "meta 1"})) meta = multihash(marshal({"key": "meta 1"}))
tx1 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta1, assets=assets1).sign( tx1 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta, assets=assets1).sign(
[alice.private_key] [alice.private_key]
) )
b.store_bulk_transactions([tx1]) b.store_bulk_transactions([tx1])
assets2 = [{"data": multihash(marshal({"msg": "abc 2"}))}] assets2 = [{"data": multihash(marshal({"msg": "abc 2"}))}]
meta2 = multihash(marshal({"key": "meta 2"})) tx2 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta, assets=assets2).sign(
tx2 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta2, assets=assets2).sign(
[alice.private_key] [alice.private_key]
) )
b.store_bulk_transactions([tx2]) b.store_bulk_transactions([tx2])
# test that both assets are returned without limit # test that both assets are returned without limit
res = client.get(METADATA_ENDPOINT + "?search=" + meta1) res = client.get(METADATA_ENDPOINT + meta)
assert res.status_code == 200 assert res.status_code == 200
assert len(res.json) == 1 assert len(res.json) == 2
# test that only one asset is returned when using limit=1 # test that only one asset is returned when using limit=1
res = client.get(METADATA_ENDPOINT + "?search=" + meta2 + "&limit=1") res = client.get(METADATA_ENDPOINT + meta + "?limit=1")
assert res.status_code == 200 assert res.status_code == 200
assert len(res.json) == 1 assert len(res.json) == 1