Add Google-like searches to your application
What you'll learn: how to use the Search API and add full-text search to your own applications
Get an Index (Python):
from google.appengine.api import search index = search.Index(name=‘productsearch1’) ... index = search.Index(name=‘yourindex’, namespace=‘yournamespace’)
Get an Index (Java):
import com.google.appengine.api.search.SearchServiceFactory; Index index = SearchServiceFactory.getSearchService() .getIndex(IndexSpec.newBuilder().setName("productsearch1")); ... Index index = SearchServiceFactory.getSearchService("yournamespace") .getIndex(IndexSpec.newBuilder().setName("yourindex"));
Document
to an index.TextField
AtomField
NumberField
DateField
All product documents share some core fields.
Each category has some additional fields.
from google.appengine.api import search fields = [ search.TextField(name=PID, value=pid), search.DateField(name=UPDATED, value=datetime.datetime.now().date()), search.TextField(name=PRODUCT_NAME, value=name), search.TextField(name=DESCRIPTION, value=description), search.AtomField(name=CATEGORY, value=category), search.NumberField(name=AVG_RATING, value=0.0), search.NumberField(name=PRICE, value=price) ] doc = search.Document(doc_id=product_id, fields=fields)
import com.google.appengine.api.search.Document; import com.google.appengine.api.search.Field; Document doc = Document.newBuilder(). .setId(productId) .addField(Field.newBuilder().setName(PID).setText(pid)) .addField(Field.newBuilder().setName(UPDATED).setDate(Field.date(date)) .addField(Field.newBuilder().setName(PRODUCT_NAME).setText(name)) .addField(Field.newBuilder().setName(DESCRIPTION).setText(description)) .addField(Field.newBuilder().setName(CATEGORY).setAtom(category)) .addField(Field.newBuilder().setName(AVG_RATING).setNumber(0.0)) .addField(Field.newBuilder().setName(PRICE).setNumber(price)) .build();
from google.appengine.api import search ... doc = search.Document(doc_id=product_id, fields=fields) try: add_result = search.Index(name=INDEX_NAME).add(doc) doc_id = add_result[0].id except search.Error: # ...
import com.google.appengine.api.search.AddResponse; import com.google.appengine.api.search.Document; import com.google.appengine.api.search.StatusCode; ... Document doc = Document.newBuilder() .setId(productId) .addField(...) .build(); try { AddResponse response = index.add(doc); String docId = response.getIds().get(0); } catch (AddException e) { if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())) { // retry adding document } }
from google.appengine.api import search query = "writings collection" index = search.Index(INDEX_NAME) try: search_results = index.search(query) for doc in search_results: # process doc ... except search.Error: # ...
import com.google.appengine.api.search.Index; import com.google.appengine.api.search.Results; import com.google.appengine.api.search.ScoredDocument; import com.google.appengine.api.search.SearchException; import com.google.appengine.api.search.SearchServiceFactory; String query = "writings collection"; Index index = SearchServiceFactory.getSearchService() .getIndex(IndexSpec.newBuilder().setName("productsearch1")); try { Results<ScoredDocument> results = index.search(query); for (ScoredDocument doc : results) { // process doc ... } } catch (SearchException e) { if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())) { // retry } }
from google.appengine.api import search index = search.Index(INDEX_NAME) user_query = "writings collection" query = search.Query( query_string=user_query, options=search.QueryOptions(limit=doc_limit)) search_results = index.search(query)
import com.google.appengine.api.search.Index; import com.google.appengine.api.search.Query; import com.google.appengine.api.search.QueryOptions; import com.google.appengine.api.search.Results; import com.google.appengine.api.search.ScoredDocument; import com.google.appengine.api.search.SearchException; import com.google.appengine.api.search.SearchServiceFactory; Index index = SearchServiceFactory.getSearchService() .getIndex(IndexSpec.newBuilder().setName(INDEX_NAME)); String userQuery = "writings collection"; Query query = Query.newBuilder() .setOptions(QueryOptions.newBuilder().setLimit(docLimit)) .build(userQuery); Results<ScoredDocument> results = index.search(query);
index = search.Index(INDEX_NAME) try: search_results = index.search(query) returned_count = len(search_results.results) number_found = search_results.number_found for doc in search_results: doc_id = doc.doc_id fields = doc.fields # etc. except search.Error: # ...
Index index = ... try { Results<ScoredDocument> results = index.search(query); int numberReturned = results.getNumberReturned(); long numberFound = results.getNumberFound(); for (ScoredDocument doc : results) { String docId = doc.getId(); Iterable<Field> fields = doc.getFields(); // etc. } } catch (SearchException e) { // ... }
query = search.Query( query_string=user_query, options=search.QueryOptions( limit=doc_limit, offset=offsetval, sort_options=sortopts, snippeted_fields=[DESCRIPTION], returned_expressions=[ search.FieldExpression(name='adjusted_price', expression='price * 1.08')], returned_fields=[PID, DESCRIPTION, CATEGORY, AVG_RATING, PRICE, PRODUCT_NAME] ))
String userQuery = ...; SortOptions sortOpts = ...; Query query = Query.newBuilder() setOptions(QueryOptions.newBuilder() .setLimit(docLimit) .setOffset(offsetVal) .setSortOptions(sortOpts) .setFieldsToSnippet(DESCRIPTION) .addExpressionToReturn( FieldExpression.newBuilder() .setName("adjusted_price") .setExpression("price * 1.08") .setFieldsToReturn(PID, DESCRIPTION, CATEGORY, AVG_RATING, PRICE, PRODUCT_NAME)) .build(userQuery);
search.QueryOptions( limit=doc_limit, offset=offsetval, ...)
returned_count = len(search_results.results) number_found = search_results.number_found
QueryOptions.newBuilder() .setLimit(docLimit) .setOffset(offsetVal) ... build(); int returnedCount = results.getNumberReturned() long numberFound = results.getNumberFound();
search.QueryOptions( snippeted_fields=[DESCRIPTION], ...) ... for doc in search_results: ... for expr in doc.expressions: if expr.name == DESCRIPTION: description_snippet = expr.value break # do something with the document ...
QueryOptions.newBuilder() .setFieldsToSnippet(DESCRIPTION) ... .build(); ... for (ScoredDocument doc : results) { ... String descriptionSnippet; for (Field expr : doc.getExpressions()) { if (DESCRIPTION.equals(expr.getName())) { descriptionSnippet = expr.getText(); break; } } // do something with the document ... }
search.QueryOptions( returned_expressions=[ search.FieldExpression(name='adjusted_price', expression='price * 1.08')], ...) ... for doc in search_results: ... for expr in doc.expressions: if expr.name == 'adjusted_price': price = expr.value # do something with the document ...
QueryOptions.newBuilder() .addExpressionToReturn( FieldExpression.newBuilder().setName("adjusted_price").setExpression("price * 1.08'")) ... build(); ... for (ScoredDocument doc : results) { ... Double price; for (Field expr : doc.getExpressions()) { if ("adjusted_price".equals(expr.getName())) { price = expr.getNumber(); break; } } // do something with the document ... }
from google.appengine.api import search search.QueryOptions( sort_options=search.SortOptions(...) ...)
import com.google.appengine.api.search.QueryOptions; import com.google.appengine.api.search.SortOptions; QueryOptions.newBuilder() .setSortOptions(SortOptions.newBuilder()...) .build();
search.QueryOptions( sort_options=search.SortOptions( match_scorer=search.MatchScorer()) ...)
QueryOptions.newBuilder() .setSortOptions(SortOptions.newBuilder() .setMatchScorer(MatchScorer.newBuilder())) ... .build();
search.SortExpression(expression='price', direction=search.SortExpression.ASCENDING, default_value=9999) search.SortExpression(expression='name', direction=search.SortExpression.ASCENDING, default_value='zzz') search.SortExpression(expression=’ar’, default_value=0)
SortExpression.newBuilder() .setExpression("price") .setDirection(SortExpression.SortDirection.ASCENDING) .setDefaultValueNumeric(9999.0) .build(); SortExpression.newBuilder() .setExpression("name") .setDirection(SortExpression.SortDirection.ASCENDING) .setDefaultValue("zzz") .build(); SortExpression.newBuilder() .setExpression("ar") .setDefaultValueNumeric(0.0) .build();
sortopts = search.SortOptions(expressions=[ search.SortExpression(expression=PRICE, direction=search.SortExpression.ASCENDING, default_value=9999)]) search.QueryOptions( sort_options=sortopts ...)
SortOptions sortOpts = SortOptions.newBuilder() .addSortExpression(SortExpression.newBuilder() .setExpression(PRICE) .setDirection(SortExpression.SortDirection.ASCENDING) .setDefaultValueNumeric(9999.0)) .build(); QueryOptions.newBuilder() .setSortOptions(sortOpts) ... .build();
search.SortExpression(expression='ar + _score', default_value=0)
If you access _score
in a sort expression, your SortOptions
object should include a scorer.
search.SortOptions( match_scorer=search.MatchScorer(), expressions=[search.SortExpression(...),...])
SortExpression sortExpr = SortExpression.newBuilder() .setExpression("ar + _score") .setDefaultValueNumeric(0.0) .build();
If you access _score
in a sort expression, your SortOptions
object should include a scorer.
SortOptions.newBuilder() .setMatchScorer(MatchScorer.newBuilder()) .addSortExpression(sortExpr) ... .build();
from google.appengine.api import search geopoint = search.GeoPoint(-33.857, 151.215) fields = [search.TextField(name=’name’, value=store_name), search.TextField(name=’address’, value=store_address), search.GeoField(name='store_location', value=geopoint)]
import com.google.appengine.api.search.Document; import com.google.appengine.api.search.Field; import com.google.appengine.api.search.GeoPoint; GeoPoint geoPoint = search.GeoPoint(-33.857, 151.215); Document.newBuilder() .addField(Field.newBuilder().setName("name").setText(storeName)) .addField(Field.newBuilder().setName("address").setText(storeAddress)) .addField(Field.newBuilder().setName("store_location").setGeoPoint(geoPoint)) .build();
distance(<field-name>, geopoint(lat, lng)) < distance
from google.appengine.api import search index = search.Index(STORE_INDEX_NAME) query = "distance(store_location, geopoint(-33.857, 151.215)) < 4500" try: search_results = index.search(query) for doc in search_results: # process doc ... except search.Error: # ...
distance(<field-name>, geopoint(lat, lng)) < distance
Index index = SearchServiceFactory.getSearchService() .getIndex(IndexSpec.newBuilder().setName(STORE_INDEX_NAME)); String query = "distance(store_location, geopoint(-33.857, 151.215)) < 4500"; try { Results<ScoredDocument> results = index.search(query); for (ScoredDocument doc : results) { // process doc ... } } catch (SearchException e) { ... }
from google.appengine.api import search index = search.Index(config.STORE_INDEX_NAME) query = "distance(store_location, geopoint(-33.857, 151.215)) < 4500" loc_expr = "distance(store_location, geopoint(-33.857, 151.215))" sort_expr = search.SortExpression( expression=loc_expr, direction=search.SortExpression.ASCENDING, default_value=4501) search_query = search.Query( query_string=query, options=search.QueryOptions(sort_options=search.SortOptions(expressions=[sort_expr]))) results = index.search(search_query)
Index index = SearchServiceFactory.getSearchService() .getIndex(IndexSpec.newBuilder().setName(STORE_INDEX_NAME)); String query = "distance(store_location, geopoint(-33.857, 151.215)) < 4500"; String locExpr = "distance(store_location, geopoint(-33.857, 151.215))"; SortExpression sortExpr = SortExpression.newBuilder() .setExpression(locExpr) .setDirection(SortExpression.SortDirection.ASCENDING) .setDefaultValueNumeric(4501.0) .build(); Query searchQuery = Query.newBuilder() .setOptions(QueryOptions.newBuilder().setSortOptions( SortOptions.newBuilder().addExpression(sortExpr))) .build(query); Results<ScoredDocument> results = index.search(searchQuery);