diff --git a/CHANGELOG.md b/CHANGELOG.md index f622648a..42c3a696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ml). - [addend] #122 system for verify all documents that it's produced from devicehub - [addend] #127 add one code for every named tag - [addend] #131 add one code for every device +- [bugfix] #138 search device with devicehubId ## [1.0.4-beta] - [addend] #95 adding endpoint for check the hash of one report diff --git a/ereuse_devicehub/migrations/versions/8d34480c82c4_add_code_device_search.py b/ereuse_devicehub/migrations/versions/8d34480c82c4_add_code_device_search.py new file mode 100644 index 00000000..fbc5e3b0 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/8d34480c82c4_add_code_device_search.py @@ -0,0 +1,49 @@ +"""add code device search + +Revision ID: 8d34480c82c4 +Revises: 8cb91ad1cc40 +Create Date: 2021-04-26 12:00:36.635784 + +""" +from alembic import op +from alembic import context +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +from ereuse_devicehub.resources.device.search import DeviceSearch + + +# revision identifiers, used by Alembic. +revision = '8d34480c82c4' +down_revision = '8cb91ad1cc40' +branch_labels = None +depends_on = None + + +def get_inv(): + INV = context.get_x_argument(as_dictionary=True).get('inventory') + if not INV: + raise ValueError("Inventory value is not specified") + return INV + +def upgrade(): + op.add_column('device_search', + sa.Column('devicehub_ids', + postgresql.TSVECTOR(), + nullable=True), + schema=f'{get_inv()}') + + op.create_index('devicehub_ids gist', + 'device_search', + ['devicehub_ids'], + unique=False, + postgresql_using='gist', + schema=f'{get_inv()}') + + # Next of the migration execute: dh inv search + +def downgrade(): + op.drop_index('devicehub_ids gist', + table_name='device_search', + schema=f'{get_inv()}') + op.drop_column('device_search', 'devicehub_ids', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/device/search.py b/ereuse_devicehub/resources/device/search.py index f8786d18..2f87da6a 100644 --- a/ereuse_devicehub/resources/device/search.py +++ b/ereuse_devicehub/resources/device/search.py @@ -26,12 +26,14 @@ class DeviceSearch(db.Model): properties = db.Column(TSVECTOR, nullable=False) tags = db.Column(TSVECTOR) + devicehub_ids = db.Column(TSVECTOR) __table_args__ = ( # todo to add concurrency this should be commited separately # see https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#indexes-with-concurrently db.Index('properties gist', properties, postgresql_using='gist'), db.Index('tags gist', tags, postgresql_using='gist'), + db.Index('devicehub_ids gist', devicehub_ids, postgresql_using='gist'), { 'prefixes': ['UNLOGGED'] # Only for temporal tables, can cause table to empty on turn on @@ -140,10 +142,16 @@ class DeviceSearch(db.Model): ) ).filter(Tag.device_id == device.id).join(Tag.org) + devicehub_ids = session.query( + search.Search.vectorize( + (db.func.string_agg(Device.devicehub_id, ' '), search.Weight.A), + ) + ).filter(Device.devicehub_id == device.devicehub_id) + # Note that commit flushes later # todo see how to get rid of the one_or_none() by embedding those as subqueries # I don't like this but I want the 'on_conflict_on_update' thingie - device_document = dict(properties=properties.one_or_none(), tags=tags.one_or_none()) + device_document = dict(properties=properties.one_or_none(), tags=tags.one_or_none(), devicehub_ids=devicehub_ids.one_or_none()) insert = postgresql.insert(DeviceSearch.__table__) \ .values(device_id=device.id, **device_document) \ .on_conflict_do_update(constraint='device_search_pkey', set_=device_document) diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index 7877000d..8e032fc7 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -54,6 +54,7 @@ class LotQ(query.Query): class Filters(query.Query): id = query.Or(query.Equal(Device.id, fields.Integer())) + devicehub_id = query.Or(query.ILike(Device.devicehub_id)) type = query.Or(OfType(Device.type)) model = query.ILike(Device.model) manufacturer = query.ILike(Device.manufacturer) @@ -154,10 +155,15 @@ class DeviceView(View): if search_p: properties = DeviceSearch.properties tags = DeviceSearch.tags + devicehub_ids = DeviceSearch.devicehub_ids query = query.join(DeviceSearch).filter( - search.Search.match(properties, search_p) | search.Search.match(tags, search_p) + search.Search.match(properties, search_p) | + search.Search.match(tags, search_p) | + search.Search.match(devicehub_ids, search_p) ).order_by( - search.Search.rank(properties, search_p) + search.Search.rank(tags, search_p) + search.Search.rank(properties, search_p) + + search.Search.rank(tags, search_p) + + search.Search.rank(devicehub_ids, search_p) ) return query.filter(*args['filter']).order_by(*args['sort']) diff --git a/tests/test_device_find.py b/tests/test_device_find.py index 12d6c3d8..476ec36d 100644 --- a/tests/test_device_find.py +++ b/tests/test_device_find.py @@ -248,6 +248,8 @@ def test_device_query_search(user: UserClient): assert i['items'][0]['id'] == 1 i, _ = user.get(res=Device, query=[('search', 'intel')]) assert len(i['items']) == 1 + i, _ = user.get(res=Device, query=[('search', i['items'][0]['devicehubID'])]) + assert len(i['items']) == 1 i, _ = user.get(res=Device, query=[('search', '1')]) assert len(i['items']) == 1