From 7ad41519847fc694af5b89ca1ee319695e74412e Mon Sep 17 00:00:00 2001 From: Lorenz Herzberger Date: Wed, 21 Dec 2022 11:05:07 +0100 Subject: [PATCH] replaced metadata search with cid lookup Signed-off-by: Lorenz Herzberger --- planetmint/backend/models/metadata.py | 6 +++--- planetmint/backend/query.py | 6 ++++++ planetmint/backend/tarantool/init.lua | 20 ++++++++++++++++++ planetmint/backend/tarantool/query.py | 12 ++++++++++- planetmint/lib.py | 4 ++++ planetmint/web/routes.py | 2 +- planetmint/web/views/metadata.py | 11 +++------- tests/web/test_metadata.py | 30 ++++++++------------------- 8 files changed, 57 insertions(+), 34 deletions(-) diff --git a/planetmint/backend/models/metadata.py b/planetmint/backend/models/metadata.py index 1603367..7fd1de7 100644 --- a/planetmint/backend/models/metadata.py +++ b/planetmint/backend/models/metadata.py @@ -14,10 +14,10 @@ class MetaData: metadata: Optional[str] = None @staticmethod - def from_dict(meta_data_tuple: dict) -> MetaData | None: - if meta_data_tuple is None: + def from_dict(meta_data: dict) -> MetaData | None: + if meta_data is None: return None - return MetaData(meta_data_tuple["meta_data"]) + return MetaData(meta_data) def to_dict(self) -> dict: return { diff --git a/planetmint/backend/query.py b/planetmint/backend/query.py index bf08148..943feaa 100644 --- a/planetmint/backend/query.py +++ b/planetmint/backend/query.py @@ -96,6 +96,12 @@ def get_transactions_by_asset(connection, asset): 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 def get_transactions(connection, transactions_ids) -> list[DbTransaction]: """Get a transaction from the transactions table. diff --git a/planetmint/backend/tarantool/init.lua b/planetmint/backend/tarantool/init.lua index 044dce3..1ae8e34 100644 --- a/planetmint/backend/tarantool/init.lua +++ b/planetmint/backend/tarantool/init.lua @@ -48,6 +48,11 @@ function init() { 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', { if_not_exists = true, 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 = box.schema.create_space('outputs', { if_not_exists = true }) diff --git a/planetmint/backend/tarantool/query.py b/planetmint/backend/tarantool/query.py index 2d9a5e1..1c8928b 100644 --- a/planetmint/backend/tarantool/query.py +++ b/planetmint/backend/tarantool/query.py @@ -56,11 +56,21 @@ def get_transaction(connection, tx_id: str) -> DbTransaction: @register_query(TarantoolDBConnection) 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] 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: output_id = uuid4().hex connection.run(connection.space(TARANT_TABLE_OUTPUT).insert(( diff --git a/planetmint/lib.py b/planetmint/lib.py index 195bc84..744ae3b 100644 --- a/planetmint/lib.py +++ b/planetmint/lib.py @@ -470,6 +470,10 @@ class Planetmint(object): """ 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 def fastquery(self): return fastquery.FastQuery(self.connection) diff --git a/planetmint/web/routes.py b/planetmint/web/routes.py index b17a107..2dd9022 100644 --- a/planetmint/web/routes.py +++ b/planetmint/web/routes.py @@ -32,7 +32,7 @@ def r(*args, **kwargs): ROUTES_API_V1 = [ r("/", info.ApiV1Index), r("assets/", assets.AssetListApi), - r("metadata/", metadata.MetadataApi), + r("metadata/", metadata.MetadataApi), r("blocks/", blocks.BlockApi), r("blocks/latest", blocks.LatestBlock), r("blocks/", blocks.BlockListApi), diff --git a/planetmint/web/views/metadata.py b/planetmint/web/views/metadata.py index b6bdeed..bf32155 100644 --- a/planetmint/web/views/metadata.py +++ b/planetmint/web/views/metadata.py @@ -18,33 +18,28 @@ logger = logging.getLogger(__name__) class MetadataApi(Resource): - def get(self): + def get(self, cid): """API endpoint to perform a text search on transaction metadata. Args: - search (str): Text search string to query the text index limit (int, optional): Limit the number of returned documents. Return: A list of metadata that match the query. """ parser = reqparse.RequestParser() - parser.add_argument("search", type=str, required=True) parser.add_argument("limit", type=int) args = parser.parse_args() - if not args["search"]: - return make_error(400, "text_search cannot be empty") if not args["limit"]: del args["limit"] pool = current_app.config["bigchain_pool"] with pool() as planet: - args["table"] = "meta_data" - metadata = planet.text_search(**args) + metadata = planet.get_metadata_by_cid(cid, **args) try: - return list(metadata) + return metadata except OperationError as e: return make_error(400, "({}): {}".format(type(e).__name__, e)) diff --git a/tests/web/test_metadata.py b/tests/web/test_metadata.py index 70cf36d..f65f4b9 100644 --- a/tests/web/test_metadata.py +++ b/tests/web/test_metadata.py @@ -11,22 +11,11 @@ from ipld import marshal, multihash 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 def test_get_metadata_tendermint(client, b, alice): assets = [{"data": multihash(marshal({"msg": "abc"}))}] # 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.status_code == 200 @@ -40,10 +29,10 @@ def test_get_metadata_tendermint(client, b, alice): b.store_bulk_transactions([tx]) # test that metadata is returned - res = client.get(METADATA_ENDPOINT + "?search=" + metadata) + res = client.get(METADATA_ENDPOINT + metadata) assert res.status_code == 200 assert len(res.json) == 1 - assert res.json[0] == {"meta_data": metadata, "id": tx.id} + assert res.json[0] == metadata @pytest.mark.bdb @@ -51,25 +40,24 @@ def test_get_metadata_limit_tendermint(client, b, alice): # create two assets assets1 = [{"data": multihash(marshal({"msg": "abc 1"}))}] - meta1 = multihash(marshal({"key": "meta 1"})) - tx1 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta1, assets=assets1).sign( + meta = multihash(marshal({"key": "meta 1"})) + tx1 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta, assets=assets1).sign( [alice.private_key] ) b.store_bulk_transactions([tx1]) assets2 = [{"data": multihash(marshal({"msg": "abc 2"}))}] - meta2 = multihash(marshal({"key": "meta 2"})) - tx2 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta2, assets=assets2).sign( + tx2 = Create.generate([alice.public_key], [([alice.public_key], 1)], metadata=meta, assets=assets2).sign( [alice.private_key] ) b.store_bulk_transactions([tx2]) # 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 len(res.json) == 1 + assert len(res.json) == 2 # 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 len(res.json) == 1