GAE текстовый поиск с вторичной сортировкой - PullRequest
3 голосов
/ 20 октября 2011

Вместо полнотекстового поиска в GAE я использую приведенное ниже решение, чтобы вернуть отсортированный набор результатов, сначала по релевантности по ключевым словам, а затем по дате (хотя на самом деле вторая сортировка может быть чем угодно).Это кажется немного громоздким, и я беспокоюсь о производительности в масштабе, поэтому я ищу предложения по оптимизации или совсем другой подход.

Вторичная сортировка важна для моего варианта использования, так как данный поиск, скорее всего, будетиметь несколько результатов одинаковой релевантности (измеряемой количеством совпадений ключевых слов), но сохранение исходного порядка запросов в настоящее время значительно усложняет задачу.Есть идеи?

Шаг 1: Получить список ключей, соответствующих каждому поисковому запросу

results_key_list = []
search_terms = ['a','b','c'] #User's search query, split into a list of strings

#query each search term and add the results to a list
#yields a list of keys with frequency of occurance indicating relevance     
for item in search_terms:
    subquery = SomeEntity.all(keys_only=True)                   
    subquery.filter('SearchIndex = ', item) #SearchIndex is a StringListProperty
    #more filters...            
    subquery.order('-DateCreated')                  
    for returned_item in subquery:
        results_key_list.append(str(returned_item))     

Шаг 2: Сгруппировать список по частоте, сохраняя при этом первоначальный порядок

#return a dictionary of keys, with their frequency of occurrence            
grouped_results = defaultdict(int)              
for key in results_key_list:
    grouped_results[key] += 1               

sorted_results = []
known = set()

#creates an empty list for each match frequency 
for i in range(len(search_terms)):
    sorted_results.append([])

#using the original results ordering, 
#construct an array of results grouped and ordered by descending frequency  
for key in results_key_list:
    if key in known: continue
    frequency = grouped_results[key]
    sorted_results[len(search_terms) - frequency].append(key)
    known.add(key)          

#combine into a single list
ordered_key_list = []   
for l in sorted_results:
    ordered_key_list.extend(l)  

del ordered_key_list[:offset]
del ordered_key_list[limit:]    
result = SomeEntity.get(ordered_key_list)

Ответы [ 2 ]

2 голосов
/ 25 октября 2011

На шаге 1 вы перебираете объект запроса. Это приводит к одному извлечению RPC на 20 возвращаемых объектов, что неэффективно по времени. Вместо этого вызовите fetch(n) для объекта Query, где n - максимальное количество возвращаемых результатов - это делает только один RPC. Он также имеет преимущество, заключающееся в ограничении числа результатов поиска - прямо сейчас, если я буду искать «I», ваше приложение будет зависать при обработке почти каждой записи на шаге 1.

Также совершенно не нужно преобразовывать ключи в строки - вы можете просто добавить ключи в набор.

Что бы это ни стоило, лично я нахожу 'или' поиски особенно бесполезными. Я понимаю, что вы будете ранжировать предметы, которые в первую очередь соответствуют всем условиям, но за ними неизбежно последуют груды ненужных результатов. Вы можете выполнить поиск «и», просто выполнив один запрос с фильтром равенства для каждого поискового запроса.

2 голосов
/ 21 октября 2011
search_terms = ['a','b','c'] #User's search query, split into a list of strings

Вы можете накапливать ключи в порядке появления и может наращивать ключевые частоты всего за один проход. Воспользуйтесь стабильностью сортировки, чтобы сделать сортировку путем уменьшения частоты, а затем по порядку появления:

keys_in_order_of_appearance = []
key_frequency = defaultdict(int)

for item in search_terms:
    subquery = SomeEntity.all(keys_only=True)                   
    subquery.filter('SearchIndex = ', item) #SearchIndex is a StringListProperty
    #more filters...            
    subquery.order('-DateCreated')                  
    for returned_item in subquery:
        key = str(returned_item)
        if key not in key_frequency:
            key_order_of_appearance.append(key)
        key_frequency[key] += 1

keys = keys_in_order_of_appearance[:]   # order of appearance kept as secondary sort
keys.sort(key=key_frequency.__getitem__, reverse=True) # descending freq as primary sort
result = SomeEntity.get(ordered_key_list)
...