Changeset 170

Show
Ignore:
Timestamp:
08/20/08 19:46:43 (3 months ago)
Author:
rgrp
Message:

[modes][m]: link in SearchQueryBuilder? work from last week into actual modes search and refactor tests to take account of new (simpler) query string syntax.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/microfacts/modes.py

    r152 r170  
    5050     
    5151    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: 
    8655            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() 
    12158     
    12259    def create_entity(self): 
     
    201138    # attributes you can query on e.g. 'title', 'description' etc 
    202139    tokens_datetime = ['start', 'end']  # Could pull these from the model? 
    203     tokens = [ 'title', 'description'] + tokens_datetime 
     140    tokens = [ 'title', 'description', 'factlets'] + tokens_datetime 
    204141    operators = ['=', '>', '<', '~'] 
    205142 
     
    209146    def execute(self): 
    210147        query_spec = self.convert_request_data_to_spec() 
     148        logger.debug('SearchQueryBuilder: query_spec was: %s' % query_spec) 
    211149        return self.run_query(query_spec) 
    212150 
     
    214152        spec = [] 
    215153        # 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 = '' 
    217158        # Go through the query string, looking for 'qualifying' tokens. 
    218159        #     - an example of a qualifying token would be 'title:'. 
     
    224165            if ch == ':' and word in self.tokens: 
    225166                if current_value: 
    226                     spec_raw[current_token] = current_value 
     167                    spec_raw[current_token] = current_value.strip() 
    227168                # reset 
    228169                current_token = word 
     
    235176                word += ch 
    236177        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() 
    238180        # Go through the raw values of the tokens, looking for 'operators'. 
    239181        for (token, value) in spec_raw.items(): 
     
    290232     
    291233    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() 
    293238        query = register.query 
    294239        if len(query_spec) == 0: 
     
    300245            if k == 'unqualified': 
    301246                query = register.text_search(query, v) 
    302             elif isinstance(v, list)
     247            elif k == 'factlets'
    303248                # List of factlets, or thread_factlets. 
    304249                singular = k[:-1] 
     
    308253                except: 
    309254                    # It's a list of thread_factlets. 
    310                     attr_name = self.get_register_name() + '_' + k 
     255                    attr_name = self.mode.get_register_name() + '_' + k 
    311256                    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 ] 
    312260                if len(v) > 1: 
    313261                    msg = 'Filtering by more than 2 items is not supported' 
  • trunk/microfacts/tests/functional/test_factlet.py

    r124 r170  
    5656         
    5757    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') 
    5959        print path 
    6060        response = self.app.get(path) 
    61         print response 
    62         assert 'Battle of Austerlitz' in response 
     61        assert 'Battle of Austerlitz' in response, response 
    6362        assert 'Battle of Borodino' not in response 
    6463 
    65         path = url_for(controller='factlet', action='search', title='%Battle%') 
     64        path = url_for(controller='factlet', action='search', q='title: Battle') 
    6665        response = self.app.get(path) 
    6766        assert 'Battle of Austerlitz' in response 
  • trunk/microfacts/tests/functional/test_rest.py

    r156 r170  
    166166        path = url_for(controller='rest', action='search', register='factlet', 
    167167                id=None) 
    168         params = { dumps(query) : '' } 
     168        # params = { dumps(query) : '' } 
     169        params = { dumps({'q' : query}) : '' } 
    169170        response = self.app.post(path, params=params) 
    170171        assert response.body is not None 
     
    174175 
    175176    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 
    179180        assert factlets[0].title == 'Factlet 1 Title' 
    180181 
    181         query_2 = { 'title' : 'Fact%' } 
     182        query_2 = u'title: Fact' 
    182183        factlets = self._run_query(query_2) 
    183184        assert len(factlets) == 3 
    184185        assert factlets[0].title.startswith('Fact') 
    185186 
    186         query_3 = { 'title' : '%lots-of-random-jibberish-aafdafjdaf' } 
     187        query_3 = u'title: lots-of-random-jibberish-aafdafjdaf' 
    187188        factlets = self._run_query(query_3) 
    188189        assert len(factlets) == 0 
    189190 
    190191    def test_search_factlet_description(self): 
    191         query_4 = { 'description' : u'%2 Description' } 
     192        query_4 = u'description: 2 Description' 
    192193        factlets = self._run_query(query_4) 
    193194        assert len(factlets) == 1 
     
    197198 
    198199    def test_search_factlet_text_search(self): 
    199         query = { 'q' : u'Factlet 1 Title' } 
     200        query = u'Factlet 1 Title' 
    200201        factlets = self._run_query(query) 
    201202        assert len(factlets) == 1 
    202203        self.assert_in(self.factlet1, factlets) 
    203204         
    204         query = { 'q' : u'Factlet 1 Description' } 
     205        query = u'Factlet 1 Description' 
    205206        factlets = self._run_query(query) 
    206207        assert len(factlets) == 1 
    207208        self.assert_in(self.factlet1, factlets) 
    208209         
    209         query = { 'q' : u'Title' } 
     210        query = u'Title' 
    210211        factlets = self._run_query(query) 
    211212        assert len(factlets) == 3 
     
    214215        self.assert_in(self.factlet3, factlets) 
    215216         
    216         query = { 'q' : u'Description' } 
     217        query = u'Description' 
    217218        factlets = self._run_query(query) 
    218219        assert len(factlets) == 3 
     
    223224    def test_search_factlet_anded(self): 
    224225        # two items at once! 
    225         query_5 = { 'title' : '%1', 'description' : u'%2 description' } 
     226        query_5 = 'title: 1 description: 2 description' 
    226227        factlets = self._run_query(query_5) 
    227228        assert len(factlets) == 0 
     
    229230    # rgrp, 2008-08-06 disable for time being as >= does not work 
    230231    def _test_search_factlet_date(self): 
    231         query_6 = { 'start' : '>=1900' } 
     232        query_6 = 'start:>1900' 
    232233        factlets = self._run_query(query_6) 
    233234        assert len(factlets) == 0 
     
    236237        path = url_for(controller='rest', action='search', register='thread', 
    237238                id=None) 
    238         params = { dumps(query) : '' } 
     239        params = { dumps({'q': query}) : '' } 
    239240        response = self.app.post(path, params=params) 
    240241        assert response.body is not None 
     
    244245 
    245246    def test_search_thread_1(self): 
    246         query = { 'title' : 'Thread 1' } 
     247        query = u'title: Thread 1' 
    247248        threads = self._run_query_thread(query) 
    248249        assert len(threads) == 1 
    249250        assert threads[0].title == 'Thread 1' 
    250251 
    251         query_2 = { 'title' : 'Threa%' } 
     252        query_2 = u'title: Threa' 
    252253        threads = self._run_query_thread(query_2) 
    253254        assert len(threads) == 3 
    254255        assert threads[0].title.startswith('Threa') 
    255256 
    256         query_3 = { 'title' : '%lots-of-random-jibberish-aafdafjdaf' } 
     257        query_3 = u'title: lots-of-random-jibberish-aafdafjdaf' 
    257258        threads = self._run_query_thread(query_3) 
    258259        assert len(threads) == 0 
    259260 
    260261    def test_search_thread_by_factlets(self): 
    261         query = { 'factlets' : [ self.ids[0] ]  } 
     262        query = u'factlets: %s' % self.ids[0] 
    262263        threads = self._run_query_thread(query) 
    263264        print self.ids[0] 
  • trunk/microfacts/tests/test_modes.py

    r134 r170  
    286286 
    287287        class MockMode(object): 
    288             pass 
     288            def get_register(self): 
     289                return model.Factlet 
    289290        mock = MockMode() 
    290291        mock.request_data = self.request_data 
    291         mock.register = model.Factlet 
    292292        self.builder = SearchQueryBuilder(mock) 
    293293 
     
    305305        query = self.builder.execute() 
    306306        print query 
    307         results = query.all() 
     307        if query is None: 
     308            results = [] 
     309        else: 
     310            results = query.all() 
    308311        self.assert_len_results(results) 
    309312 
     
    325328    request_data = { 'q' : '' } 
    326329    expected_spec = [] 
    327     len_results = 5 
     330    len_results = 0 
     331 
     332class TestEmptyQuery(SearchQueryBuilderBase): 
     333    request_data = { } 
     334    expected_spec = [] 
     335    len_results = 0 
    328336 
    329337class TestTitleQuery(SearchQueryBuilderBase):