Запрос с подкачкой курсором вызывает ошибку из-за ограничений для «IN filter» в методе cursor () ... Какой должна быть альтернатива? - PullRequest
2 голосов
/ 16 мая 2011

Я занимаюсь разработкой системы микроблогов, подобной твиттеру, используя следующие модели:

class Member(db.Model):    
    user = db.UserProperty(required=True)
    follower_count = db.IntegerProperty(default=0) # members following you    
    following_count = db.IntegerProperty(default=0) # members you are following

class NewsItem(db.Model):    
    text = db.StringProperty(required=True)
    posted_by = db.ReferenceProperty(reference_class=Member,required=True,collection_name="posted_items")
    posted_on = db.DateTimeProperty(auto_now_add=True)
    status = db.IntegerProperty(default=1) # 0: deleted

class Follow(db.Model):    
    member = db.ReferenceProperty(reference_class=Member,required=True,collection_name="followings")    
    followed_member = db.ReferenceProperty(reference_class=Member,required=True,collection_name="followers")    
    added_on = db.DateTimeProperty(auto_now_add=True) 

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

follow_log_list = Follow.gql('WHERE member = :1 ', member)
followed_member_list = []
for follow_log in follow_log_list:
    followed_member_list.append(follow_log.followed_member)

query = NewsItem.all()
query.filter('posted_by IN', followed_member_list)
query.filter('status =', 1)
query.order('-posted_on')
query.with_cursor(cursor)   
newsList = query.fetch(10)  

template_values['cursor'] = query.cursor()

Когда вызывается метод query.cursor (), я получаю следующую ошибку:

"Нет курсора для MultiQuery (запросы с использованием "IN" или "! =" операторы) "

Это нормально, поскольку в документации по курсорам это ограничение четко указано как:

http://code.google.com/appengine/docs/python/datastore/queries.html

"Вы не можете использовать курсоры с запросами, которые используют операторы IN или! = Filter."

Каков альтернативный способ получения постов следующих участников?

Спасибо,

РЕДАКТИРОВАТЬ: отправленные сообщения были отфильтрованы по их статусу и упорядочены по дате их публикации ... Но образец не показал здесь, я изменил это ...

Ответы [ 5 ]

2 голосов
/ 16 мая 2011

Быстрый и неприятный способ ...

Скачайте этот pagintor.py импортируйте его в свой проект.

Тогда вы можете сделать что-то подобное для нумерации страниц

    from paginator import Paginator, InvalidPage, EmptyPage
     model = Member.all().fetch(100)
     paginator = Paginator(model,5)

                if(self.request.GET):
                    page = int(self.request.GET.get('page', '1'))
                    if(page is not None):
                        try:
                            page = int(self.request.GET.get('page', '1'))
                        except ValueError:
                            page = 1

                        # If page request (9999) is out of range, deliver last page of results.
                        try:
                            paginator = paginator.page(page)
                        except (EmptyPage, InvalidPage):
                            paginator = paginator.page(paginator.num_pages)
    return self.response.out.write( template.render(path+'.html',{'paginator':paginator}))


#In templates

{% if paginator.object_list %}

{% for values in paginator.object_list %}

#do your tasks

{% endfor %}
<div  align="right" class="pagination" >
        {% if paginator.has_previous %}
            <a  id="previous" href="{{ paginator.previous_page_number }}">Previous</a>
        {% else %}
         <span class="page-nulled" >
            Previous
        </span>
        {% endif %}

        <span class="current" id="pagenum" title="{{ paginator.number }}">
            &nbsp;&nbsp;&nbsp;Page {{ paginator.number }} of {{paginator.paginator.num_pages }}&nbsp;&nbsp;&nbsp;
        </span>

        {% if paginator.has_next %}
            <a  id="next" href="{{ paginator.next_page_number }}"> Next </a>
            {% else %}
         <span class="page-nulled" >
            Next 
        </span>
        {% endif %}

</div>

при нажатии на следующую или предыдущую, возьмите href val () и перейдите к URL-адресу в качестве переменной get, например http://someurl? Page = .

moreссылка здесь

1 голос
/ 18 мая 2011

Мое решение использует значение даты как курсор, как я описал как комментарий к ответу Ника Джонсона ... Это так:

if cursor: # This is not actually a cursor! It is base64 datetime string
  cursordate = _strptime(base64.b64decode(cursor)) # _strptime is a local method that converts str to datetime

# IN has a limit for lists: 30 items allowed
listofNewsLists = []
listofMemberLists = [followed_member_list[i:i+30] for i in range(0, len(followed_member_list), 30)]
for eachList in listofMemberLists:
   query = NewsItem.all()
   query.filter('posted_by IN', eachList).filter('status =', 1)
   if cursor:
      query.filter('posted_on <', cursordate)
   query.order('-posted_on')                        
   listofNewsLists.append(query.fetch(PAGE_SIZE))

  newsList = []
  if listofNewsLists:
    emptyListCount = 0
    while len(newsList) < PAGE_SIZE and emptyListCount < len(listofNewsLists):
      max = datetime.datetime.min
      maxInd = -1
      emptyListCount = 0
      for i in range(len(listofNewsLists)):
        if listofNewsLists[i] == []:
          emptyListCount += 1
        elif listofNewsLists[i][0].posted_on > max:
          max = listofNewsLists[i][0].posted_on
          maxInd = i
      if max > datetime.datetime.min:
        newsList.append(listofNewsLists[maxInd].pop(0))

template_values['cursor'] = base64.b64encode(newsList[-1].posted_on.isoformat())

То есть; Я сохраняю значение даты последнего отображаемого элемента в качестве отправной точки нового списка ...

Это работает хорошо (я думаю), если у меня нет предметов с таким же значением posts_on ...

1 голос
/ 17 мая 2011

Причина этого ограничения заключается в том, что запросы IN и != выполняются путем разделения запроса на несколько базовых запросов, которые выполняются хранилищем данных индивидуально, а затем объединяются в отсортированном порядке.

Если вы хотите выполнить такой запрос в разбивке на страницы, вам придется выполнить запросы самостоятельно и выполнить слияние самостоятельно. Чтобы получить курсор, вам нужно выбрать курсоры из отдельных подзапросов и объединить их вместе. Кроме того, вам нужно следить за тем, сколько полученных, но не потребленных результатов вы получили, чтобы вы могли получить информацию именно с того места, где остановились.

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

0 голосов
/ 04 июня 2014

Мне удалось решить эту проблему с помощью API поиска.

https://developers.google.com/appengine/docs/python/search/

Вам необходимо отразить соответствующие биты ваших объектов в документе поиска и сохранить его.к индексу в _pre_put_hook или _post_put_hook (и вы можете очистить их в своем _pre_delete_hook или _post_delete_hook).Используйте сериализацию вашего ключа в качестве поискового doc_id.

Как только вы это сделаете, вы можете выполнить поиск с помощью поиска, который вы пытаетесь сделать выше.Это быстро!Просто верните doc_ids, а затем используйте их, чтобы получить () ваши объекты хранилища данных.

0 голосов
/ 16 мая 2011

Предположим, у вас есть объект-член с именем member_obj.С вашей моделью вы можете сделать что-то вроде этого

Чтобы получить список следующих элементов,

member_obj.followings предоставит вам список ключей, просто передайте его

followings_keys = member_obj.followings
Member.get(following_keys).

Затем просто переберите участников и покажите их сообщения. Этот пост на Моделирование в appengine вам очень поможет.

...