~jcsackett/charmworld/bac-tag-constraints

« back to all changes in this revision

Viewing changes to charmworld/search.py

  • Committer: Tarmac
  • Author(s): Brad Crittenden
  • Date: 2013-08-01 20:59:27 UTC
  • mfrom: (325.1.9 bundle-search)
  • Revision ID: tarmac-20130801205927-lcbxxxb2bba92akt
[r=abentley][bug=][author=bac] Provide support for bundles in searching. The display of search results in the web app still only shows charms.

Show diffs side-by-side

added added

removed removed

Lines of Context:
96
96
 
97
97
    reviewed_clause = {'not': {'prefix': {'store_url': 'cs:~'}}}
98
98
 
99
 
    valid_charms_clauses = [
100
 
        {'missing': {'field': 'store_data.errors'}},
101
 
    ]
 
99
    valid_charms_clauses = [{
 
100
        'or': [{'and': [{'missing': {'field': 'store_data.errors'}}]},
 
101
               {'type': {'value': 'bundle'}},
 
102
               ]
 
103
    }]
102
104
 
103
105
    def __init__(self, client, index_name):
104
106
        self._client = client
282
284
        return dsl
283
285
 
284
286
    @classmethod
285
 
    def _get_filtered(cls, dsl, filters, type_, valid_charms_only):
 
287
    def _get_filtered(cls, dsl, filters, type_, valid_only):
286
288
        and_clause = {
287
 
            'and': [
288
 
                {'terms': {key: value}} for key, value in filters.items()
289
 
            ]
 
289
            'and': [{'terms': {key: value}} for key, value in filters.items()],
290
290
        }
291
 
        if valid_charms_only:
 
291
        if valid_only:
292
292
            and_clause['and'].extend(cls.valid_charms_clauses)
 
293
 
293
294
        if type_ is not None:
294
295
            typeclauses = []
295
296
            for value in type_:
297
298
                    typeclauses.append(cls.reviewed_clause)
298
299
                elif value == 'community':
299
300
                    typeclauses.append({
300
 
                        'not': cls.reviewed_clause
 
301
                        'not': cls.reviewed_clause,
301
302
                    })
302
303
                elif value == 'environment':
303
304
                    typeclauses.append({'term': {'_id': ''}})
343
344
            hits = self._client.search(dsl, **kwargs)
344
345
        return hits
345
346
 
346
 
    def api_search(self, text='', filters=None, type_=None, limit=None,
347
 
                   valid_charms_only=True, sort=None, autocomplete=False):
 
347
    def api_search(self, text='', filters=None, type_=None,
 
348
                   limit=None, valid_only=True, sort=None,
 
349
                   autocomplete=False):
348
350
        """Search charms (as used by the API).
349
351
 
350
352
        :param text: A string to search for-- will be analyzed (i.e. split
359
361
            is supplied, charms need not be "reviewed".
360
362
        :param limit: The maximum number of charms to return.  If None, all
361
363
            possible charms will be returned, but this is not efficient.
362
 
        :param valid_charms_only: If True, only charms that were successfully
363
 
            parsed are returned.
 
364
        :param valid_only: If True, only charms and bundles that were
 
365
            successfully parsed are returned.
364
366
        :param sort: If None, charms are sorted by score.  If 'downloaded',
365
367
            charms are in order of most to least downloaded.  If 'new', charms
366
368
            are in order of most-recently to least-recently created.
 
369
        :param autocomplete: If true use a prefix search for autocompletion.
367
370
        """
368
371
        if filters is None:
369
372
            filters = {}
370
373
        dsl = self._get_text_query(text, autocomplete)
371
374
        dsl = self._get_official_boost(dsl)
372
 
        dsl = self._get_filtered(dsl, filters, type_, valid_charms_only)
 
375
        dsl = self._get_filtered(dsl, filters, type_, valid_only)
373
376
        if sort == 'downloaded':
374
377
            dsl['sort'] = [{
375
378
                'downloads': {'order': 'desc'},
390
393
        with translate_error():
391
394
            return self._client.status(index=self.index_name)['indices']
392
395
 
393
 
    def search(self, terms, valid_charms_only=True):
 
396
    def search(self, terms, valid_only=True):
394
397
        # Avoid circular imports.
395
 
        from charmworld.models import Charm
 
398
        from charmworld.models import (
 
399
            Bundle,
 
400
            Charm,
 
401
        )
396
402
        size = None
397
403
        if terms == '':
398
404
            size = 0
399
405
 
400
406
        dsl = self._get_text_query(terms)
401
407
        dsl = self._get_official_boost(dsl)
402
 
        dsl = self._get_filtered(dsl, {}, None, valid_charms_only)
 
408
        dsl = self._get_filtered(dsl, {}, None, valid_only)
403
409
 
404
410
        with Timer() as timer:
405
411
            status = self.get_status()
415
421
                    real_index)
416
422
                log.info(message + '  Raising IndexNotReady.')
417
423
                raise IndexNotReady(message)
418
 
            charm_total = docs['num_docs']
 
424
            result_total = docs['num_docs']
419
425
            if size is None:
420
 
                size = charm_total
 
426
                size = result_total
421
427
            hits = self._client.search(dsl, index=self.index_name, size=size)
 
428
        hitlist = hits['hits']['hits']
422
429
        charms = [{'data': Charm(hit['_source']), 'weight': hit['_score']}
423
 
                  for hit in hits['hits']['hits']]
 
430
                  for hit in hitlist if hit['_type'] == CHARM]
 
431
        bundles = [{'data': Bundle(hit['_source']), 'weight': hit['_score']}
 
432
                   for hit in hitlist if hit['_type'] == BUNDLE]
 
433
 
 
434
        matches = len(charms) + len(bundles)
424
435
        return {
425
 
            'results': charms,
 
436
            'results': {CHARM: charms, BUNDLE: bundles},
426
437
            'search_time': u"%.5f" % timer.duration(),
427
 
            'charm_total': charm_total,
428
 
            'matches': len(charms),
429
 
            'matches_human_readable_estimate': '%d' % len(charms),
 
438
            'result_total': result_total,
 
439
            'matches': matches,
 
440
            'matches_human_readable_estimate': '%d' % matches,
430
441
        }
431
442
 
432
443
    def related_charms(self, requires, provides, series=None,
456
467
        dsl = {
457
468
            'filtered': {
458
469
                'filter': {
459
 
                    'and': and_clauses
 
470
                    'and': and_clauses,
460
471
                }
461
472
            }
462
473
        }
512
523
        with self.replacement(name) as copy:
513
524
            if charms is None:
514
525
                try:
515
 
                    charms = self.api_search(valid_charms_only=False)
 
526
                    charms = self.api_search(valid_only=False)
516
527
                except IndexMissing:
517
528
                    charms = []
518
529
            copy.index_charms(charms)