diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index c87e1a2a555..c17e5ee9a87 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -273,19 +273,18 @@ def insert_data(context, data_dict): context['connection'].execute(sql_string, rows) -def delete_data(context, data_dict): - filters = data_dict.get('filters') +def _where_from_filters(field_ids, data_dict): + 'Return a SQL WHERE clause from data_dict filters' + filters = data_dict.get('filters', {}) if not isinstance(filters, dict): raise p.toolkit.ValidationError({ 'filters': 'Not a json object'} ) - fields = _get_fields(context, data_dict) - field_ids = set([field['id'] for field in fields]) - where_clauses = [] values = [] + for field, value in filters.iteritems(): if field not in field_ids: raise p.toolkit.ValidationError({ @@ -295,17 +294,59 @@ def delete_data(context, data_dict): values.append(value) where_clause = ' and '.join(where_clauses) + return where_clause, values + + +def delete_data(context, data_dict): + fields = _get_fields(context, data_dict) + field_ids = set([field['id'] for field in fields]) + where_clause, where_values = _where_from_filters(field_ids, data_dict) context['connection'].execute( 'delete from "{}" where {}'.format( data_dict['resource_id'], where_clause ), - values + where_values ) def search_data(context, data_dict): + all_fields = _get_fields(context, data_dict) + all_field_ids = set([field['id'] for field in all_fields]) + + fields = data_dict.get('fields') + + if fields: + check_fields(context, fields) + field_ids = set([field['id'] for field in fields]) + + for field in field_ids: + if not field in all_field_ids: + raise p.toolkit.ValidationError({ + 'fields': 'field "{}" not in table'.format(field)} + ) + else: + fields = all_fields + field_ids = all_field_ids + + select_columns = ', '.join(field_ids) + + where_clause, where_values = _where_from_filters(all_field_ids, data_dict) + + sql_string = 'select {} from "{}"'.format( + select_columns, data_dict['resource_id'] + ) + + if where_clause: + sql_string += ' where {}'.format(where_clause) + + results = context['connection'].execute(sql_string, where_values) + + data_dict['total'] = results.rowcount + records = [(dict((f, r[f]) for f in field_ids)) for r in results] + data_dict['records'] = records + return data_dict diff --git a/ckanext/datastore/logic/action.py b/ckanext/datastore/logic/action.py index e75d4e21323..aa856b7a10c 100644 --- a/ckanext/datastore/logic/action.py +++ b/ckanext/datastore/logic/action.py @@ -85,7 +85,7 @@ def datastore_search(context, data_dict): :type offset: int :param fields: ordered list of fields to return (default: all fields in original order) - :type fields: list of strings + :type fields: list of dictionaries :param sort: comma separated field names with ordering eg: "fieldname1, fieldname2 desc" :type sort: string diff --git a/ckanext/datastore/tests/test_datastore.py b/ckanext/datastore/tests/test_datastore.py index 2f56eb89cd6..c49d3f970ea 100644 --- a/ckanext/datastore/tests/test_datastore.py +++ b/ckanext/datastore/tests/test_datastore.py @@ -488,3 +488,44 @@ def test_search_basic(self): extra_environ=auth) res_dict = json.loads(res.body) assert res_dict['success'] is True + result = res_dict['result'] + assert result['total'] == len(self.data['records']) + assert result['records'] == self.data['records'] + + def test_search_invalid_field(self): + data = {'resource_id': self.data['resource_id'], + 'fields': [{'id': 'bad'}]} + postparams = '%s=1' % json.dumps(data) + auth = {'Authorization': str(self.sysadmin_user.apikey)} + res = self.app.post('/api/action/datastore_search', params=postparams, + extra_environ=auth, status=409) + res_dict = json.loads(res.body) + assert res_dict['success'] is False + + def test_search_fields(self): + data = {'resource_id': self.data['resource_id'], + 'fields': [{'id': 'book'}]} + postparams = '%s=1' % json.dumps(data) + auth = {'Authorization': str(self.sysadmin_user.apikey)} + res = self.app.post('/api/action/datastore_search', params=postparams, + extra_environ=auth) + res_dict = json.loads(res.body) + assert res_dict['success'] is True + result = res_dict['result'] + assert result['total'] == len(self.data['records']) + assert result['records'] == [{'book': 'annakarenina'}, + {'book': 'warandpeace'}] + + def test_search_filters(self): + data = {'resource_id': self.data['resource_id'], + 'filters': {'book': 'annakarenina'}} + postparams = '%s=1' % json.dumps(data) + auth = {'Authorization': str(self.sysadmin_user.apikey)} + res = self.app.post('/api/action/datastore_search', params=postparams, + extra_environ=auth) + res_dict = json.loads(res.body) + assert res_dict['success'] is True + result = res_dict['result'] + assert result['total'] == 1 + assert result['records'] == [{'book': 'annakarenina', + 'author': 'tolstoy'}]