试用GAE数据库的全文搜索功能

标签:Google App Engine, Python

今天在澄空得知,GAE的数据库有个隐藏的全文搜索API,英文介绍可以看《How-to: Full-text search in Google App Engine》(可能要翻墙,18岁以下不要看太多评论…)。(更新一下,原文地址已经不能访问了,目前雅虎的缓存里还能找到当时的页面)
喜欢看源码的可以去看google_appengine\google\appengine\ext\search\__init__.py,或者去Google Code

不过试用过后,发现隐藏起来(无文档)也是应该的,因为还处于开发阶段,缺陷太多了。
其中最重要的一点:不支持ASCII码以外的字符(这点就足以让我放弃了,不支持中文就没什么能搜的了)。

嘛,还是把用法写出来吧,毕竟还是有人有英文搜索的需求。

首先要注意几点:
  1. 索引只针对单词(即用空格或标点符号隔开的英文),不能搜索单词的一部分(例如无法通过搜索'hell'来找到'hello')。
  2. 搜索的最少字数为3,这个是用_FULL_TEXT_MIN_LENGTH定义的。
  3. 部分常用单词不编入索引(例如'a'),这个是用_FULL_TEXT_STOP_WORDS定义的。
  4. 索引的单词都调用了lower,所以是不区分大小写的。
  5. 索引会作用于所有的string和text属性,搜索时不能只指定其中的一个,且属性指定indexed=False时仍会被索引。
  6. 由于每个单词都要索引,所以索引空间会占用很多,且性能也比一般的要低(即CPU时间会长些)。
  7. 搜索条件可以有多个,且允许与filter、sort等搭配,搜索限制上类似于IN操作符(即可以与不等于操作符搭配)。

接着看用法:

模型的定义不再使用db.Model,而是使用google.appengine.ext.search的search.SearchableModel,它是db.Model的子类。

数据的存储及删除操作完全与db.Model相同,因为是直接继承自db.Model的。

搜索时增加了一个search方法,将需要搜索的字符填入即可(前面已说过,不支持中文,不区分大小写,忽略常见词和3个字符以下的词)。
如果需要搜索多个词,用空格分隔各个单词即可,或者使用2次search方法。这2种方法在大部分情况下相同,但如果搜索的单词包含被忽略词(常见词和3个字符以下的词),前者会忽略不符合的部分,后者则会忽略整个search条件。即搜索'hello all'(all是常见词),可以找到包含'hello'的实体,但搜索'hello'和'all'则会忽略search条件,搜索所有实体。

索引的定义使用__searchable_text_index,这个是用_FULL_TEXT_INDEX_PROPERTY定义的。


然后给个例子:
# -*- coding: utf-8 -*-

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext import search

class Card(search.SearchableModel):
  id = db.IntegerProperty()
  name = db.StringProperty()
  description = db.TextProperty()

class AddHandler(webapp.RequestHandler):
  def get(self):
    db.put([
      Card(id=1172, name='Obelisk the Tormentor', description=u'咒文●其到来之时,灼热之疾风必吹荒大地,生者必成尸骸'),
      Card(id=1116, name='Slifer the Sky Dragon', description=u'咒文●天空雷鸣混沌之时,集连锁中之远古魔导书,其力可至无限!'),
      Card(id=1177, name='The Winged Dragon of Ra', description=u'咒文●精灵歌唱,亘古之力司万物!其命,其魂,以至其骸。')
    ])

class DelHandler(webapp.RequestHandler):
  def get(self):
    db.delete(Card.all().order('id').fetch(500))

class MainHandler(webapp.RequestHandler):
  def get(self):
    cards = Card.all().search('dragon').fetch(1000)
    for card in cards:
      self.response.out.write(card.description + '<br />')

def main():
  application = webapp.WSGIApplication([('/', MainHandler),
                                        ('/add', AddHandler),
                                        ('/del', DelHandler)])
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()

3条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?