是否有可能在ndb中的实体上设置两个字段作为索引? [英] Is it possible to set two fields as indexes on an entity in ndb?

查看:74
本文介绍了是否有可能在ndb中的实体上设置两个字段作为索引?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是ndb和gae的新手,并且遇到了一个解决方案设置索引的问题。
假设我们有这样的用户模型:

$ p $ class User(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty(required = True)
fb_id = ndb.StringProperty()

登录后,如果我要用查询来检查电子邮件地址,我相信这会很慢并且效率低下。可能它必须进行全表扫描。

  q = User.query(User.email == EMAIL)
user = q.fetch(1)

我相信如果用户模型是用电子邮件作为他们的钥匙保存。

  user = user(id = EMAIL)
user.put()

这样我就可以更快地检索它们(所以我相信)

  key = ndb.Key('User',EMAIL)
user = key.get()

到目前为止,如果我错了,请纠正我。但是在实现这个之后,我意识到facebook用户有可能改变他们的电子邮件地址,这样在新的oauth2.0连接上他们的新电子邮件不能在系统中被识别,并且他们将被创建为新用户。因此,也许我应该使用不同的方法: b
$ b


  • 使用social-media-provider-id(所有提供者用户都是唯一的)






  • 供应商名称(极少数情况下这两个Twitter和Facebook用户分享
    相同的提供者ID)



然而,为了实现这一点,我需要设置两个索引,我相信这不是可能



那我该怎么办?我应该将两个字段连接为单个键和索引吗?



例如。新的想法是:

$ p $ class User(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty(required = True)
provider_id = ndb.StringProperty()
provider_type = ndb.StringProperty()
$ b

保存:

  provider_id = 1234 
provider_type = fb
user = user(id = provider_id + provider_type)
user.put()

检索:

  provider_id = 1234 
provider_type = fb
key = ndb.Key('User ',provider_id + provider_type)
user = key.get()

这样我们不会如果用户更改他的社交媒体上的电子邮件地址,则不再关心。
这个主意听起来不错吗?



谢谢,
$ b

更新



Tim的解决方案迄今为止最清晰,也可能是我最快的解决方案。但是我遇到了一个问题。

  class AuthProvider(polymodel.PolyModel):
user_key = ndb.KeyProperty(kind =用户)
active = ndb.BooleanProperty(default = True)
date_created = ndb.DateTimeProperty(auto_now_add = True)

@property
def user(self) :
返回self.user_key.get()
$ b $ class FacebookLogin(AuthProvider):
pass


$ b

View.py:在facebook_callback方法中

 提供者= ndb.Key('FacebookLogin',fb_id).get()

#问题就在这里。提供者总是无。只有当我使用PolyModel时才会这样:
#ndb.Key('AuthProvider',fb_id).get()
#But这样做会破坏将不同子类作为不同提供者的全部目的。
#可能我正在使用钥匙handeling错误?


如果提供者:
user = provider.user
else:
提供者= FacebookLogin(id = fb_id)
如果不是用户:
user = User()
user_key = user.put()
provider.user_key = user_key
provider.put()
返回用户


解决方案

您的方法可能允许更灵活的模型的一个细微变化是创建一个用于provider_id,provider_type的独立实体,作为您创建的密钥或任何其他授权方案



然后,该实体拥有实际用户详细信息的引用(键) 。

然后,您可以为auth直接获取get(),然后使用


  1. 详细信息,然后获取()实际用户详细信息。

  2. 可以更改认证详细信息,而不必实际重写/更改用户详细信息。针对单个用户的多个授权方案。

我使用这个对于拥有> 2000用户的应用程序,pproach大多数使用自定义认证方案(app specific userid / passwd)或谷歌账户。 b

  class AuthLogin(ndb.Polymodel):
user_key = ndb.KeyProperty(kind = User)
status = ndb.StringProperty() #也许你需要禁用一个特定的登录而不删除它。
date_created = ndb.DatetimeProperty(auto_now_add = True)

@property
def user(self):
return self.user_key.get()


类FacebookLogin(AuthLogin):
#一些额外的Facebook属性

类TwitterLogin(AuthLogin):
#一些额外的Twitter特定属性

etc ...

通过使用PolyModel作为您可以执行 AuthLogin.query()。过滤器(AuthLogin.user_key == user.key)并获取为该用户定义的所有auth类型,因为它们全部共享相同的基类AuthLogin。你需要这个,否则你将不得不依次查询每个支持的auth类型,因为你不能在没有祖先的情况下进行无情的查询,在这种情况下,我们不能使用 User 作为祖先,因此我们无法从登录ID做一个简单的get()。



然而有些事情要注意,AuthLogin的所有子类都会在密钥AuthLogin中共享相同类型,所以您仍然需要连接auth_provider和auth_type以获取密钥id,以确保您拥有唯一的密钥。例如。

  dev〜fish-and-lily> from google.appengine.ext.ndb.polymodel import PolyModel 
dev〜fish-and-lily>类X(PolyModel):
...传递
...
dev〜鱼和百合>类Y(X):
...通过
...
dev〜鱼和百合>类Z(X):
...通过
...
dev〜鱼和百合> y = Y(id =abc)
dev〜fish-and-lily> y.put()
Key('X','abc')
dev〜fish-and-lily> z = Z(id =abc)
dev〜fish-and-lily> z.put()
Key('X','abc')
dev〜fish-and-lily> y.key.get()
Z(key = Key('X','abc'),class _ = [u'X',u'Z'])

dev〜鱼和百合> z.key.get()
Z(key = Key('X','abc'),class _ = [u'X',u'Z'])

这是您遇到的问题。通过将提供者类型添加为密钥的一部分,您现在可以获得不同的密钥。

  dev〜fish-and-lily> z = Z(id =Zabc)
dev〜fish-and-lily> z.put()
键('X','Zabc')
dev〜鱼和百合> y = Y(id =Yabc)
dev〜fish-and-lily> y.put()
Key('X','Yabc')
dev〜fish-and-lily> y.key.get()
Y(key = Key('X','Yabc'),class _ = [u'X',u'Y'])
dev〜fish-and-百合> z.key.get()
Z(key = Key('X','Zabc'),class _ = [u'X',u'Z'])
dev〜fish-and-百合>

我不相信这对您来说是一个不太方便的模型。



这一切是否合理;-)


I am new to ndb and gae and have a problem coming up with a good solution setting indexes. Let say we have a user model like this:

class User(ndb.Model):
    name = ndb.StringProperty()    
    email = ndb.StringProperty(required = True)    
    fb_id = ndb.StringProperty()

Upon login if I was going to check against the email address with a query, I believe this would be quite slow and inefficient. Possibly it has to do a full table scan.

q = User.query(User.email == EMAIL)
user = q.fetch(1)

I believe it would be much faster, if User models were saved with the email as their key.

user = user(id=EMAIL)
user.put()

That way I could retrieve them like this a lot faster (so I believe)

key = ndb.Key('User', EMAIL) 
user = key.get()

So far if I am wrong please correct me. But after implementing this I realized there is a chance that facebook users would change their email address, that way upon a new oauth2.0 connection their new email can't be recognized in the system and they will be created as a new user. Hence maybe I should use a different approach:

  • Using the social-media-provider-id (unique for all provider users)

and

  • provider-name (in rare case that two twitter and facebook users share the same provider-id)

However in order to achieve this, I needed to set two indexes, which I believe is not possible.

So what could I do? Shall I concatenate both fields as a single key and index on that?

e.g. the new idea would be:

class User(ndb.Model):
    name = ndb.StringProperty()    
    email = ndb.StringProperty(required = True)    
    provider_id = ndb.StringProperty()
    provider_type = ndb.StringProperty()

saving:

provider_id = 1234
provider_type = fb
user = user(id=provider_id + provider_type)
user.put()

retrieval:

provider_id = 1234
provider_type = fb
key = ndb.Key('User', provider_id + provider_type) 
user = key.get()

This way we don't care any more if the user changes the email address on his social media. Is this idea sound?

Thanks,

UPDATE

Tim's solution sounded so far the cleanest and likely also the fastest to me. But I came across a problem.

class AuthProvider(polymodel.PolyModel):
    user_key = ndb.KeyProperty(kind=User)
    active = ndb.BooleanProperty(default=True)  
    date_created = ndb.DateTimeProperty(auto_now_add=True)

    @property
    def user(self):
        return self.user_key.get()

class FacebookLogin(AuthProvider):
    pass

View.py: Within facebook_callback method

provider = ndb.Key('FacebookLogin', fb_id).get() 

# Problem is right here. provider is always None. Only if I used the PolyModel like this:
# ndb.Key('AuthProvider', fb_id).get()
#But this defeats the whole purpose of having different sub classes as different providers. 
#Maybe I am using the key handeling wrong?


if provider:
    user = provider.user
else:
    provider = FacebookLogin(id=fb_id)          
if not user:
        user = User()
        user_key = user.put()
        provider.user_key = user_key
        provider.put() 
return user

解决方案

One slight variation on your approach which could allow a more flexible model will be to create a separate entity for the provider_id, provider_type, as the key or any other auth scheme you come up with

This entity then holds a reference (key) of the actual user details.

You can then

  1. do a direct get() for the auth details, then get() the actual user details.
  2. The auth details can be changed without actually rewriting/rekeying the user details
  3. You can support multiple auth schemes for a single user.

I use this approach for an application that has > 2000 users, most use a custom auth scheme (app specific userid/passwd) or google account.

e.g

class AuthLogin(ndb.Polymodel):
     user_key = ndb.KeyProperty(kind=User)
     status = ndb.StringProperty()  # maybe you need to disable a particular login with out deleting it.
     date_created = ndb.DatetimeProperty(auto_now_add=True)

     @property
     def user(self):
         return self.user_key.get()


class FacebookLogin(AuthLogin):
    # some additional facebook properties

class TwitterLogin(AuthLogin):
    # Some additional twitter specific properties

etc...

By using PolyModel as the base class you can do a AuthLogin.query().filter(AuthLogin.user_key == user.key) and get all auth types defined for that user as they all share the same base class AuthLogin. You need this otherwise you would have to query in turn for each supported auth type, as you can not do a kindless query without an ancestor, and in this case we can't use the User as the ancestor becuase then we couldn't do a simple get() to from the login id.

However some things to note, all subclasses of AuthLogin will share the same kind in the key "AuthLogin" so you still need to concatenate the auth_provider and auth_type for the keys id so that you can ensure you have unique keys. E.g.

dev~fish-and-lily> from google.appengine.ext.ndb.polymodel import PolyModel
dev~fish-and-lily> class X(PolyModel):
...    pass
... 
dev~fish-and-lily> class Y(X):
...    pass
... 
dev~fish-and-lily> class Z(X):
...    pass
... 
dev~fish-and-lily> y = Y(id="abc")
dev~fish-and-lily> y.put()
Key('X', 'abc')
dev~fish-and-lily> z = Z(id="abc")
dev~fish-and-lily> z.put()
Key('X', 'abc')
dev~fish-and-lily> y.key.get()
Z(key=Key('X', 'abc'), class_=[u'X', u'Z'])

dev~fish-and-lily> z.key.get()
Z(key=Key('X', 'abc'), class_=[u'X', u'Z'])

This is the problem you ran into. By adding the provider type as part of the key you now get distinct keys.

dev~fish-and-lily> z = Z(id="Zabc")
dev~fish-and-lily> z.put()
Key('X', 'Zabc')
dev~fish-and-lily> y = Y(id="Yabc")
dev~fish-and-lily> y.put()
Key('X', 'Yabc')
dev~fish-and-lily> y.key.get()
Y(key=Key('X', 'Yabc'), class_=[u'X', u'Y'])
dev~fish-and-lily> z.key.get()
Z(key=Key('X', 'Zabc'), class_=[u'X', u'Z'])
dev~fish-and-lily> 

I don't believe this is any less convenient a model for you.

Does all that make sense ;-)

这篇关于是否有可能在ndb中的实体上设置两个字段作为索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆