【问题标题】:Better way to find sub strings in Datastore?在 Datastore 中查找子字符串的更好方法?
【发布时间】:2016-10-30 13:22:20
【问题描述】:

我有一个应用程序,其中用户输入一个名称,该应用程序返回该名称的地址和城市

名称在数据存储中

class Person(ndb.Model):
    name = ndb.StringProperty(repeated=True) 
    address = ndb.StringProperty(indexed=False)
    city = ndb.StringProperty()

有超过 500 万个 Person 实体。名字可以由2到8个单词组成(是的,他的名字中有8个单词的人)

用户可以为名称输入任何单词(以任何顺序),应用程序将返回第一个匹配项。(“John Doe Smith”等同于“Smith Doe John”)

这是我的实体示例(放置方式(ndb.put_multi)

id="L12802795",nombre=["Smith","Loyola","Peter","","","","",""], city="Cali",address="Conchuela 471"
id="M19181478",nombre=["Hoffa","Manzano","Linda","Rosse","Claudia","Cindy","Patricia",""], comuna="Lima",address=""
id="L18793849",nombre=["Parker","Martinez","Claudio","George","Paul","","",""], comuna="Santiago",address="Calamar 323 Villa Los Pescadores"

这是我从用户那里得到名字的方式:

name = self.request.get('content').strip()  #The input is the name (an string with several words)
name=" ".join(name.split()).split() #now the name is a list of single words

在我的设计中,为了找到一种方法来查找和搜索每个实体名称中的单词,我这样做了。

      q = Person.query()

      if len(name)==1:
                names_query =q.filter(Person.name==name[0])
      elif len(name)==2:
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1])
      elif len(name)==3:
                    names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2])
      elif len(name)==4:
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2]).filter(Person.name==name[3])
      elif len(name)==5:
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2]).filter(Person.name==name[3]).filter(Person.name==name[4])
      elif len(name)==6:
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2]).filter(Person.name==name[3]).filter(Person.name==name[4]).filter(Person.name==name[5])
      elif len(name)==7:
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2]).filter(Person.name==name[3]).filter(Person.name==name[4]).filter(Person.name==name[5]).filter(Person.name==name[6])
      else :
                names_query =q.filter(Person.name==name[0]).filter(Person.name==name[1]).filter(Person.name==name[2]).filter(Person.name==name[3]).filter(Person.name==name[4]).filter(Person.name==name[5]).filter(Person.name==name[6]).filter(Person.name==name[7])

       Person = names_query.fetch(1)
       person_id=Person.key.id()

问题 1 您是否认为,在我的设计中,有一种更好的方法可以在字符串 (ndb.StringProperty) 中搜索子字符串。 (我知道它有效,但我觉得它可以改进)

问题 2 我的解决方案对于名称中有重复单词的实体存在问题。

如果我想找到一个带有“Smith Smith”字样的实体,它会给我带来“Paul Smith Wshite”而不是“Paul Smith Smith”,我不知道如何修改我的查询以找到 2 个(或更多) Person.name 中重复的单词

【问题讨论】:

  • 在我的示例代码中 (This is a sample of my entities(the way how was put(ndb.put_multi)) 替换 "nombre""name" "city" 的 "comuna" 以便理解它。

标签: python-2.7 google-app-engine google-cloud-datastore


【解决方案1】:

您可以为每个名称生成所有可能标记的列表,并使用前缀过滤器来查询它们:

class Person(ndb.Model):
  name = ndb.StringProperty(required=True)
  address = ndb.StringProperty(indexed=False)
  city = ndb.StringProperty()

  def _tokens(self):
    """Returns all possible combinations of name tokens combined.

    For example, for input 'john doe smith' we will get:
    ['john doe smith', 'john smith doe', 'doe john smith', 'doe smith john',
     'smith john doe', 'smith doe john']
    """
    tokens = [t.lower() for t in self.name.split(' ') if t]
    return [' '.join(t) for t in itertools.permutations(tokens)] or None

  tokens = ndb.ComputedProperty(_tokens, repeated=True)

  @classmethod
  def suggest(cls, s):
    s = s.lower()
    return cls.query(ndb.AND(cls.tokens >= s, cls.tokens <= s + u'\ufffd'))


ndb.put_multi([Person(name='John Doe Smith'), Person(name='Jane Doe Smith'),
               Person(name='Paul Smith Wshite'), Person(name='Paul Smith'),
               Person(name='Test'), Person(name='Paul Smith Smith')])
assert Person.suggest('j').count() == 2
assert Person.suggest('ja').count() == 1
assert Person.suggest('jo').count() == 1
assert Person.suggest('doe').count() == 2
assert Person.suggest('t').count() == 1
assert Person.suggest('Smith Smith').get().name == 'Paul Smith Smith'
assert Person.suggest('Paul Smith').count() == 3

如果您只需要键/ID,请确保使用 keys_only 查询。就数据存储区 OP 而言,这将使事情变得更快并且几乎免费。

【讨论】:

  • 感谢@Dmytro Sadovnychyi,这是一个聪明的解决方案。 (我花了一段时间不明白,我是新手)。但它不适用于大于 8 个单词的名称(有更多 40320 排列大于 1MB,所以我不能把它放在数据存储中)。对于少于 7 个单词的名称可以正常工作。我改进了替换: return [' '.join(t) for t in itertools.permutations(tokens)] 或 None for: m=[' '.join(t) for t in itertools.permutations(tokens)] return list( set(l)) 或无
  • 感谢@DmytroSadovnychyi
  • 不客气!如果它为您解决了问题,请确保接受答案。
猜你喜欢
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 2014-02-09
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多