mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Change metadata model, fix tests and update docs
This commit is contained in:
parent
832ecb5e63
commit
4eca26782c
@ -366,10 +366,7 @@ def get_new_blocks_feed(conn, start_block_id):
|
|||||||
|
|
||||||
@register_query(MongoDBConnection)
|
@register_query(MongoDBConnection)
|
||||||
def text_search(conn, search, *, language='english', case_sensitive=False,
|
def text_search(conn, search, *, language='english', case_sensitive=False,
|
||||||
diacritic_sensitive=False, text_score=False, limit=0, table=None):
|
diacritic_sensitive=False, text_score=False, limit=0, table='assets'):
|
||||||
if table is None:
|
|
||||||
table = 'assets'
|
|
||||||
|
|
||||||
cursor = conn.run(
|
cursor = conn.run(
|
||||||
conn.collection(table)
|
conn.collection(table)
|
||||||
.find({'$text': {
|
.find({'$text': {
|
||||||
|
|||||||
@ -384,8 +384,8 @@ class Bigchain(object):
|
|||||||
for transaction in transactions:
|
for transaction in transactions:
|
||||||
# ignore transactions in invalid blocks
|
# ignore transactions in invalid blocks
|
||||||
# FIXME: Isn't there a faster solution than doing I/O again?
|
# FIXME: Isn't there a faster solution than doing I/O again?
|
||||||
_, status = self.get_transaction(transaction['id'],
|
txn, status = self.get_transaction(transaction['id'],
|
||||||
include_status=True)
|
include_status=True)
|
||||||
if status == self.TX_VALID:
|
if status == self.TX_VALID:
|
||||||
num_valid_transactions += 1
|
num_valid_transactions += 1
|
||||||
# `txid` can only have been spent in at most on valid block.
|
# `txid` can only have been spent in at most on valid block.
|
||||||
@ -395,13 +395,7 @@ class Bigchain(object):
|
|||||||
' with the chain'.format(txid))
|
' with the chain'.format(txid))
|
||||||
# if its not and invalid transaction
|
# if its not and invalid transaction
|
||||||
if status is not None:
|
if status is not None:
|
||||||
if 'metadata' not in transaction:
|
transaction.update({'metadata': txn.metadata})
|
||||||
metadata = list(self.get_metadata([transaction['id']]))
|
|
||||||
metadata = metadata[0] if metadata else None
|
|
||||||
if metadata:
|
|
||||||
metadata.pop('id', None)
|
|
||||||
transaction.update({'metadata': metadata})
|
|
||||||
|
|
||||||
non_invalid_transactions.append(transaction)
|
non_invalid_transactions.append(transaction)
|
||||||
|
|
||||||
if non_invalid_transactions:
|
if non_invalid_transactions:
|
||||||
@ -672,7 +666,7 @@ class Bigchain(object):
|
|||||||
"""
|
"""
|
||||||
return backend.query.write_metadata(self.connection, metadata)
|
return backend.query.write_metadata(self.connection, metadata)
|
||||||
|
|
||||||
def text_search(self, search, *, limit=0, table=None):
|
def text_search(self, search, *, limit=0, table='assets'):
|
||||||
"""
|
"""
|
||||||
Return an iterator of assets that match the text search
|
Return an iterator of assets that match the text search
|
||||||
|
|
||||||
@ -683,9 +677,6 @@ class Bigchain(object):
|
|||||||
Returns:
|
Returns:
|
||||||
iter: An iterator of assets that match the text search.
|
iter: An iterator of assets that match the text search.
|
||||||
"""
|
"""
|
||||||
if table is None:
|
|
||||||
table = 'assets'
|
|
||||||
|
|
||||||
objects = backend.query.text_search(self.connection, search, limit=limit,
|
objects = backend.query.text_search(self.connection, search, limit=limit,
|
||||||
table=table)
|
table=table)
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,7 @@ from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
|
|||||||
DoubleSpend, InputDoesNotExist,
|
DoubleSpend, InputDoesNotExist,
|
||||||
TransactionNotInValidBlock,
|
TransactionNotInValidBlock,
|
||||||
AssetIdMismatch, AmountError,
|
AssetIdMismatch, AmountError,
|
||||||
SybilError, ValidationError,
|
SybilError, DuplicateTransaction)
|
||||||
DuplicateTransaction)
|
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction
|
||||||
from bigchaindb.common.utils import (gen_timestamp, serialize,
|
from bigchaindb.common.utils import (gen_timestamp, serialize,
|
||||||
validate_txn_obj, validate_key)
|
validate_txn_obj, validate_key)
|
||||||
@ -119,7 +118,7 @@ class Transaction(Transaction):
|
|||||||
if 'metadata' not in tx_dict:
|
if 'metadata' not in tx_dict:
|
||||||
metadata = metadata[0] if metadata else None
|
metadata = metadata[0] if metadata else None
|
||||||
if metadata:
|
if metadata:
|
||||||
metadata.pop('id', None)
|
metadata = metadata.get('metadata')
|
||||||
|
|
||||||
tx_dict.update({'metadata': metadata})
|
tx_dict.update({'metadata': metadata})
|
||||||
|
|
||||||
@ -406,11 +405,10 @@ class Block(object):
|
|||||||
metadatas = []
|
metadatas = []
|
||||||
for transaction in block_dict['block']['transactions']:
|
for transaction in block_dict['block']['transactions']:
|
||||||
metadata = transaction.pop('metadata')
|
metadata = transaction.pop('metadata')
|
||||||
if isinstance(metadata, dict):
|
if metadata:
|
||||||
metadata.update({'id': transaction['id']})
|
metadata_new = {'id': transaction['id'],
|
||||||
metadatas.append(metadata)
|
'metadata': metadata}
|
||||||
elif metadata:
|
metadatas.append(metadata_new)
|
||||||
raise ValidationError('Invalid value for metadata')
|
|
||||||
|
|
||||||
return (metadatas, block_dict)
|
return (metadatas, block_dict)
|
||||||
|
|
||||||
@ -460,16 +458,11 @@ class Block(object):
|
|||||||
dict: The dict of the reconstructed block.
|
dict: The dict of the reconstructed block.
|
||||||
"""
|
"""
|
||||||
# create a dict with {'<txid>': metadata}
|
# create a dict with {'<txid>': metadata}
|
||||||
metadatal = {m.pop('id'): m for m in metadatal}
|
metadatal = {m.pop('id'): m.pop('metadata') for m in metadatal}
|
||||||
# add the metadata to their corresponding transactions
|
# add the metadata to their corresponding transactions
|
||||||
for transaction in block_dict['block']['transactions']:
|
for transaction in block_dict['block']['transactions']:
|
||||||
if 'metadata' not in transaction:
|
metadata = metadatal.get(transaction['id'], None)
|
||||||
metadata = metadatal.get(transaction['id'])
|
transaction.update({'metadata': metadata})
|
||||||
if metadata:
|
|
||||||
metadata.pop('id', None)
|
|
||||||
transaction.update({'metadata': metadata})
|
|
||||||
else:
|
|
||||||
transaction.update({'metadata': None})
|
|
||||||
return block_dict
|
return block_dict
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -452,6 +452,118 @@ Assets
|
|||||||
text search.
|
text search.
|
||||||
|
|
||||||
|
|
||||||
|
Transaction Metadata
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/metadata
|
||||||
|
|
||||||
|
Return all the metadata that match a given text search.
|
||||||
|
|
||||||
|
:query string text search: Text search string to query.
|
||||||
|
:query int limit: (Optional) Limit the number of returned metadata objects. Defaults
|
||||||
|
to ``0`` meaning return all matching objects.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Currently this enpoint is only supported if the server is running
|
||||||
|
MongoDB as the backend.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/metadata/?search={text_search}
|
||||||
|
|
||||||
|
Return all metadata that match a given text search. The ``id`` of the metadata
|
||||||
|
is the same ``id`` of the transaction where it was defined.
|
||||||
|
|
||||||
|
If no metadata match the text search it returns an empty list.
|
||||||
|
|
||||||
|
If the text string is empty or the server does not support text search,
|
||||||
|
a ``400`` is returned.
|
||||||
|
|
||||||
|
The results are sorted by text score.
|
||||||
|
For more information about the behavior of text search see `MongoDB text
|
||||||
|
search behavior <https://docs.mongodb.com/manual/reference/operator/query/text/#behavior>`_
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/metadata/?search=bigchaindb HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-type: application/json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"metadata": {"metakey1": "Hello BigchainDB 1!"},
|
||||||
|
"id": "51ce82a14ca274d43e4992bbce41f6fdeb755f846e48e710a3bbb3b0cf8e4204"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {"metakey2": "Hello BigchainDB 2!"},
|
||||||
|
"id": "b4e9005fa494d20e503d916fa87b74fe61c079afccd6e084260674159795ee31"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {"metakey3": "Hello BigchainDB 3!"},
|
||||||
|
"id": "fa6bcb6a8fdea3dc2a860fcdc0e0c63c9cf5b25da8b02a4db4fb6a2d36d27791"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
:resheader Content-Type: ``application/json``
|
||||||
|
|
||||||
|
:statuscode 200: The query was executed successfully.
|
||||||
|
:statuscode 400: The query was not executed successfully. Returned if the
|
||||||
|
text string is empty or the server does not support
|
||||||
|
text search.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/metadata/?search={text_search}&limit={n_documents}
|
||||||
|
|
||||||
|
Return at most ``n`` metadata objects that match a given text search.
|
||||||
|
|
||||||
|
If no metadata match the text search it returns an empty list.
|
||||||
|
|
||||||
|
If the text string is empty or the server does not support text search,
|
||||||
|
a ``400`` is returned.
|
||||||
|
|
||||||
|
The results are sorted by text score.
|
||||||
|
For more information about the behavior of text search see `MongoDB text
|
||||||
|
search behavior <https://docs.mongodb.com/manual/reference/operator/query/text/#behavior>`_
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/metadata/?search=bigchaindb&limit=2 HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-type: application/json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"metadata": {"msg": "Hello BigchainDB 1!"},
|
||||||
|
"id": "51ce82a14ca274d43e4992bbce41f6fdeb755f846e48e710a3bbb3b0cf8e4204"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {"msg": "Hello BigchainDB 2!"},
|
||||||
|
"id": "b4e9005fa494d20e503d916fa87b74fe61c079afccd6e084260674159795ee31"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
:resheader Content-Type: ``application/json``
|
||||||
|
|
||||||
|
:statuscode 200: The query was executed successfully.
|
||||||
|
:statuscode 400: The query was not executed successfully. Returned if the
|
||||||
|
text string is empty or the server does not support
|
||||||
|
text search.
|
||||||
|
|
||||||
|
|
||||||
Advanced Usage
|
Advanced Usage
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,16 @@ def decouple_assets(b, block):
|
|||||||
return block_dict
|
return block_dict
|
||||||
|
|
||||||
|
|
||||||
|
def decouple_metadata(b, block, block_dict):
|
||||||
|
# the block comming from the database does not contain the metadata
|
||||||
|
# so we need to pass the block without the metadata and store the metadata
|
||||||
|
# so that the voting pipeline can reconstruct it
|
||||||
|
metadata, block_dict = block.decouple_metadata(block_dict)
|
||||||
|
if metadata:
|
||||||
|
b.write_metadata(metadata)
|
||||||
|
return block_dict
|
||||||
|
|
||||||
|
|
||||||
DUMMY_SHA3 = '0123456789abcdef' * 4
|
DUMMY_SHA3 = '0123456789abcdef' * 4
|
||||||
|
|
||||||
|
|
||||||
@ -89,6 +99,7 @@ def test_vote_validate_block(b):
|
|||||||
tx = dummy_tx(b)
|
tx = dummy_tx(b)
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
block_dict = decouple_assets(b, block)
|
block_dict = decouple_assets(b, block)
|
||||||
|
block_dict = decouple_metadata(b, block, block_dict)
|
||||||
|
|
||||||
vote_obj = vote.Vote()
|
vote_obj = vote.Vote()
|
||||||
validation = vote_obj.validate_block(block_dict)
|
validation = vote_obj.validate_block(block_dict)
|
||||||
@ -230,6 +241,7 @@ def test_valid_block_voting_multiprocessing(b, genesis_block, monkeypatch):
|
|||||||
|
|
||||||
block = dummy_block(b)
|
block = dummy_block(b)
|
||||||
block_dict = decouple_assets(b, block)
|
block_dict = decouple_assets(b, block)
|
||||||
|
block_dict = decouple_metadata(b, block, block_dict)
|
||||||
|
|
||||||
inpipe.put(block_dict)
|
inpipe.put(block_dict)
|
||||||
vote_pipeline.start()
|
vote_pipeline.start()
|
||||||
@ -268,6 +280,7 @@ def test_valid_block_voting_with_create_transaction(b,
|
|||||||
monkeypatch.setattr('time.time', lambda: 1111111111)
|
monkeypatch.setattr('time.time', lambda: 1111111111)
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
block_dict = decouple_assets(b, block)
|
block_dict = decouple_assets(b, block)
|
||||||
|
block_dict = decouple_metadata(b, block, block_dict)
|
||||||
|
|
||||||
inpipe = Pipe()
|
inpipe = Pipe()
|
||||||
outpipe = Pipe()
|
outpipe = Pipe()
|
||||||
|
|||||||
@ -43,7 +43,7 @@ def test_get_metadata(client, b):
|
|||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
assert len(res.json) == 1
|
assert len(res.json) == 1
|
||||||
assert res.json[0] == {
|
assert res.json[0] == {
|
||||||
'key': 'my_meta',
|
'metadata': {'key': 'my_meta'},
|
||||||
'id': tx.id
|
'id': tx.id
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user