예제 #1
0
class Searcher(object):

    def __init__(self, aerospike_connector):
        self._search_index = SearchIndex(aerospike_connector)

    def search(self, params):
        if params.features == None or len(params.features) == 0:
            signs = self._search_nearest(params)
        else:
            signs = self._search_by_fea(params)

        return self._make_result(params, signs)

    def _records_processor(self, params, heap):
        def _impl(record):
            entry = SearchEntry()
            entry.sign_id = int(record['sign_id'])
            entry.rank = record.get('rank')
            # Min rank threshold
            if entry.rank != None and entry.rank < params.min_rank:
                return
            # Legacy
            if entry.rank == None:
                entry.rank = 0

            location = record['location'].unwrap().get('coordinates')
            entry.distance = geo.distance(params.latitude, params.longitude, location[1], location[0])
            heap.push(entry)
        return _impl

    def _search_nearest(self, params):
        heap = SearchResultsHeap(params.max_n, lambda entry: -entry.distance)
        # Retrieve records
        self._search_index.search_nearest(params.user_id, \
                                          params.latitude, \
                                          params.longitude, \
                                          params.radius, \
                                          self._records_processor(params, heap))
        # Resort
        signs = heap.results.values()
        signs.sort(key=lambda entry: entry.distance)
        return signs

    def _search_by_fea(self, params):
        heap = SearchResultsHeap(params.max_n, lambda entry: entry.rank)
        # Normailize features vector
        qsum = 0
        for fea in params.features:
            qsum = qsum + fea * fea
        qsum = math.sqrt(qsum)
        for i in range(len(params.features)):
            params.features[i] = params.features[i] / qsum
        # Retrieve records
        self._search_index.search_by_fea(params.user_id, \
                                         params.latitude, \
                                         params.longitude, \
                                         params.radius, \
                                         params.features, \
                                         self._records_processor(params, heap))
        # Resort
        signs = heap.results.values()
        if params.sort_by == SearchParams.SORT_BY_RANK or \
           params.sort_by == SearchParams.SORT_BY_UNKNOWN:
            signs.sort(key=lambda entry: entry.rank, reverse=True)
        else:
            signs.sort(key=lambda entry: entry.distance)
        return signs

    def _make_result(self, params, signs):
        signs_list = list()
        for sign in signs:
            signs_list.append({
                'sign_id': sign.sign_id,
                'distance': sign.distance
            })

        debug = None
        if params.debug:
            weights = list()
            for sign in signs:
                weights.append({
                    'sign_id': sign.sign_id,
                    'weight': sign.rank
                })
            debug = {
                'weights': weights
            }
        return signs_list, debug