Skip to content

Instantly share code, notes, and snippets.

@bnlucas
Last active August 2, 2020 21:57
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bnlucas/5813387 to your computer and use it in GitHub Desktop.
Save bnlucas/5813387 to your computer and use it in GitHub Desktop.
Full Text Searching with the Google App Engine Search API. Using with Flask to demonstrate.
When going to /search/artist/<query_string> using the documents below you get:
/search/artist/m - this returns both Macklemore and Against Me!
/search/artist/a - this returns both Against Me! and AWOLNATION
/search/artist/ma - this returns only Macklemore
/search/artist/aw - this returns only AWOLNATION
/search/artist/me - this returns only Against Me!
Adding 'AWOLNATION' to models.Artist produces:
index_spec=[name='api-artist]
doc_id=['3861f55c-289f-5fb4-b738-a9c54d341da1']
fields=[
TextField[name='artist', value='AWOLNATION']
TextField[name='pieces', value='A,AW,AWO,AWOL,AWOLN,AWOLNA,AWOLNAT,AWOLNATI,AWOLNATIO,AWOLNATION']
TextField[name='id', value='3861f55c-289f-5fb4-b738-a9c54d341da1']
]
Adding 'Macklemore' to models.Artist produces:
index_spec=[name='api-artist]
doc_id=['59a83c26-851a-5a21-8d2c-7107ca515994']
fields=[
TextField[name='artist', value='Macklemore']
TextField[name='pieces', value='M,Ma,Mac,Mack,Mackl,Mackle,Macklem,Macklemo,Macklemor,Macklemore']
TextField[name='id', value='59a83c26-851a-5a21-8d2c-7107ca515994']
]
Adding 'Against Me!' to models.Artist produces:
index_spec=[name='api-artist]
doc_id=['183b7dba-976d-564b-bdb4-5865d90b34a3']
fields=[
TextField[name='artist', value='Against Me!']
TextField[name='pieces', value='A,Ag,Aga,Agai,Again,Agains,Against,M,Me,Me!']
TextField[name='id', value='183b7dba-976d-564b-bdb4-5865d90b34a3']
]
from google.appengine.ext import ndb
from google.appengine.api import search
def pieces(string):
pieces = []
for word in string.split():
cursor = 1
while True:
# this method produces pieces of 'TEXT' as 'T,TE,TEX,TEXT'
pieces.append(word[:cursor])
# optionally, you can do the following instead to procude
# 'TEXT' as 'T,E,X,T,TE,EX,XT,TEX,EXT,TEXT'
#
# for i in range(len(word) - cursor + 1):
# pieces.append(word[i:i + cursor])
if cursor == len(word): break
cursor += 1
return ','.join(pieces)
class Artist(ndb.Model):
name = ndb.StringProperty(required=True)
name_lower = ndb.ComputedProperty(lambda self: self.name.lower())
# ...other keys...
def _post_put_hook(self, future):
doc = search.Document(doc_id=self.key.id(), fields=[
search.TextField(name='artist', value=self.name),
search.TextField(name='pieces', value=pieces(self.name)),
search.TextField(name='id', value=self.key.id())])
search.Index('api-artist').put(doc)
@classmethod
def _post_delete_hook(cls, key, future):
search.Index('api-artist').delete(key.id())
from flask import jsonify
from google.appengine.api import search
from application import app, cache
@cache.memoize(timeout=60*60)
@app.route('/search/artist/<query_string>')
def search_artist(query_string):
sort = search.SortOptions(expressions=[
search.SortExpression(expression='artist',
direction=search.SortExpression.ASCENDING, default_value='')
], limit=1000)
options = search.QueryOptions(
limit=10,
sort_options=sort,
returned_fields=['artist', 'id'])
query_string = ''.join(['pieces:', query_string])
query = search.Query(query_string=query_string, options=options)
results = search.Index('api-artist').search(query)
out = {'results': []}
if results:
for item in results:
out['results'].append({f.name: f.value for f in item.fields})
return jsonify(out)
@fcgregorio
Copy link

Great example. But, for posterity, isn't line 26 of views.py supposed to be "for item in results.results"?

@nvictor
Copy link

nvictor commented Mar 26, 2015

that's a very good example indeed.

@nawarkhede
Copy link

Thanks for this example. Searched a lot but didn't find anything like this. Thanks :)

@houmie
Copy link

houmie commented Aug 27, 2016

Thank you so much for this. The documentation of GAE is bad. Without this I couldn't have figured it out.

@SaymoreChifamba
Copy link

Thanks a lot. This post has been very very helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment