PHPackages                             tomwalder/php-appengine-search - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. tomwalder/php-appengine-search

ActiveLibrary[Search &amp; Filtering](/categories/search)

tomwalder/php-appengine-search
==============================

Google App Engine Search Library for PHP

v0.0.4-alpha(9y ago)242.2k4[6 issues](https://github.com/tomwalder/php-appengine-search/issues)Apache-2.0PHPPHP &gt;=5.5.0

Since Aug 27Pushed 9y ago2 watchersCompare

[ Source](https://github.com/tomwalder/php-appengine-search)[ Packagist](https://packagist.org/packages/tomwalder/php-appengine-search)[ Docs](https://github.com/tomwalder/php-appengine-search)[ RSS](/packages/tomwalder-php-appengine-search/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (4)Dependencies (3)Versions (5)Used By (0)

[![Build Status](https://camo.githubusercontent.com/7da8ec4917f96f22b10ac42fd15da3049ef006f861b2d6454f21b127b6b00807/68747470733a2f2f6170692e7472617669732d63692e6f72672f746f6d77616c6465722f7068702d617070656e67696e652d7365617263682e737667)](https://travis-ci.org/tomwalder/php-appengine-search)[![Coverage Status](https://camo.githubusercontent.com/a4980b5731bc211bf7f79318da5fe21f6eac7cea3b526c8a6632f9b89e6d97e3/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f746f6d77616c6465722f7068702d617070656e67696e652d7365617263682f62616467652e737667)](https://coveralls.io/r/tomwalder/php-appengine-search)

Full Text Search for PHP on Google App Engine
=============================================

[](#full-text-search-for-php-on-google-app-engine)

This library provides native PHP access to the Google App Engine Search API.

At the time of writing there is no off-the-shelf way to access the Google App Engine full text search API from the PHP runtime.

Generally this means developers cannot access the service without using [Python/Java/Go proxy modules](https://github.com/tomwalder/phpne14-text-search) - which adds complexity, another language, additional potential points of failure and performance impact.

**ALPHA** This library is in the very early stages of development. Do not use it in production. It will change.

Table of Contents
-----------------

[](#table-of-contents)

- [Examples](#examples)
- [Install with Composer](#install-with-composer)
- [Queries](#queries)
- [Geo Queries](#distance-from)
- [Autocomplete](#autocomplete)
- [Creating Documents](#creating-documents) - includes location (Geopoint) and Dates
- [Facets](#facets)
- [Deleting Documents](#deleting-documents)
- [Local Development](#local-development-environment)
- [Best Practice, Free Quotas, Costs](#best-practice-free-quotas-costs)
- [Google Software](#google-software)

Examples
--------

[](#examples)

I find examples a great way to decide if I want to even try out a library, so here's a couple for you.

```
// Schema describing a book
$obj_schema = (new \Search\Schema())
    ->addText('title')
    ->addText('author')
    ->addAtom('isbn')
    ->addNumber('price');

// Create and populate a document
$obj_book = $obj_schema->createDocument([
    'title' => 'The Merchant of Venice',
    'author' => 'William Shakespeare',
    'isbn' => '1840224312',
    'price' => 11.99
]);

// Write it to the Index
$obj_index = new \Search\Index('library');
$obj_index->put($obj_book);
```

In this example, I've used the [Alternative Array Syntax](#alternative-array-syntax) for creating Documents - but you can also do it like this:

```
$obj_book = $obj_schema->createDocument();
$obj_book->title = 'Romeo and Juliet';
$obj_book->author = 'William Shakespeare';
$obj_book->isbn = '1840224339';
$obj_book->price = 9.99;
```

Now let's do a simple search and display the output

```
$obj_index = new \Search\Index('library');
$obj_response = $obj_index->search('romeo');
foreach($obj_response->results as $obj_result) {
    echo "Title: {$obj_result->doc->title}, ISBN: {$obj_result->doc->isbn} ", PHP_EOL;
}
```

### Demo Application

[](#demo-application)

Search pubs!

Application:

Code:

Getting Started
---------------

[](#getting-started)

### Install with Composer

[](#install-with-composer)

To install using Composer, use this require line in your `composer.json` for bleeding-edge features, dev-master

`"tomwalder/php-appengine-search": "v0.0.4-alpha"`

Or, if you're using the command line:

`composer require tomwalder/php-appengine-search`

You may need `minimum-stability: dev`

Queries
=======

[](#queries)

You can supply a simple query string to `Index::search`

```
$obj_index->search('romeo');
```

For more control and options, you can supply a `Query` object

```
$obj_query = (new \Search\Query($str_query))
   ->fields(['isbn', 'price'])
   ->limit(10)
   ->sort('price');
$obj_response = $obj_index->search($obj_query);
```

Query Strings
-------------

[](#query-strings)

Some simple, valid query strings:

- `price:2.99`
- `romeo`
- `dob:2015-01-01`
- `dob < 2000-01-01`
- `tom AND age:36`

For *much* more information, see the Python reference docs: [https://cloud.google.com/appengine/docs/python/search/query\_strings](https://cloud.google.com/appengine/docs/python/search/query_strings)

Sorting
-------

[](#sorting)

```
$obj_query->sort('price');
```

```
$obj_query->sort('price', Query::ASC);
```

Limits &amp; Offsets
--------------------

[](#limits--offsets)

```
$obj_query->limit(10);
```

```
$obj_query->offset(5);
```

Return Fields
-------------

[](#return-fields)

```
$obj_query->fields(['isbn', 'price']);
```

Expressions
-----------

[](#expressions)

The library supports requesting arbitrary expressions in the results.

```
$obj_query->expression('euros', 'gbp * 1.45']);
```

These can be accessed from the `Document::getExpression()` method on the resulting documents, like this:

```
$obj_doc->getExpression('euros');
```

Get Document by ID
------------------

[](#get-document-by-id)

You can fetch a single document from an index directly, by it's unique Doc ID:

```
$obj_index->get('some-document-id-here');
```

Scoring
-------

[](#scoring)

You can enable the MatchScorer by calling the `Query::score` method.

If you do this, each document in the result set will be scored by the Search API "according to search term frequency" - Google.

Without it, documents will all have a score of 0.

```
$obj_query->score();
```

And the results...

```
foreach($obj_response->results as $obj_result) {
    echo $obj_result->score, ''; // Score will be a float
}
```

### Multiple Sorts and Scoring

[](#multiple-sorts-and-scoring)

If you apply `score()` and `sort()` you may be wasting cycles and costing money. Only score documents when you intend to sort by score.

If you need to mix sorting of score and another field, you can use the magic field name `_score` like this - here we sort by price then score, so records with the same price are sorted by their score.

```
$obj_query->score()->sort('price')->sort('_score');
```

Helper Queries &amp; Tools
==========================

[](#helper-queries--tools)

Distance From
-------------

[](#distance-from)

A common use case is searching for documents that have a Geopoint field, based on their distance from a known Geopoint. e.g. "Find pubs near me"

There is a helper method to do this for you, and it also returns the distance in meters in the response.

```
$obj_query->sortByDistance('location', [53.4653381,-2.2483717]);
```

This will return results, nearest first to the supplied Lat/Lon, and there will be an expression returned for the distance itself - prefixed with `distance_from_`:

```
$obj_result->doc->getExpression('distance_from_location');
```

Autocomplete
------------

[](#autocomplete)

Autocomplete is one of the most desired and useful features of a search solution.

This can be implemented fairly easily with the Google App Engine Search API, **with a little slight of hand!**

The Search API does not natively support "edge n-gram" tokenisation (which is what we need for autocomplete!).

So, you can do this with the library - when creating documents, set a second text field with the output from the included `Tokenizer::edgeNGram` function

```
$obj_tkzr = new \Search\Tokenizer();
$obj_schema->createDocument([
    'name' => $str_name,
    'name_ngram' => $obj_tkzr->edgeNGram($str_name),
]);
```

Then you can run autocomplete queries easily like this:

```
$obj_response = $obj_index->search((new \Search\Query('name_ngram:' . $str_query)));
```

You can see a full demo application using this in my "pub search" demo app

Creating Documents
==================

[](#creating-documents)

Schemas &amp; Field Types
-------------------------

[](#schemas--field-types)

As per the Python docs, the available field types are

- **Atom** - an indivisible character string
- **Text** - a plain text string that can be searched word by word
- **HTML** - a string that contains HTML markup tags, only the text outside the markup tags can be searched
- **Number** - a floating point number
- **Date** - a date with year/month/day and optional time
- **Geopoint** - latitude and longitude coordinates

### Dates

[](#dates)

We support `DateTime` objects or date strings in the format `YYYY-MM-DD` (PHP `date('Y-m-d')`)

```
$obj_person_schema = (new \Search\Schema())
    ->addText('name')
    ->addDate('dob');

$obj_person = $obj_person_schema->createDocument([
    'name' => 'Marty McFly',
    'dob' => new DateTime()
]);
```

### Geopoints - Location Data

[](#geopoints---location-data)

Create an entry with a Geopoint field

```
$obj_pub_schema = (new \Search\Schema())
    ->addText('name')
    ->addGeopoint('where')
    ->addNumber('rating');

$obj_pub = $obj_pub_schema->createDocument([
    'name' => 'Kim by the Sea',
    'where' => [53.4653381, -2.2483717],
    'rating' => 3
]);
```

Batch Inserts
-------------

[](#batch-inserts)

It's more efficient to insert in batches if you have multiple documents. Up to 200 documents can be inserted at once.

Just pass an array of Document objects into the `Index::put()` method, like this:

```
$obj_index = new \Search\Index('library');
$obj_index->put([$obj_book1, $obj_book2, $obj_book3]);
```

Alternative Array Syntax
------------------------

[](#alternative-array-syntax)

There is an alternative to directly constructing a new `Search\Document` and setting it's member data, which is to use the `Search\Schema::createDocument` factory method as follows.

```
$obj_book = $obj_schema->createDocument([
    'title' => 'The Merchant of Venice',
    'author' => 'William Shakespeare',
    'isbn' => '1840224312',
    'price' => 11.99
]);
```

Namespaces
----------

[](#namespaces)

You can set a namespace when constructing an index. This will allow you to support multi-tenant applications.

```
$obj_index = new \Search\Index('library', 'client1');
```

Facets
======

[](#facets)

The Search API supports 2 types of document facets for categorisation, ATOM and NUMBER.

ATOM are probably the ones you are most familiar with, and result sets will include counts per unique facet, kind of like this:

For shirt sizes

- small (9)
- medium (37)

Adding Facets to a Document
---------------------------

[](#adding-facets-to-a-document)

```
$obj_doc->atomFacet('size', 'small');
$obj_doc->atomFacet('colour', 'blue');
```

Getting Facets in Results
-------------------------

[](#getting-facets-in-results)

```
$obj_query->facets();
```

Deleting Documents
==================

[](#deleting-documents)

You can delete documents by calling the `Index::delete()` method.

It support one or more `Document` objects - or one or more Document ID strings - or a mixture of objects and ID strings!

```
$obj_index = new \Search\Index('library');
$obj_index->delete('some-document-id');
$obj_index->delete([$obj_doc1, $obj_doc2]);
$obj_index->delete([$obj_doc3, 'another-document-id']);
```

Local Development Environment
=============================

[](#local-development-environment)

The Search API is supported locally, because it's included to support the Python, Java and Go App Engine runtimes.

Best Practice, Free Quotas, Costs
=================================

[](#best-practice-free-quotas-costs)

Like most App Engine services, search is free... up to a point!

- [Free quota information](https://cloud.google.com/appengine/docs/quotas?hl=en#search)
- [Search API pricing](https://cloud.google.com/appengine/pricing#search_pricing)

And some best practice that is most certainly worth a read

- [Best practice for the Google App Engine Search API](https://cloud.google.com/appengine/docs/python/search/best_practices)

Google Software
===============

[](#google-software)

I've had to include 2 files from Google to make this work - they are the Protocol Buffer implementations for the Search API. You will find them in the `/libs` folder.

They are also available directly from the following repository:

These 2 files are Copyright 2007 Google Inc.

As and when they make it into the actual live PHP runtime, I will remove them from here.

Thank you to @sjlangley for the assist.

Other App Engine Software
=========================

[](#other-app-engine-software)

If you've enjoyed this, you might be interested in my [Google Cloud Datastore Library for PHP, PHP-GDS](https://github.com/tomwalder/php-gds)

###  Health Score

27

—

LowBetter than 49% of packages

Maintenance15

Infrequent updates — may be unmaintained

Popularity27

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~191 days

Total

4

Last Release

3343d ago

PHP version history (2 changes)v0.0.1-alphaPHP &gt;=5.4.0

v0.0.4-alphaPHP &gt;=5.5.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/e4e0f7bb9ed3cd1e6c7d0068cf6d446cab11ec0908a0328e807a3db9a14f7e63?d=identicon)[tomwalder](/maintainers/tomwalder)

---

Top Contributors

[![tomwalder](https://avatars.githubusercontent.com/u/1258131?v=4)](https://github.com/tomwalder "tomwalder (1 commits)")

---

Tags

searchgoogleappenginegaefull-text

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tomwalder-php-appengine-search/health.svg)

```
[![Health](https://phpackages.com/badges/tomwalder-php-appengine-search/health.svg)](https://phpackages.com/packages/tomwalder-php-appengine-search)
```

###  Alternatives

[tomwalder/php-gds

Google Cloud Datastore Library for PHP. Also Firestore in Datastore mode.

161174.0k5](/packages/tomwalder-php-gds)[jan-drda/laravel-google-custom-search-engine

Laravel package to get Google Custom Search results from Google Custom Search Engine API for both free and paid version.

5036.7k](/packages/jan-drda-laravel-google-custom-search-engine)[imarc/google-site-search

A PHP Interface to the Google Custom Search API

3110.9k](/packages/imarc-google-site-search)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
