Changeset 170
- Timestamp:
- 08/20/08 19:46:43 (3 months ago)
- Files:
-
- trunk/microfacts/modes.py (modified) (9 diffs)
- trunk/microfacts/tests/functional/test_factlet.py (modified) (1 diff)
- trunk/microfacts/tests/functional/test_rest.py (modified) (8 diffs)
- trunk/microfacts/tests/test_modes.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/microfacts/modes.py
r152 r170 50 50 51 51 def search_entities(self): 52 '''Search for entities matching specified criteria. 53 54 Supports a JSON-oriented dictionary based message format: 55 56 { 57 key : value, 58 ... 59 } 60 61 Conditions are ANDed together. Use '%' for like support. For example 62 the following query will select items which have BOTH a title LIKE %2 63 AND a description like %abc. 64 65 { "title" : "%2", description : "%abc" } 66 67 Selecting by collection membership is supported. E.g. to get all 68 threads which contain factlet 1 do: 69 70 { 71 'factlets' : [1], 72 } 73 74 To do a general text search: 75 76 { 77 'q' : 'term' 78 } 79 80 TODO: other query options such as limit (limiting number of 81 results), 'OR's etc. 82 ''' 83 register = self.get_register() 84 kwds = self.request_data 85 if kwds is None or len(kwds) == 0: 52 builder = SearchQueryBuilder(self) 53 query = builder.execute() 54 if query is None: 86 55 return [] 87 kwds = self.convert_unicode_kwds(kwds) 88 query = register.query 89 for k, v in kwds.items(): 90 if k == 'q': 91 query = register.text_search(query, v) 92 elif isinstance(v, list): # an association test 93 # depending on how it was set up join made be on e.g. 94 # factlets or thread_factlets 95 singular = k[:-1] 96 target_object = getattr(model, singular.capitalize()) 97 try: 98 query = query.join(k) 99 except: 100 # its thread_factlets 101 attr_name = self.get_register_name() + '_' + k 102 query = query.join(attr_name) 103 # at present cannot do more than 1 item 104 # want to do: 105 # select thread where thread_2_factlet contains 106 # thread, factlet1 and thread, factlet2 and ... 107 # however with simple join do 108 # select thread.id from thread join thread_2_factlet where 109 # factlet.id = id1 and factlet.id = id2 110 # this obviously always give zero results 111 if len(v) > 1: 112 msg = 'Filtering by more than 2 items is not supported' 113 raise NotImplementedError(msg) 114 for entity_id in v: 115 query = query.filter(target_object.id == entity_id) 116 else: 117 model_attr = getattr(register, k) 118 query = query.filter(model_attr.like(v)) 119 logger.debug(query) 120 return query.all() 56 else: 57 return query.all() 121 58 122 59 def create_entity(self): … … 201 138 # attributes you can query on e.g. 'title', 'description' etc 202 139 tokens_datetime = ['start', 'end'] # Could pull these from the model? 203 tokens = [ 'title', 'description' ] + tokens_datetime140 tokens = [ 'title', 'description', 'factlets'] + tokens_datetime 204 141 operators = ['=', '>', '<', '~'] 205 142 … … 209 146 def execute(self): 210 147 query_spec = self.convert_request_data_to_spec() 148 logger.debug('SearchQueryBuilder: query_spec was: %s' % query_spec) 211 149 return self.run_query(query_spec) 212 150 … … 214 152 spec = [] 215 153 # Get the query string from request data. 216 query_str = self.mode.request_data['q'] 154 if self.mode.request_data: 155 query_str = self.mode.request_data.get('q', '') 156 else: 157 query_str = '' 217 158 # Go through the query string, looking for 'qualifying' tokens. 218 159 # - an example of a qualifying token would be 'title:'. … … 224 165 if ch == ':' and word in self.tokens: 225 166 if current_value: 226 spec_raw[current_token] = current_value 167 spec_raw[current_token] = current_value.strip() 227 168 # reset 228 169 current_token = word … … 235 176 word += ch 236 177 if current_value or word: 237 spec_raw[current_token] = current_value + word 178 value = current_value + word 179 spec_raw[current_token] = value.strip() 238 180 # Go through the raw values of the tokens, looking for 'operators'. 239 181 for (token, value) in spec_raw.items(): … … 290 232 291 233 def run_query(self, query_spec): 292 register = self.mode.register 234 # if the query was empty 235 if not query_spec: 236 return None 237 register = self.mode.get_register() 293 238 query = register.query 294 239 if len(query_spec) == 0: … … 300 245 if k == 'unqualified': 301 246 query = register.text_search(query, v) 302 elif isinstance(v, list):247 elif k == 'factlets': 303 248 # List of factlets, or thread_factlets. 304 249 singular = k[:-1] … … 308 253 except: 309 254 # It's a list of thread_factlets. 310 attr_name = self. get_register_name() + '_' + k255 attr_name = self.mode.get_register_name() + '_' + k 311 256 query = query.join(attr_name) 257 # v is currently a string containing ids 258 v = v.split() 259 v = [ int(id) for id in v ] 312 260 if len(v) > 1: 313 261 msg = 'Filtering by more than 2 items is not supported' trunk/microfacts/tests/functional/test_factlet.py
r124 r170 56 56 57 57 def test_search_2(self): 58 path = url_for(controller='factlet', action='search', title='%Aust%')58 path = url_for(controller='factlet', action='search', q='title: Aust') 59 59 print path 60 60 response = self.app.get(path) 61 print response 62 assert 'Battle of Austerlitz' in response 61 assert 'Battle of Austerlitz' in response, response 63 62 assert 'Battle of Borodino' not in response 64 63 65 path = url_for(controller='factlet', action='search', title='%Battle%')64 path = url_for(controller='factlet', action='search', q='title: Battle') 66 65 response = self.app.get(path) 67 66 assert 'Battle of Austerlitz' in response trunk/microfacts/tests/functional/test_rest.py
r156 r170 166 166 path = url_for(controller='rest', action='search', register='factlet', 167 167 id=None) 168 params = { dumps(query) : '' } 168 # params = { dumps(query) : '' } 169 params = { dumps({'q' : query}) : '' } 169 170 response = self.app.post(path, params=params) 170 171 assert response.body is not None … … 174 175 175 176 def test_search_factlet_title(self): 176 query = { 'title' : 'Factlet 1 Title' }177 factlets = self._run_query(query) 178 assert len(factlets) == 1 177 query = u'title: Factlet 1 Title' 178 factlets = self._run_query(query) 179 assert len(factlets) == 1, factlets 179 180 assert factlets[0].title == 'Factlet 1 Title' 180 181 181 query_2 = { 'title' : 'Fact%' }182 query_2 = u'title: Fact' 182 183 factlets = self._run_query(query_2) 183 184 assert len(factlets) == 3 184 185 assert factlets[0].title.startswith('Fact') 185 186 186 query_3 = { 'title' : '%lots-of-random-jibberish-aafdafjdaf' }187 query_3 = u'title: lots-of-random-jibberish-aafdafjdaf' 187 188 factlets = self._run_query(query_3) 188 189 assert len(factlets) == 0 189 190 190 191 def test_search_factlet_description(self): 191 query_4 = { 'description' : u'%2 Description' }192 query_4 = u'description: 2 Description' 192 193 factlets = self._run_query(query_4) 193 194 assert len(factlets) == 1 … … 197 198 198 199 def test_search_factlet_text_search(self): 199 query = { 'q' : u'Factlet 1 Title' }200 query = u'Factlet 1 Title' 200 201 factlets = self._run_query(query) 201 202 assert len(factlets) == 1 202 203 self.assert_in(self.factlet1, factlets) 203 204 204 query = { 'q' : u'Factlet 1 Description' }205 query = u'Factlet 1 Description' 205 206 factlets = self._run_query(query) 206 207 assert len(factlets) == 1 207 208 self.assert_in(self.factlet1, factlets) 208 209 209 query = { 'q' : u'Title' }210 query = u'Title' 210 211 factlets = self._run_query(query) 211 212 assert len(factlets) == 3 … … 214 215 self.assert_in(self.factlet3, factlets) 215 216 216 query = { 'q' : u'Description' }217 query = u'Description' 217 218 factlets = self._run_query(query) 218 219 assert len(factlets) == 3 … … 223 224 def test_search_factlet_anded(self): 224 225 # two items at once! 225 query_5 = { 'title' : '%1', 'description' : u'%2 description' }226 query_5 = 'title: 1 description: 2 description' 226 227 factlets = self._run_query(query_5) 227 228 assert len(factlets) == 0 … … 229 230 # rgrp, 2008-08-06 disable for time being as >= does not work 230 231 def _test_search_factlet_date(self): 231 query_6 = { 'start' : '>=1900' }232 query_6 = 'start:>1900' 232 233 factlets = self._run_query(query_6) 233 234 assert len(factlets) == 0 … … 236 237 path = url_for(controller='rest', action='search', register='thread', 237 238 id=None) 238 params = { dumps( query) : '' }239 params = { dumps({'q': query}) : '' } 239 240 response = self.app.post(path, params=params) 240 241 assert response.body is not None … … 244 245 245 246 def test_search_thread_1(self): 246 query = { 'title' : 'Thread 1' }247 query = u'title: Thread 1' 247 248 threads = self._run_query_thread(query) 248 249 assert len(threads) == 1 249 250 assert threads[0].title == 'Thread 1' 250 251 251 query_2 = { 'title' : 'Threa%' }252 query_2 = u'title: Threa' 252 253 threads = self._run_query_thread(query_2) 253 254 assert len(threads) == 3 254 255 assert threads[0].title.startswith('Threa') 255 256 256 query_3 = { 'title' : '%lots-of-random-jibberish-aafdafjdaf' }257 query_3 = u'title: lots-of-random-jibberish-aafdafjdaf' 257 258 threads = self._run_query_thread(query_3) 258 259 assert len(threads) == 0 259 260 260 261 def test_search_thread_by_factlets(self): 261 query = { 'factlets' : [ self.ids[0] ] }262 query = u'factlets: %s' % self.ids[0] 262 263 threads = self._run_query_thread(query) 263 264 print self.ids[0] trunk/microfacts/tests/test_modes.py
r134 r170 286 286 287 287 class MockMode(object): 288 pass 288 def get_register(self): 289 return model.Factlet 289 290 mock = MockMode() 290 291 mock.request_data = self.request_data 291 mock.register = model.Factlet292 292 self.builder = SearchQueryBuilder(mock) 293 293 … … 305 305 query = self.builder.execute() 306 306 print query 307 results = query.all() 307 if query is None: 308 results = [] 309 else: 310 results = query.all() 308 311 self.assert_len_results(results) 309 312 … … 325 328 request_data = { 'q' : '' } 326 329 expected_spec = [] 327 len_results = 5 330 len_results = 0 331 332 class TestEmptyQuery(SearchQueryBuilderBase): 333 request_data = { } 334 expected_spec = [] 335 len_results = 0 328 336 329 337 class TestTitleQuery(SearchQueryBuilderBase):
