/
global_search.py
225 lines (180 loc) · 7.58 KB
/
global_search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from .client import client
from .resource import Object
from .resource import Vault
from .query import QueryBase
from .query import Query
from .query import Filter
import logging
logger = logging.getLogger('solvebio')
class GlobalSearch(Query):
"""
GlobalSearch acts as a request wrapper that generates a request from Filter objects,
and can iterate through streaming result sets.
"""
def __init__(
self,
query=None,
filters=None,
entities=None,
entities_match='any',
vault_scope='all',
ordering=None,
limit=float('inf'),
page_size=QueryBase.DEFAULT_PAGE_SIZE,
result_class=dict,
debug=False,
raw_results=False,
**kwargs):
"""
Creates a new Query object.
:Parameters:
- `query` (optional): An optional query string (advanced search).
- `filters` (optional): Filter or List of filter objects.
- `entities` (optional): List of entity tuples to filter on (entity type, entity).
- `entities_match` (optional): Can be 'all' or 'any' (match any provided entity).
- `vault_scope` (optional): Can be 'all' or 'access'.
- `ordering` (optional): List of fields to order the results by.
- `limit` (optional): Maximum number of query results to return.
- `page_size` (optional): Number of results to fetch per query page.
- `result_class` (optional): Class of object returned by query.
- `debug` (optional): Sends debug information to the API.
- `raw_results` (optional): Whether to use raw API response or to cast logical
objects to Vault and Object instances.
"""
super(GlobalSearch, self).__init__(None)
self._data_url = '/v2/search'
self._query = query
self._entities = entities
self._entities_match = entities_match
self._vault_scope = vault_scope
self._ordering = ordering
self._result_class = result_class
self._debug = debug
self._raw_results = raw_results
self._error = None
if filters:
if isinstance(filters, Filter):
filters = [filters]
else:
filters = []
self._filters = filters
# init response and cursor
self._response = None
# Limit defines the total number of results that will be returned
# from a query involving 1 or more pagination requests.
self._limit = limit
# Page size/offset are the low level API limit and offset params.
self._page_size = int(page_size)
# Page offset can only be set by execute(). It is always set to the
# current absolute offset contained in the buffer.
self._page_offset = None
# slice is set when the Query is being sliced.
# In this case, __iter__() and next() will not
# reset the page_offset to 0 before iterating.
self._slice = None
# parameter error checking
if self._limit < 0:
raise Exception('\'limit\' parameter must be >= 0')
if not 0 < self._page_size <= self.MAX_PAGE_SIZE:
raise Exception('\'page_size\' parameter must be in '
'range [1, {}]'.format(self.MAX_PAGE_SIZE))
# Set up the SolveClient
# (kwargs overrides pre-set, which overrides global)
self._client = kwargs.get('client') or self._client or client
def _clone(self, filters=None, entities=None, limit=None):
new = self.__class__(query=self._query,
limit=self._limit,
entities=self._entities,
ordering=self._ordering,
page_size=self._page_size,
result_class=self._result_class,
vault_scope=self._vault_scope,
entities_match=self._entities_match,
debug=self._debug,
client=self._client)
new._filters += self._filters
if entities:
new._entities = entities
if filters:
new._filters += filters
if limit is not None:
new._limit = limit
return new
def __len__(self):
"""
Returns the total number of results returned in a query. It is the
number of items you can iterate over.
In contrast to count(), the result does take into account any limit
given. In SQL it is like:
SELECT COUNT(*) FROM (
SELECT * FROM <table> [WHERE condition] [LIMIT number]
)
"""
return super(GlobalSearch, self).__len__()
def _build_query(self, **kwargs):
q = {}
if self._query:
q['query'] = self._query
if self._filters:
filters = self._process_filters(self._filters)
if len(filters) > 1:
q['filters'] = [{'and': filters}]
else:
q['filters'] = filters
if self._entities is not None:
q['entities'] = self._entities
if self._ordering is not None:
q['ordering'] = self._ordering
if self._vault_scope is not None:
q['vault_scope'] = self._vault_scope
if self._entities_match is not None:
q['entities_match'] = self._entities_match
if self._debug:
q['debug'] = 'True'
# Add or modify query parameters
# (used by BatchQuery and facets)
q.update(**kwargs)
return q
def execute(self, offset=0, **query):
def _process_result(result):
# Internally the client uses object_type, not type
result['object_type'] = result['type']
if result['object_type'] == 'vault':
return Vault.construct_from(result)
else:
return Object.construct_from(result)
# Call superclass method execute
super(GlobalSearch, self).execute(offset, **query)
# Cast logical objects from response to Object/Vault instances
if not self._raw_results:
self._response['results'] = [_process_result(i) for i in self._response['results']]
def entity(self, **kwargs):
"""
Returns GlobalSearch instance with the query args combined with
existing set with AND.
kwargs can contain only one entity, entity_type as parameter name and entity as its value.
If entity is already set for the GlobalSearch, it will be overridden.
"""
if not kwargs:
raise AttributeError('Faceting requires at least one field')
return self._clone(entities=list(kwargs.items()))
def subjects(self):
"""Returns the list of subjects"""
# Executes a query to get a full API response which contains subjects list
gs = self.limit(0)
gs.execute(include_subjects=True)
return gs._response.get('subjects')
def subjects_count(self):
"""Returns the number of subjects"""
# Executes a query to get a full API response which contains subjects list
gs = self.limit(0)
gs.execute()
return gs._response.get('subjects_count')
def vaults(self):
"""Returns the list of vaults"""
# Executes a query to get a full API response which contains vaults list
gs = self.limit(0)
gs.execute(include_vaults=True)
return gs._response.get('vaults')