Python 由于“按光标分页”的限制,查询会导致错误;“在过滤器中”;在cursor()方法中。。。替代方案应该是什么?

Python 由于“按光标分页”的限制,查询会导致错误;“在过滤器中”;在cursor()方法中。。。替代方案应该是什么?,python,google-app-engine,google-cloud-datastore,Python,Google App Engine,Google Cloud Datastore,我正在使用以下模型开发一个类似twitter的微博系统: 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 fo

我正在使用以下模型开发一个类似twitter的微博系统:

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()方法时,出现以下错误:

“没有可用于多重查询的光标 (使用“IN”或“!=”的查询) 运营商)

这是正常的,因为在游标的文档中,该限制明确规定为:

“不能将游标用于使用IN或!=筛选器运算符的查询。”

获得后续成员职位的替代方法是什么

谢谢,


编辑:已发布的邮件按其状态筛选,并按发布日期排序。。。但是示例没有在这里显示它,我已经更改了它…

假设您有一个名为member_obj的成员对象。根据你的模型,你可以做这样的事情

要获得以下列表

以下将为您提供密钥列表,只需将其传递给

followings_keys = member_obj.followings
Member.get(following_keys).
然后在成员之间循环并显示他们的消息。。
这篇文章对你会有很大帮助。

一种快速而恶劣的方式

下载这个 将其导入到项目中

然后你可以这样做分页

    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>
从分页器导入分页器,InvalidPage,EmptyPage
model=Member.all().fetch(100)
paginator=paginator(型号5)
if(self.request.GET):
page=int(self.request.GET.GET('page','1'))
如果(页面不是无):
尝试:
page=int(self.request.GET.GET('page','1'))
除值错误外:
页码=1
#如果页面请求(9999)超出范围,请提交最后一页结果。
尝试:
paginator=paginator.page(第页)
除(空页、无效页)外:
paginator=paginator.page(paginator.num_页面)
返回self.response.out.write(template.render(path+'.html',{'paginator':paginator}))
#在模板中
{%if paginator.object_list%}
{paginator.object_list%中的值为%s}
#完成你的任务
{%endfor%}
{%如果paginator.has_previous%}
{%else%}
以前的
{%endif%}
第{{paginator.paginator.num_pages}页中的第{{paginator.number}页
{%如果paginator.has_next%}
{%else%}
下一个
{%endif%}
单击next或previous,获取href val()并将其作为get变量传递给url,如


更多参考此限制的原因是
中的
=查询通过将查询拆分为多个基础查询来执行,这些查询由数据存储单独执行,然后按排序顺序合并在一起

如果您想以分页方式执行这样的查询,您必须自己执行查询,然后自己进行合并。要获取游标,需要从各个子查询中获取游标并将它们连接在一起。此外,您还需要跟踪有多少已获取但未使用的结果,这样您就可以准确地从停止的地方开始


如您所见,这很复杂,并导致光标值过长,这就是为什么SDK目前没有实现它的原因。不幸的是,这是唯一可行的方法,除非您可以找到一种方法来避免在
子句中使用
,或者放弃由另一个子句进行排序的要求(在这种情况下,您可以串行执行查询,对每个查询进行分页).

我的解决方案是像光标一样使用日期值,正如我对Nick Johnson答案的评论所述。。。是这样的:

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())
如果光标:#这实际上不是光标!它是base64日期时间字符串
cursordate=_strtime(base64.b64解码(cursor))35;_strtime是一个将str转换为datetime的本地方法
#中有列表限制:允许30个项目
listofNewsLists=[]
ListOfMemberList=[i:i+30]表示范围内的i(0,len(后跟成员列表),30)]
对于ListofMemberList中的每个列表:
query=NewsItem.all()
query.filter('posted_by IN',eachList.).filter('status=',1)
如果光标:

我已经能够使用搜索API解决这个问题

您需要在搜索文档中镜像对象的相关位,并将其保存到\u pre\u put\u hook或\u post\u put\u hook中的索引中(您可以在\u pre\u delete\u hook或\u post\u delete\u hook中清除这些内容)。使用密钥的序列化作为搜索文档id


一旦你做到了这一点,你就可以用上面提到的搜索来进行搜索。它很快!只需返回doc_id,然后用它们来获取()您的数据存储对象。

感谢您提供的信息和有用的链接…但我的问题与收集后续成员列表无关…真正的问题是获得订单(按发布日期)消息列表并有一个用于分页的光标…我不会一次显示所有消息…消息在新请求时以10×10的方式显示…如果不在查询筛选器中使用IN,我找不到在有序跟随成员的消息查询中使用光标的方法!你应该看看Brett Slatkin两年前的出色I/O对话,即c这是一个非常准确的用例:我在搜索我的问题的类似条目时看到了这个链接,我同意你的观点,它包含了非常有用的信息…但是,我无法解决分页问题,也无法从那里按日期排序后面的成员帖子…无论如何