SlideShare a Scribd company logo
1 of 67
Download to read offline
WRITING APPS THE GOOGLE-Y WAY Pamela Fox, YOW! Australia 2010
Who am I? twitter.com/pamelafox [email_address] pamelafox.org you get the idea...
Who am I? USC Google Amazon, Flickr, Maps Google Maps API Google Wave API Spreadsheets, Blogger, Youtube, Picasa, Gadgets,  App Engine
Who am I? Java pYthon
What is App Engine? ,[object Object],[object Object]
What is a “web app”?
Static vs. Dynamic
Anonymous vs. Users
Intranet vs. Internet ~2 billion Hundreds - Thousands
What is a “web app”?
Some Google web apps
Some Google App Engine web apps www.gifttag.com www.buddypoke.com
Google apps on App Engine panoramio.com pubsubhubbub.appspot.com
How does App Engine work? ,[object Object],[object Object],[object Object]
Example app: YOW!*
App Engine architecture user or task
App Engine architecture
App Engine architecture LIMIT!
The tricky bits LIMIT!
Datastore Entity Properties Key Entity Entity Entity Entity Path Kind Name/ID
Example: Speaker Entities Key Path Kind ID First Name Last Name Speaker1 - Speaker 1 Rod Johnson Key Path Kind ID First Name Last Name Middle Name Suffix Speaker1 - Speaker 2 Guy Steele L Jr.
Modeling Speaker Entities ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
Saving Speaker Entities ,[object Object],[object Object],[object Object],[object Object],[object Object]
Updating Speaker Entities ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],LIMIT!
How Updates Happen commit journal apply entities apply indexes A B
Queries & Indexes Query Index Index Index Index Query Query Query Query Query
Queries & Indexes SELECT * from Speaker ORDER BY lastname LIMIT! key lastname Speaker3 Fox Speaker4 Hohpe Speaker1 Johnson Speaker2 Steele
Queries & Indexes SELECT * from Speaker ORDER by middlename key middlename Speaker2 L
Queries & Indexes SELECT * from Speaker WHERE keynote = True key keynote Speaker1 True Speaker2 True Speaker3 False Speaker4 False
Queries & Indexes SELECT * from Speaker WHERE keynote = False key keynote Speaker1 True Speaker2 True Speaker3 False Speaker4 False
Queries ,[object Object],[object Object],[object Object],[object Object],[object Object],LIMIT!
Custom Indexes ,[object Object],[object Object],SELECT * from Speaker ORDER BY lastname, keynote key lastname keynote Speaker3 Fox false Speaker4 Hohpe false Speaker1 Johnson true Speaker2 Steele true
Custom Indexes ,[object Object],[object Object],SELECT * from Speaker WHERE lastname > 'Johnson'  and keynote = true key lastname keynote Speaker3 Fox false Speaker4 Hohpe false Speaker1 Johnson true Speaker2 Steele true
Impossible Indexes SELECT * from Speaker WHERE lastname < 'Steele'  and firstname > 'Gregory' ...not in subsequent rows! key lastname firstname Speaker3 Fox Pamela Speaker4 Hohpe Gregory Speaker1 Johnson Ron Speaker2 Steele Guy
Impossible Indexes SELECT * from Speaker WHERE lastname > 'Fox' ORDER BY firstname  ...not in the correct order! key lastname firstname Speaker3 Fox Pamela Speaker4 Hohpe Gregory Speaker1 Johnson Ron Speaker2 Steele Guy
Queries with Offset ,[object Object],SELECT * from Speaker LIMIT 2 OFFSET 2 1 2 ...slow! LIMIT! key lastname Speaker3 Fox Speaker4 Hohpe Speaker1 Johnson Speaker2 Steele
Queries with Cursors ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
More Properties class Talk(db.Model): title = db.StringProperty(required=True) abstract = db.TextProperty(required=True) speaker = db.ReferenceProperty(Speaker) tags = db.StringListProperty() pamela = Speaker.all().filter('firstname = ', 'Pamela').get() talk = Talk('Writing Apps the Googley Way', 'Bla bla bla', pamela, ['App Engine', 'Python']) talk.put() talk = Talk('Wonders of the Onesie', 'Bluh bluh bluh', pamela, ['Pajamas', 'Onesies']) talk.put()
Back-References pamela = Speaker.all().filter('firstname = ', 'Pamela').get() for talk in pamela.talk_set: print talk.title SELECT * from Talk WHERE speaker = Speaker3 key speaker Talk6 Speaker2 Talk1 Speaker3 Talk2 Speaker3 Talk5 Speaker4
Searching List Properties talks = Talk.all().filter('tags = ', 'python').fetch(10) SELECT * from Talk WHERE tags = 'Python' LIMIT! key lastname Talk1 App Engine Talk2 Pajamas Talk1 Python Talk2 Onesies
Entity Groups pamela = Speaker.all().filter('firstname = ', 'Pamela').get() talk1 = Talk('Writing Apps the Googley Way', 'Bla bla bla', pamela, ['App Engine', 'Python'], parent=pamela) talk2 = Talk('Wonders of the Onesie', 'Bluh bluh bluh', pamela, ['Pajamas', 'Onesies'], parent=pamela) db.put(talk1, talk2) def update_talks(): talk1.title = 'Writing Apps the Microsoft Way' talk2.title = 'Wonders of the Windows' db.put(talk1, talk2) db.run_in_transaction(update_talks)
Common Features
Counters 1 2 3 4 5 people have done something.
RageTube: Global Stats
RageTube: Global Stats SongStat yaycount viewcount title artist Key Path Kind Name (song) naycount mehcount
RageTube: Global Stats viewcount viewcount viewcount datastore memcache
RageTube: Global Stats class Song(db.Model): viewcount = db.IntegerProperty(default=0) title = db.StringProperty() artist = db.StringProperty() def get_viewcount(self): viewcount = self.viewcount cached_viewcount = memcache.get('viewcount-' + self.key().name(), self.key().kind()) if cached_viewcount: viewcount += cached_viewcount return viewcount @classmethod def flush_viewcount(cls, name): song = cls.get_by_key_name(name) value = memcache.get('viewcount-' + name, cls.kind()) memcache.decr('viewcount-' + name, value, cls.kind()) song.viewcount += value song.put() @classmethod def incr_viewcount(cls, name, interval=5, value=1): memcache.incr('viewcount-' + name, value, cls.kind()) interval_num = get_interval_number(datetime.now(), interval) task_name = '-'.join([cls.kind(), name.replace(' ', '-'),  'viewcount', str(interval), str(interval_num)]) deferred.defer(cls.flush_viewcount, name, _name=task_name) LIMIT!
Ratings Rated by 500 users.
App Gallery: Ratings
App Gallery: Ratings Comment Application total_ratings sum_ratings avg_rating rated_index comment_count rating
App Gallery: Ratings def UpdateAppCommentData(self, rating, operation): def UpdateCommentData(self, rating, operation): self.comment_count += 1 * operation self.sum_ratings += rating * operation self.total_ratings += 1 * operation self.avg_rating = int(round(self.sum_ratings / self.total_ratings)) self.rated_index = '%d:%d:%d' %  (self.avg_rating, self.total_ratings, self.index) self.put() db.run_in_transaction(UpdateCommentData, self, rating, operation) app.UpdateAppCommentData(rating, db_models.Comment.ADD) comment = db_models.Comment() comment.application = app comment.rating = rating comment.put() query.order('-avg_rating').order('-rated_index')
Geospatial Queries
City-Go-Round: Agencies citygoround.org https://github.com/walkscore/City-Go-Round
City-Go-Round: Geo Queries Agency GeoModel location (GeoPt) location_geocells (StringListProperty)
City-Go-Round: Geo Queries def fetch_agencies_near(lat, long, bbox_side_in_miles): query = Agency.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return Agency.bounding_box_fetch(query, bbox, max_results = 50) def bounding_box_fetch(query, bbox, max_results=1000,): results = [] query_geocells = geocell.best_bbox_search_cells(bbox) for entity in query.filter('location_geocells IN', query_geocells): if len(results) == max_results: break if (entity.location.lat >= bbox.south and entity.location.lat <= bbox.north and entity.location.lon >= bbox.west and entity.location.lon <= bbox.east): results.append(entity) return results
City-Go-Round: Apps citygoround.org
City-Go-Round: Geo Queries TransitAppLocation GeoModel location (GeoPt) location_geocells (StringListProperty) TransitApp
City-Go-Round: Geo Queries class TransitAppLocation(GeoModel): transit_app = db.ReferenceProperty(TransitApp) def fetch_transit_app_locations_near(lat, longi): query = TransitAppLocation.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return TransitAppLocation.bounding_box_fetch(query, bounding_box, max_results = 500) def fetch_transit_apps_near(lat, long): transit_app_locations = TransitAppLocation.fetch_transit_app_locations_near(lat, long)] transit_apps = [transit_app_location.transit_app  for transit_app_location in transit_app_locations] return transit_apps
Full Text Search ,[object Object],Search Thingy It's like pizza, but in the cloud. Other Thingy This will make you smell as delicious as pizza.
Disclosed.ca: Search
Disclosed.ca: Search Contract agency_name vendor_name description comments uri
Disclosed.ca: Search from search.core import SearchIndexProperty, porter_stemmer class Contract(db.Model): uri = db.StringProperty(required=True) agency_name = db.StringProperty(required=True) vendor_name = db.StringProperty(required=True) description = db.StringProperty() comments = db.TextProperty() search_index = SearchIndexProperty(('agency_name', 'vendor_name', 'description', 'comments'), indexer=porter_stemmer) results = Contract.search_index.search(sheep').fetch(20)
Disclosed.ca: Search Contract agency_name vendor_name description comments uri search_index (StringListProperty) SearchIndex
Disclosed.ca: Search SELECT FROM ContractSearch WHERE search_index = &quot;sheep&quot; key search_index ContractSearch1 charter ContractSearch1 june ContractSearch1 sheep ContractSearch2 sheep ContractSearch1 wood
More Learning http://ae-book.appspot.com http://code.google.com/appengine http://blog.notdot.net/
AppEngine: Now & Later &quot;Run your web apps on Google's infrastructure. Easy to build, easy to maintain, easy to scale.&quot; ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
[object Object],[object Object]

More Related Content

What's hot

Php Basic Security
Php Basic SecurityPhp Basic Security
Php Basic Securitymussawir20
 
Writing Friendly libraries for CodeIgniter
Writing Friendly libraries for CodeIgniterWriting Friendly libraries for CodeIgniter
Writing Friendly libraries for CodeIgniterCodeIgniter Conference
 
WIRED and the WP REST API
WIRED and the WP REST APIWIRED and the WP REST API
WIRED and the WP REST APIkvignos
 
Creating Themes
Creating ThemesCreating Themes
Creating ThemesDaisyOlsen
 
Dealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottDealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottO'Reilly Media
 
Power Theming
Power ThemingPower Theming
Power Themingdrkdn
 
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaReno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaDre Armeda
 
ABC of Perl programming
ABC of Perl programmingABC of Perl programming
ABC of Perl programmingBo Hua Yang
 
Moving from Django Apps to Services
Moving from Django Apps to ServicesMoving from Django Apps to Services
Moving from Django Apps to ServicesCraig Kerstiens
 
Elasticsearch – mye mer enn søk! [JavaZone 2013]
Elasticsearch – mye mer enn søk! [JavaZone 2013]Elasticsearch – mye mer enn søk! [JavaZone 2013]
Elasticsearch – mye mer enn søk! [JavaZone 2013]foundsearch
 
Web Scraping is BS
Web Scraping is BSWeb Scraping is BS
Web Scraping is BSJohn D
 
Finding things on the web with BOSS
Finding things on the web with BOSSFinding things on the web with BOSS
Finding things on the web with BOSSChristian Heilmann
 
How does get template part works in twenty ten theme
How does get template part works in twenty ten themeHow does get template part works in twenty ten theme
How does get template part works in twenty ten thememohd rozani abd ghani
 
Google Wave 20/20: Product, Protocol, Platform
Google Wave 20/20: Product, Protocol, PlatformGoogle Wave 20/20: Product, Protocol, Platform
Google Wave 20/20: Product, Protocol, PlatformPamela Fox
 
Introduction to PHP Lecture 1
Introduction to PHP Lecture 1Introduction to PHP Lecture 1
Introduction to PHP Lecture 1Ajay Khatri
 

What's hot (20)

Php Basic Security
Php Basic SecurityPhp Basic Security
Php Basic Security
 
Writing Friendly libraries for CodeIgniter
Writing Friendly libraries for CodeIgniterWriting Friendly libraries for CodeIgniter
Writing Friendly libraries for CodeIgniter
 
WIRED and the WP REST API
WIRED and the WP REST APIWIRED and the WP REST API
WIRED and the WP REST API
 
Creating Themes
Creating ThemesCreating Themes
Creating Themes
 
Ruby on discuz
Ruby on discuzRuby on discuz
Ruby on discuz
 
Rails 101
Rails 101Rails 101
Rails 101
 
Dealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottDealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter Scott
 
Introduction to python scrapping
Introduction to python scrappingIntroduction to python scrapping
Introduction to python scrapping
 
Power Theming
Power ThemingPower Theming
Power Theming
 
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaReno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
 
Php 3 1
Php 3 1Php 3 1
Php 3 1
 
ABC of Perl programming
ABC of Perl programmingABC of Perl programming
ABC of Perl programming
 
Moving from Django Apps to Services
Moving from Django Apps to ServicesMoving from Django Apps to Services
Moving from Django Apps to Services
 
Elasticsearch – mye mer enn søk! [JavaZone 2013]
Elasticsearch – mye mer enn søk! [JavaZone 2013]Elasticsearch – mye mer enn søk! [JavaZone 2013]
Elasticsearch – mye mer enn søk! [JavaZone 2013]
 
Web Scraping is BS
Web Scraping is BSWeb Scraping is BS
Web Scraping is BS
 
Finding things on the web with BOSS
Finding things on the web with BOSSFinding things on the web with BOSS
Finding things on the web with BOSS
 
Symfony2 meets propel 1.5
Symfony2 meets propel 1.5Symfony2 meets propel 1.5
Symfony2 meets propel 1.5
 
How does get template part works in twenty ten theme
How does get template part works in twenty ten themeHow does get template part works in twenty ten theme
How does get template part works in twenty ten theme
 
Google Wave 20/20: Product, Protocol, Platform
Google Wave 20/20: Product, Protocol, PlatformGoogle Wave 20/20: Product, Protocol, Platform
Google Wave 20/20: Product, Protocol, Platform
 
Introduction to PHP Lecture 1
Introduction to PHP Lecture 1Introduction to PHP Lecture 1
Introduction to PHP Lecture 1
 

Similar to Writing Apps the Google-y Way

Python - Getting to the Essence - Points.com - Dave Park
Python - Getting to the Essence - Points.com - Dave ParkPython - Getting to the Essence - Points.com - Dave Park
Python - Getting to the Essence - Points.com - Dave Parkpointstechgeeks
 
The bones of a nice Python script
The bones of a nice Python scriptThe bones of a nice Python script
The bones of a nice Python scriptsaniac
 
Object Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonObject Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonPython Ireland
 
Javascript Primer
Javascript PrimerJavascript Primer
Javascript PrimerAdam Hepton
 
Decorators in Python
Decorators in PythonDecorators in Python
Decorators in PythonBen James
 
Custom Signals for Uncoupled Design
Custom Signals for Uncoupled DesignCustom Signals for Uncoupled Design
Custom Signals for Uncoupled Designecomsmith
 
Testing Jboss Seam Bottom Up
Testing Jboss Seam Bottom UpTesting Jboss Seam Bottom Up
Testing Jboss Seam Bottom UpDan Hinojosa
 
Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009Core Software Group
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Languageintelliyole
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPStephan Schmidt
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPstubbles
 
Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Stephan Schmidt
 

Similar to Writing Apps the Google-y Way (20)

Python - Getting to the Essence - Points.com - Dave Park
Python - Getting to the Essence - Points.com - Dave ParkPython - Getting to the Essence - Points.com - Dave Park
Python - Getting to the Essence - Points.com - Dave Park
 
The bones of a nice Python script
The bones of a nice Python scriptThe bones of a nice Python script
The bones of a nice Python script
 
Django
DjangoDjango
Django
 
Fantom and Tales
Fantom and TalesFantom and Tales
Fantom and Tales
 
Object Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonObject Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in Python
 
Javascript Primer
Javascript PrimerJavascript Primer
Javascript Primer
 
Decorators in Python
Decorators in PythonDecorators in Python
Decorators in Python
 
JavaScript Needn't Hurt!
JavaScript Needn't Hurt!JavaScript Needn't Hurt!
JavaScript Needn't Hurt!
 
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
 
Having Fun Programming!
Having Fun Programming!Having Fun Programming!
Having Fun Programming!
 
Custom Signals for Uncoupled Design
Custom Signals for Uncoupled DesignCustom Signals for Uncoupled Design
Custom Signals for Uncoupled Design
 
Testing Jboss Seam Bottom Up
Testing Jboss Seam Bottom UpTesting Jboss Seam Bottom Up
Testing Jboss Seam Bottom Up
 
Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009
 
SlideShare Instant
SlideShare InstantSlideShare Instant
SlideShare Instant
 
SlideShare Instant
SlideShare InstantSlideShare Instant
SlideShare Instant
 
The Kotlin Programming Language
The Kotlin Programming LanguageThe Kotlin Programming Language
The Kotlin Programming Language
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHP
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHP
 
Apache Ant
Apache AntApache Ant
Apache Ant
 
Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5
 

More from Pamela Fox

Teaching Programming Online
Teaching Programming OnlineTeaching Programming Online
Teaching Programming OnlinePamela Fox
 
Engineering culture
Engineering cultureEngineering culture
Engineering culturePamela Fox
 
Django Admin: Widgetry & Witchery
Django Admin: Widgetry & WitcheryDjango Admin: Widgetry & Witchery
Django Admin: Widgetry & WitcheryPamela Fox
 
A Year of Hermit Hacking
A Year of Hermit HackingA Year of Hermit Hacking
A Year of Hermit HackingPamela Fox
 
The Developer Experience
The Developer Experience The Developer Experience
The Developer Experience Pamela Fox
 
Making JavaScript Libraries More Approachable
Making JavaScript Libraries More ApproachableMaking JavaScript Libraries More Approachable
Making JavaScript Libraries More ApproachablePamela Fox
 
How I became a born again vegetable-tarian
How I became a born again vegetable-tarianHow I became a born again vegetable-tarian
How I became a born again vegetable-tarianPamela Fox
 
The Developer Experience
The Developer ExperienceThe Developer Experience
The Developer ExperiencePamela Fox
 
No, Really, I'm Shy
No, Really, I'm ShyNo, Really, I'm Shy
No, Really, I'm ShyPamela Fox
 
The Wonders of the "Onesie"
The Wonders of the "Onesie"The Wonders of the "Onesie"
The Wonders of the "Onesie"Pamela Fox
 
I’M A Barbie Girl In A CS World
I’M A Barbie Girl In A CS WorldI’M A Barbie Girl In A CS World
I’M A Barbie Girl In A CS WorldPamela Fox
 
Collaborative Mapping with Google Wave
Collaborative Mapping with Google WaveCollaborative Mapping with Google Wave
Collaborative Mapping with Google WavePamela Fox
 
Google Products: Deep Dive on Google Maps
Google Products: Deep Dive on Google MapsGoogle Products: Deep Dive on Google Maps
Google Products: Deep Dive on Google MapsPamela Fox
 
Google Products & Google Maps
Google Products & Google MapsGoogle Products & Google Maps
Google Products & Google MapsPamela Fox
 
A World of Words
A World of WordsA World of Words
A World of WordsPamela Fox
 
Web APIs & Google APIs
Web APIs & Google APIsWeb APIs & Google APIs
Web APIs & Google APIsPamela Fox
 
Growing up Geek: My Dad, the Computer Scientist
Growing up Geek: My Dad, the Computer ScientistGrowing up Geek: My Dad, the Computer Scientist
Growing up Geek: My Dad, the Computer ScientistPamela Fox
 
Living in the Cloud: Hosting Data & Apps Using the Google Infrastructure
Living in the Cloud: Hosting Data & Apps Using the Google InfrastructureLiving in the Cloud: Hosting Data & Apps Using the Google Infrastructure
Living in the Cloud: Hosting Data & Apps Using the Google InfrastructurePamela Fox
 
Client Killed the Server Star
Client Killed the Server StarClient Killed the Server Star
Client Killed the Server StarPamela Fox
 
Flex vs. HTML5 for RIAS
Flex vs. HTML5 for RIASFlex vs. HTML5 for RIAS
Flex vs. HTML5 for RIASPamela Fox
 

More from Pamela Fox (20)

Teaching Programming Online
Teaching Programming OnlineTeaching Programming Online
Teaching Programming Online
 
Engineering culture
Engineering cultureEngineering culture
Engineering culture
 
Django Admin: Widgetry & Witchery
Django Admin: Widgetry & WitcheryDjango Admin: Widgetry & Witchery
Django Admin: Widgetry & Witchery
 
A Year of Hermit Hacking
A Year of Hermit HackingA Year of Hermit Hacking
A Year of Hermit Hacking
 
The Developer Experience
The Developer Experience The Developer Experience
The Developer Experience
 
Making JavaScript Libraries More Approachable
Making JavaScript Libraries More ApproachableMaking JavaScript Libraries More Approachable
Making JavaScript Libraries More Approachable
 
How I became a born again vegetable-tarian
How I became a born again vegetable-tarianHow I became a born again vegetable-tarian
How I became a born again vegetable-tarian
 
The Developer Experience
The Developer ExperienceThe Developer Experience
The Developer Experience
 
No, Really, I'm Shy
No, Really, I'm ShyNo, Really, I'm Shy
No, Really, I'm Shy
 
The Wonders of the "Onesie"
The Wonders of the "Onesie"The Wonders of the "Onesie"
The Wonders of the "Onesie"
 
I’M A Barbie Girl In A CS World
I’M A Barbie Girl In A CS WorldI’M A Barbie Girl In A CS World
I’M A Barbie Girl In A CS World
 
Collaborative Mapping with Google Wave
Collaborative Mapping with Google WaveCollaborative Mapping with Google Wave
Collaborative Mapping with Google Wave
 
Google Products: Deep Dive on Google Maps
Google Products: Deep Dive on Google MapsGoogle Products: Deep Dive on Google Maps
Google Products: Deep Dive on Google Maps
 
Google Products & Google Maps
Google Products & Google MapsGoogle Products & Google Maps
Google Products & Google Maps
 
A World of Words
A World of WordsA World of Words
A World of Words
 
Web APIs & Google APIs
Web APIs & Google APIsWeb APIs & Google APIs
Web APIs & Google APIs
 
Growing up Geek: My Dad, the Computer Scientist
Growing up Geek: My Dad, the Computer ScientistGrowing up Geek: My Dad, the Computer Scientist
Growing up Geek: My Dad, the Computer Scientist
 
Living in the Cloud: Hosting Data & Apps Using the Google Infrastructure
Living in the Cloud: Hosting Data & Apps Using the Google InfrastructureLiving in the Cloud: Hosting Data & Apps Using the Google Infrastructure
Living in the Cloud: Hosting Data & Apps Using the Google Infrastructure
 
Client Killed the Server Star
Client Killed the Server StarClient Killed the Server Star
Client Killed the Server Star
 
Flex vs. HTML5 for RIAS
Flex vs. HTML5 for RIASFlex vs. HTML5 for RIAS
Flex vs. HTML5 for RIAS
 

Recently uploaded

UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1DianaGray10
 
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...DianaGray10
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-pyJamie (Taka) Wang
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1DianaGray10
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Bird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemBird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemAsko Soukka
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding TeamAdam Moalla
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationIES VE
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfAijun Zhang
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.YounusS2
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopBachir Benyammi
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 

Recently uploaded (20)

UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
 
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
20230104 - machine vision
20230104 - machine vision20230104 - machine vision
20230104 - machine vision
 
20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-py
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Bird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemBird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystem
 
20150722 - AGV
20150722 - AGV20150722 - AGV
20150722 - AGV
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdf
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 Workshop
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 

Writing Apps the Google-y Way

  • 1. WRITING APPS THE GOOGLE-Y WAY Pamela Fox, YOW! Australia 2010
  • 2. Who am I? twitter.com/pamelafox [email_address] pamelafox.org you get the idea...
  • 3. Who am I? USC Google Amazon, Flickr, Maps Google Maps API Google Wave API Spreadsheets, Blogger, Youtube, Picasa, Gadgets, App Engine
  • 4. Who am I? Java pYthon
  • 5.
  • 6. What is a “web app”?
  • 9. Intranet vs. Internet ~2 billion Hundreds - Thousands
  • 10. What is a “web app”?
  • 12. Some Google App Engine web apps www.gifttag.com www.buddypoke.com
  • 13. Google apps on App Engine panoramio.com pubsubhubbub.appspot.com
  • 14.
  • 16. App Engine architecture user or task
  • 19. The tricky bits LIMIT!
  • 20. Datastore Entity Properties Key Entity Entity Entity Entity Path Kind Name/ID
  • 21. Example: Speaker Entities Key Path Kind ID First Name Last Name Speaker1 - Speaker 1 Rod Johnson Key Path Kind ID First Name Last Name Middle Name Suffix Speaker1 - Speaker 2 Guy Steele L Jr.
  • 22.
  • 23.
  • 24.
  • 25. How Updates Happen commit journal apply entities apply indexes A B
  • 26. Queries & Indexes Query Index Index Index Index Query Query Query Query Query
  • 27. Queries & Indexes SELECT * from Speaker ORDER BY lastname LIMIT! key lastname Speaker3 Fox Speaker4 Hohpe Speaker1 Johnson Speaker2 Steele
  • 28. Queries & Indexes SELECT * from Speaker ORDER by middlename key middlename Speaker2 L
  • 29. Queries & Indexes SELECT * from Speaker WHERE keynote = True key keynote Speaker1 True Speaker2 True Speaker3 False Speaker4 False
  • 30. Queries & Indexes SELECT * from Speaker WHERE keynote = False key keynote Speaker1 True Speaker2 True Speaker3 False Speaker4 False
  • 31.
  • 32.
  • 33.
  • 34. Impossible Indexes SELECT * from Speaker WHERE lastname < 'Steele' and firstname > 'Gregory' ...not in subsequent rows! key lastname firstname Speaker3 Fox Pamela Speaker4 Hohpe Gregory Speaker1 Johnson Ron Speaker2 Steele Guy
  • 35. Impossible Indexes SELECT * from Speaker WHERE lastname > 'Fox' ORDER BY firstname ...not in the correct order! key lastname firstname Speaker3 Fox Pamela Speaker4 Hohpe Gregory Speaker1 Johnson Ron Speaker2 Steele Guy
  • 36.
  • 37.
  • 38. More Properties class Talk(db.Model): title = db.StringProperty(required=True) abstract = db.TextProperty(required=True) speaker = db.ReferenceProperty(Speaker) tags = db.StringListProperty() pamela = Speaker.all().filter('firstname = ', 'Pamela').get() talk = Talk('Writing Apps the Googley Way', 'Bla bla bla', pamela, ['App Engine', 'Python']) talk.put() talk = Talk('Wonders of the Onesie', 'Bluh bluh bluh', pamela, ['Pajamas', 'Onesies']) talk.put()
  • 39. Back-References pamela = Speaker.all().filter('firstname = ', 'Pamela').get() for talk in pamela.talk_set: print talk.title SELECT * from Talk WHERE speaker = Speaker3 key speaker Talk6 Speaker2 Talk1 Speaker3 Talk2 Speaker3 Talk5 Speaker4
  • 40. Searching List Properties talks = Talk.all().filter('tags = ', 'python').fetch(10) SELECT * from Talk WHERE tags = 'Python' LIMIT! key lastname Talk1 App Engine Talk2 Pajamas Talk1 Python Talk2 Onesies
  • 41. Entity Groups pamela = Speaker.all().filter('firstname = ', 'Pamela').get() talk1 = Talk('Writing Apps the Googley Way', 'Bla bla bla', pamela, ['App Engine', 'Python'], parent=pamela) talk2 = Talk('Wonders of the Onesie', 'Bluh bluh bluh', pamela, ['Pajamas', 'Onesies'], parent=pamela) db.put(talk1, talk2) def update_talks(): talk1.title = 'Writing Apps the Microsoft Way' talk2.title = 'Wonders of the Windows' db.put(talk1, talk2) db.run_in_transaction(update_talks)
  • 43. Counters 1 2 3 4 5 people have done something.
  • 45. RageTube: Global Stats SongStat yaycount viewcount title artist Key Path Kind Name (song) naycount mehcount
  • 46. RageTube: Global Stats viewcount viewcount viewcount datastore memcache
  • 47. RageTube: Global Stats class Song(db.Model): viewcount = db.IntegerProperty(default=0) title = db.StringProperty() artist = db.StringProperty() def get_viewcount(self): viewcount = self.viewcount cached_viewcount = memcache.get('viewcount-' + self.key().name(), self.key().kind()) if cached_viewcount: viewcount += cached_viewcount return viewcount @classmethod def flush_viewcount(cls, name): song = cls.get_by_key_name(name) value = memcache.get('viewcount-' + name, cls.kind()) memcache.decr('viewcount-' + name, value, cls.kind()) song.viewcount += value song.put() @classmethod def incr_viewcount(cls, name, interval=5, value=1): memcache.incr('viewcount-' + name, value, cls.kind()) interval_num = get_interval_number(datetime.now(), interval) task_name = '-'.join([cls.kind(), name.replace(' ', '-'), 'viewcount', str(interval), str(interval_num)]) deferred.defer(cls.flush_viewcount, name, _name=task_name) LIMIT!
  • 48. Ratings Rated by 500 users.
  • 50. App Gallery: Ratings Comment Application total_ratings sum_ratings avg_rating rated_index comment_count rating
  • 51. App Gallery: Ratings def UpdateAppCommentData(self, rating, operation): def UpdateCommentData(self, rating, operation): self.comment_count += 1 * operation self.sum_ratings += rating * operation self.total_ratings += 1 * operation self.avg_rating = int(round(self.sum_ratings / self.total_ratings)) self.rated_index = '%d:%d:%d' % (self.avg_rating, self.total_ratings, self.index) self.put() db.run_in_transaction(UpdateCommentData, self, rating, operation) app.UpdateAppCommentData(rating, db_models.Comment.ADD) comment = db_models.Comment() comment.application = app comment.rating = rating comment.put() query.order('-avg_rating').order('-rated_index')
  • 53. City-Go-Round: Agencies citygoround.org https://github.com/walkscore/City-Go-Round
  • 54. City-Go-Round: Geo Queries Agency GeoModel location (GeoPt) location_geocells (StringListProperty)
  • 55. City-Go-Round: Geo Queries def fetch_agencies_near(lat, long, bbox_side_in_miles): query = Agency.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return Agency.bounding_box_fetch(query, bbox, max_results = 50) def bounding_box_fetch(query, bbox, max_results=1000,): results = [] query_geocells = geocell.best_bbox_search_cells(bbox) for entity in query.filter('location_geocells IN', query_geocells): if len(results) == max_results: break if (entity.location.lat >= bbox.south and entity.location.lat <= bbox.north and entity.location.lon >= bbox.west and entity.location.lon <= bbox.east): results.append(entity) return results
  • 57. City-Go-Round: Geo Queries TransitAppLocation GeoModel location (GeoPt) location_geocells (StringListProperty) TransitApp
  • 58. City-Go-Round: Geo Queries class TransitAppLocation(GeoModel): transit_app = db.ReferenceProperty(TransitApp) def fetch_transit_app_locations_near(lat, longi): query = TransitAppLocation.all() bbox = bbox_centered_at(lat, long, bbox_side_in_miles) return TransitAppLocation.bounding_box_fetch(query, bounding_box, max_results = 500) def fetch_transit_apps_near(lat, long): transit_app_locations = TransitAppLocation.fetch_transit_app_locations_near(lat, long)] transit_apps = [transit_app_location.transit_app for transit_app_location in transit_app_locations] return transit_apps
  • 59.
  • 61. Disclosed.ca: Search Contract agency_name vendor_name description comments uri
  • 62. Disclosed.ca: Search from search.core import SearchIndexProperty, porter_stemmer class Contract(db.Model): uri = db.StringProperty(required=True) agency_name = db.StringProperty(required=True) vendor_name = db.StringProperty(required=True) description = db.StringProperty() comments = db.TextProperty() search_index = SearchIndexProperty(('agency_name', 'vendor_name', 'description', 'comments'), indexer=porter_stemmer) results = Contract.search_index.search(sheep').fetch(20)
  • 63. Disclosed.ca: Search Contract agency_name vendor_name description comments uri search_index (StringListProperty) SearchIndex
  • 64. Disclosed.ca: Search SELECT FROM ContractSearch WHERE search_index = &quot;sheep&quot; key search_index ContractSearch1 charter ContractSearch1 june ContractSearch1 sheep ContractSearch2 sheep ContractSearch1 wood
  • 65. More Learning http://ae-book.appspot.com http://code.google.com/appengine http://blog.notdot.net/
  • 66.
  • 67.

Editor's Notes

  1. *This is not actually an App Engine site. But for the sake of demonstration and having something to talk about that you guys are familiar with, let’s use it as an example It has data, like each speaker and their talks, and it has users that want to register for the conference.
  2. App Engine gets request from client OR from cron Figures out what app its mapping to Decides if request corresponds to static or dynamic content If static: Serves file from static servers. Cache, faster, latency. Otherwise: Selects a server that will respond the fastest Fires up app, sends the request, gets the response, gives response to user
  3. Runs either Python interpreter or JVM 6 Doesn’t retain state (like global variables) Can read its own files, can’t write any files or read other app’s files Can’t access networking facilities or hardware Doesn’t expose OS details
  4. Run multiple apps from same hardware Limit clock time, CPU usage, memory usage of apps Gives each app 30 seconds to respond to each  Enforces isolation
  5. Similar to an object store / object database. A datastore is made up of entities, and each entity has a kind, a key, and properties. The key is unique for each entity, is set on creation and never changed. It provides a fast way to retrive that entity. The kind is used mostly when querying the datastore, as most queries only returns results of a particular kind. The properties can vary for each entity of a kind – the underlying datastore is schemaless. You’ll often use the API to enforce a schema, for better application logic, however. Properties are optional; you don’t have to have any at all.
  6. We could actually have more properties on Guy to store the additional parts of his name. App Engine would have no problems with this.
  7. But we usually like to enforce schema in code, like so. When we save them to the datastore using put(), the datastore auto assigns a key. We could also set the keys ourself, but we have to make sure they are unique.
  8. But we usually like to enforce schema in code, like so. When we save them to the datastore using put(), the datastore auto assigns a key. We could also set the keys ourself, but we have to make sure they are unique.
  9. We know the key_name of the entity, as we specified it when we created it. That isn&apos;t the same as the full key, however. Here we do a batch put() to save in time. Subject to limit in size/number of entities.
  10. This is a transaction! By default, only one entity is in a transaction at a time. We&apos;ll see how later how to have multiple in a transaction.
  11. In App Engine, every query must be answered by an existing index or it will return an error. So it must know ahead of time the types of questions that you will ask. App Engine doesn&apos;t has a weak query engine compared to other DBs. Those other DBs don&apos;t perform at web speeds with large amounts of data spread across multiple machines, however.  Let&apos;s look at some example queries and indexes.
  12. When performing query, finds the index, finds the first matching row, returns entities until first not-matching row.
  13. Filtering Or Sorting On a Property Requires That the Property Exists A query filter condition or sort order for a property also implies a condition that the entity have a value for the property. A datastore entity is not required to have a value for a property that other entities of the same kind have. A filter on a property can only match an entity with a value for the property. Entities without a value for a property used in a filter or sort order are omitted from the index built for the query.
  14. When performing query, finds the index, finds the first matching row, returns entities until first not-matching row.
  15. This index can also answer this query.
  16. App Engine will always return either the full entities or the keys only, but never partial entities. The size of the result set is subject to a limit, so you need to be careful not to put too much information in one entity. You can spread info across multiple entities for an object if necessary.
  17. - Needs custom ones because building every possible index would take huge amount of space/time, and an app won&apos;t use most of them, and more indexes means slower entity updates. - These queries require custom indexes: query with multiple sort orders, query with inequality filter on a property
  18. Needs custom ones because building every possible index would take huge amount of space/time, and an app won&apos;t use most of them, and more indexes means slower entity updates. - These queries require custom indexes: queries with multiple sort orders queries with a sort order on keys in descending order queries with one or more inequality filters on a property and one or more equality filters over other properties queries with inequality filters and ancestor filters
  19. Inequality Filters Are Allowed On One Property Only A query may only use inequality filters (&lt;, &lt;=, &gt;=, &gt;, !=) on one property across all of its filters. This makes geo queries difficult as they typically compare lat and lon in same query.
  20. Properties In Inequality Filters Must Be Sorted Before Other Sort Orders If a query has both a filter with an inequality comparison and one or more sort orders, the query must include a sort order for the property used in the inequality, and the sort order must appear  before  sort orders on other properties. This query is  not  valid, because it uses an inequality filter and does not order by the filtered property: SELECT * FROM Person WHERE birth_year &gt;= :min_year ORDER BY last_name # ERROR Similarly, this query is not valid because it does not order by the filtered property before ordering by other properties: SELECT * FROM Person WHERE birth_year &gt;= :min_year ORDER BY last_name, birth_year # ERROR This query is valid: SELECT * FROM Person WHERE birth_year &gt;= :min_year ORDER BY birth_year, last_name To get all results that match an inequality filter, a query scans the index table for the first matching row, then returns all consecutive results until it finds a row that doesn&apos;t match. For the consecutive rows to represent the complete result set, the rows must be ordered by the inequality filter before other sort orders.
  21. You can specify an offset, but it is slow. It will still have to go to the first result, then count until it gets the one you want. Limit of 1000 as offset. For large datastore sets, this is not a good way to paginate.
  22. Query cursors allow an app to perform a query and retrieve a batch of results, then fetch additional results for the same query in a subsequent web request without the overhead of a query offset. After the app fetches some results for a query, it can ask for an encoded string that represents the location in the result set after the last result fetched (the &amp;quot;cursor&amp;quot;). The app can use the cursor to fetch additional results starting from that point at a later time. A cursor is a base64-encoded string that represents the next starting position of a query after a fetch operation. The app can store the cursor in the datastore or memcache, or in a task queue task payload. A future request handler can perform the same query and include the cursor with the query to tell the datastore to start returning results from the location represented by the cursor. A cursor can only be used by the app that performed the original query, and can only be used to continue the same query.
  23. Query cursors allow an app to perform a query and retrieve a batch of results, then fetch additional results for the same query in a subsequent web request without the overhead of a query offset. After the app fetches some results for a query, it can ask for an encoded string that represents the location in the result set after the last result fetched (the &amp;quot;cursor&amp;quot;). The app can use the cursor to fetch additional results starting from that point at a later time. A cursor is a base64-encoded string that represents the next starting position of a query after a fetch operation. The app can store the cursor in the datastore or memcache, or in a task queue task payload. A future request handler can perform the same query and include the cursor with the query to tell the datastore to start returning results from the location represented by the cursor. A cursor can only be used by the app that performed the original query, and can only be used to continue the same query.
  24. Query cursors allow an app to perform a query and retrieve a batch of results, then fetch additional results for the same query in a subsequent web request without the overhead of a query offset. After the app fetches some results for a query, it can ask for an encoded string that represents the location in the result set after the last result fetched (the &amp;quot;cursor&amp;quot;). The app can use the cursor to fetch additional results starting from that point at a later time. A cursor is a base64-encoded string that represents the next starting position of a query after a fetch operation. The app can store the cursor in the datastore or memcache, or in a task queue task payload. A future request handler can perform the same query and include the cursor with the query to tell the datastore to start returning results from the location represented by the cursor. A cursor can only be used by the app that performed the original query, and can only be used to continue the same query.
  25. Query cursors allow an app to perform a query and retrieve a batch of results, then fetch additional results for the same query in a subsequent web request without the overhead of a query offset. After the app fetches some results for a query, it can ask for an encoded string that represents the location in the result set after the last result fetched (the &amp;quot;cursor&amp;quot;). The app can use the cursor to fetch additional results starting from that point at a later time. A cursor is a base64-encoded string that represents the next starting position of a query after a fetch operation. The app can store the cursor in the datastore or memcache, or in a task queue task payload. A future request handler can perform the same query and include the cursor with the query to tell the datastore to start returning results from the location represented by the cursor. A cursor can only be used by the app that performed the original query, and can only be used to continue the same query.
  26. http://blog.notdot.net/2010/04/High-concurrency-counters-without-sharding We could do sharding, but that&apos;s a lot of work, and it takes time to add up the counter.
  27. ..and search
  28. http://code.google.com/appengine/docs/python/datastore/transactions.html http://www.google.com/codesearch/p?hl=en#e-58ZvqBkF0/trunk/samples-gallery/projectgallery.py&amp;q=rated_index%20lang:py%20appengine&amp;sa=N&amp;cd=2&amp;ct=rc http://google-wave-resources.googlecode.com/svn/trunk/samples-gallery/db_models.py
  29. App Engine&apos;s motto is that its easy to build, easy to maintain, and easy to scale. I think you may find that parts of your app are harder to build on App Engine than on other platforms, because you&apos;re not used to doing things the scalable App Engine way, and you&apos;ll have to do some rethinking. But, once you do start thinking that way, and perhaps also experiment with other scalable platforms, you should find it easier and easier. App Engine is also continuing to come out with features to enable developers to do more in their webapps, and also to make webapps for different audiences. So, try it out, see how you like it, and keep it in mind for your next project.