Integrate output search api

This commit is contained in:
kansi 2017-11-30 23:59:42 +05:30
parent 1e104ad2c4
commit 2b3a9fa6f6
7 changed files with 122 additions and 11 deletions

View File

@ -111,3 +111,27 @@ def get_txids_filtered(conn, asset_id, operation=None):
@register_query(LocalMongoDBConnection)
def text_search(*args, **kwargs):
return mongodb.query.text_search(*args, **kwargs)
@register_query(LocalMongoDBConnection)
def get_owned_ids(conn, owner):
cursor = conn.run(
conn.collection('transactions').aggregate([
{'$match': {'outputs.public_keys': owner}},
{'$project': {'_id': False}}
]))
return cursor
@register_query(LocalMongoDBConnection)
def get_spending_transactions(conn, inputs):
cursor = conn.run(
conn.collection('transactions').aggregate([
{'$match': {
'inputs.fulfills': {
'$in': inputs,
},
}},
{'$project': {'_id': False}}
]))
return cursor

View File

@ -0,0 +1,48 @@
from bigchaindb.utils import condition_details_has_owner
from bigchaindb.backend import query
from bigchaindb.common.transaction import TransactionLink
class FastQuery():
"""
Database queries that join on block results from a single node.
"""
def get_outputs_by_public_key(self, public_key):
"""
Get outputs for a public key
"""
txs = list(query.get_owned_ids(self.connection, public_key))
return [TransactionLink(tx['id'], index)
for tx in txs
for index, output in enumerate(tx['outputs'])
if condition_details_has_owner(output['condition']['details'],
public_key)]
def filter_spent_outputs(self, outputs):
"""
Remove outputs that have been spent
Args:
outputs: list of TransactionLink
"""
links = [o.to_dict() for o in outputs]
txs = list(query.get_spending_transactions(self.connection, links))
spends = {TransactionLink.from_dict(input_['fulfills'])
for tx in txs
for input_ in tx['inputs']}
return [ff for ff in outputs if ff not in spends]
def filter_unspent_outputs(self, outputs):
"""
Remove outputs that have not been spent
Args:
outputs: list of TransactionLink
"""
links = [o.to_dict() for o in outputs]
txs = list(query.get_spending_transactions(self.connection, links))
spends = {TransactionLink.from_dict(input_['fulfills'])
for tx in txs
for input_ in tx['inputs']}
return [ff for ff in outputs if ff in spends]

View File

@ -11,6 +11,7 @@ from bigchaindb import Bigchain
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import SchemaValidationError, ValidationError
from bigchaindb.tendermint.utils import encode_transaction
from bigchaindb.tendermint import fastquery
logger = logging.getLogger(__name__)
@ -117,5 +118,9 @@ class BigchainDB(Bigchain):
return False
return transaction
@property
def fastquery(self):
return fastquery.FastQuery(self.connection, self.me)
Block = namedtuple('Block', ('app_hash', 'height'))

View File

@ -3,10 +3,9 @@ from copy import deepcopy
import pytest
import pymongo
pytestmark = [pytest.mark.tendermint, pytest.mark.localmongodb]
pytestmark = [pytest.mark.tendermint, pytest.mark.localmongodb, pytest.mark.bdb]
@pytest.mark.bdb
def test_get_txids_filtered(signed_create_tx, signed_transfer_tx):
from bigchaindb.backend import connect, query
from bigchaindb.models import Transaction
@ -32,7 +31,6 @@ def test_get_txids_filtered(signed_create_tx, signed_transfer_tx):
assert txids == {signed_transfer_tx.id}
@pytest.mark.bdb
def test_write_assets():
from bigchaindb.backend import connect, query
conn = connect()
@ -57,7 +55,6 @@ def test_write_assets():
assert list(cursor) == assets[:-1]
@pytest.mark.bdb
def test_get_assets():
from bigchaindb.backend import connect, query
conn = connect()
@ -74,8 +71,40 @@ def test_get_assets():
assert query.get_asset(conn, asset['id'])
@pytest.mark.bdb
def test_text_search():
from ..mongodb.test_queries import test_text_search
test_text_search('assets')
def test_get_owned_ids(signed_create_tx, user_pk):
from bigchaindb.backend import connect, query
conn = connect()
# insert a transaction
conn.db.transactions.insert_one(signed_create_tx.to_dict())
txns = list(query.get_owned_ids(conn, user_pk))
assert txns[0] == signed_create_tx.to_dict()
def test_get_spending_transactions(user_pk):
from bigchaindb.backend import connect, query
from bigchaindb.models import Transaction
conn = connect()
out = [([user_pk], 1)]
tx1 = Transaction.create([user_pk], out * 3)
inputs = tx1.to_inputs()
tx2 = Transaction.transfer([inputs[0]], out, tx1.id)
tx3 = Transaction.transfer([inputs[1]], out, tx1.id)
tx4 = Transaction.transfer([inputs[2]], out, tx1.id)
txns = [tx.to_dict() for tx in [tx1, tx2, tx3, tx4]]
conn.db.transactions.insert_many(txns)
links = [inputs[0].fulfills.to_dict(), inputs[2].fulfills.to_dict()]
txns = list(query.get_spending_transactions(conn, links))
# tx3 not a member because input 1 not asked for
assert txns == [tx2.to_dict(), tx4.to_dict()]

View File

@ -92,7 +92,7 @@ def test_deliver_transfer_tx__double_spend_fails(b):
carly = generate_key_pair()
asset = {
"msg": "live long and prosper"
'msg': 'live long and prosper'
}
tx = Transaction.create([alice.public_key],

View File

@ -3,11 +3,10 @@ import pytest
@pytest.fixture
def app(request):
from bigchaindb import config
from bigchaindb.web import server
from bigchaindb.tendermint.lib import BigchainDB
if config['database']['backend'] == 'localmongodb':
if request.config.getoption('--database-backend') == 'localmongodb':
app = server.create_app(debug=True, bigchaindb_factory=BigchainDB)
else:
app = server.create_app(debug=True)

View File

@ -6,11 +6,12 @@ pytestmark = [pytest.mark.bdb, pytest.mark.usefixtures('inputs')]
OUTPUTS_ENDPOINT = '/api/v1/outputs/'
@pytest.mark.tendermint
def test_get_outputs_endpoint(client, user_pk):
m = MagicMock()
m.txid = 'a'
m.output = 0
with patch('bigchaindb.core.Bigchain.get_outputs_filtered') as gof:
with patch('bigchaindb.tendermint.lib.BigchainDB.get_outputs_filtered') as gof:
gof.return_value = [m, m]
res = client.get(OUTPUTS_ENDPOINT + '?public_key={}'.format(user_pk))
assert res.json == [
@ -21,11 +22,12 @@ def test_get_outputs_endpoint(client, user_pk):
gof.assert_called_once_with(user_pk, None)
@pytest.mark.tendermint
def test_get_outputs_endpoint_unspent(client, user_pk):
m = MagicMock()
m.txid = 'a'
m.output = 0
with patch('bigchaindb.core.Bigchain.get_outputs_filtered') as gof:
with patch('bigchaindb.tendermint.lib.BigchainDB.get_outputs_filtered') as gof:
gof.return_value = [m]
params = '?spent=False&public_key={}'.format(user_pk)
res = client.get(OUTPUTS_ENDPOINT + params)
@ -34,11 +36,12 @@ def test_get_outputs_endpoint_unspent(client, user_pk):
gof.assert_called_once_with(user_pk, False)
@pytest.mark.tendermint
def test_get_outputs_endpoint_spent(client, user_pk):
m = MagicMock()
m.txid = 'a'
m.output = 0
with patch('bigchaindb.core.Bigchain.get_outputs_filtered') as gof:
with patch('bigchaindb.tendermint.lib.BigchainDB.get_outputs_filtered') as gof:
gof.return_value = [m]
params = '?spent=true&public_key={}'.format(user_pk)
res = client.get(OUTPUTS_ENDPOINT + params)
@ -47,11 +50,13 @@ def test_get_outputs_endpoint_spent(client, user_pk):
gof.assert_called_once_with(user_pk, True)
@pytest.mark.tendermint
def test_get_outputs_endpoint_without_public_key(client):
res = client.get(OUTPUTS_ENDPOINT)
assert res.status_code == 400
@pytest.mark.tendermint
def test_get_outputs_endpoint_with_invalid_public_key(client):
expected = {'message': {'public_key': 'Invalid base58 ed25519 key'}}
res = client.get(OUTPUTS_ENDPOINT + '?public_key=abc')
@ -59,6 +64,7 @@ def test_get_outputs_endpoint_with_invalid_public_key(client):
assert res.status_code == 400
@pytest.mark.tendermint
def test_get_outputs_endpoint_with_invalid_spent(client, user_pk):
expected = {'message': {'spent': 'Boolean value must be "true" or "false" (lowercase)'}}
params = '?spent=tru&public_key={}'.format(user_pk)