123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- // Copyright 2015 Google Inc. All rights reserved.
- // Use of this source code is governed by the Apache 2.0
- // license that can be found in the LICENSE file.
- /*
- Package search provides a client for App Engine's search service.
- Basic Operations
- Indexes contain documents. Each index is identified by its name: a
- human-readable ASCII string.
- Within an index, documents are associated with an ID, which is also
- a human-readable ASCII string. A document's contents are a mapping from
- case-sensitive field names to values. Valid types for field values are:
- - string,
- - search.Atom,
- - search.HTML,
- - time.Time (stored with millisecond precision),
- - float64 (value between -2,147,483,647 and 2,147,483,647 inclusive),
- - appengine.GeoPoint.
- The Get and Put methods on an Index load and save a document.
- A document's contents are typically represented by a struct pointer.
- Example code:
- type Doc struct {
- Author string
- Comment string
- Creation time.Time
- }
- index, err := search.Open("comments")
- if err != nil {
- return err
- }
- newID, err := index.Put(ctx, "", &Doc{
- Author: "gopher",
- Comment: "the truth of the matter",
- Creation: time.Now(),
- })
- if err != nil {
- return err
- }
- A single document can be retrieved by its ID. Pass a destination struct
- to Get to hold the resulting document.
- var doc Doc
- err := index.Get(ctx, id, &doc)
- if err != nil {
- return err
- }
- Search and Listing Documents
- Indexes have two methods for retrieving multiple documents at once: Search and
- List.
- Searching an index for a query will result in an iterator. As with an iterator
- from package datastore, pass a destination struct to Next to decode the next
- result. Next will return Done when the iterator is exhausted.
- for t := index.Search(ctx, "Comment:truth", nil); ; {
- var doc Doc
- id, err := t.Next(&doc)
- if err == search.Done {
- break
- }
- if err != nil {
- return err
- }
- fmt.Fprintf(w, "%s -> %#v\n", id, doc)
- }
- Search takes a string query to determine which documents to return. The query
- can be simple, such as a single word to match, or complex. The query
- language is described at
- https://cloud.google.com/appengine/docs/standard/go/search/query_strings
- Search also takes an optional SearchOptions struct which gives much more
- control over how results are calculated and returned.
- Call List to iterate over all documents in an index.
- for t := index.List(ctx, nil); ; {
- var doc Doc
- id, err := t.Next(&doc)
- if err == search.Done {
- break
- }
- if err != nil {
- return err
- }
- fmt.Fprintf(w, "%s -> %#v\n", id, doc)
- }
- Fields and Facets
- A document's contents can be represented by a variety of types. These are
- typically struct pointers, but they can also be represented by any type
- implementing the FieldLoadSaver interface. The FieldLoadSaver allows metadata
- to be set for the document with the DocumentMetadata type. Struct pointers are
- more strongly typed and are easier to use; FieldLoadSavers are more flexible.
- A document's contents can be expressed in two ways: fields and facets.
- Fields are the most common way of providing content for documents. Fields can
- store data in multiple types and can be matched in searches using query
- strings.
- Facets provide a way to attach categorical information to a document. The only
- valid types for facets are search.Atom and float64. Facets allow search
- results to contain summaries of the categories matched in a search, and to
- restrict searches to only match against specific categories.
- By default, for struct pointers, all of the struct fields are used as document
- fields, and the field name used is the same as on the struct (and hence must
- start with an upper case letter). Struct fields may have a
- `search:"name,options"` tag. The name must start with a letter and be
- composed only of word characters. A "-" tag name means that the field will be
- ignored. If options is "facet" then the struct field will be used as a
- document facet. If options is "" then the comma may be omitted. There are no
- other recognized options.
- Example code:
- // A and B are renamed to a and b.
- // A, C and I are facets.
- // D's tag is equivalent to having no tag at all (E).
- // F and G are ignored entirely by the search package.
- // I has tag information for both the search and json packages.
- type TaggedStruct struct {
- A float64 `search:"a,facet"`
- B float64 `search:"b"`
- C float64 `search:",facet"`
- D float64 `search:""`
- E float64
- F float64 `search:"-"`
- G float64 `search:"-,facet"`
- I float64 `search:",facet" json:"i"`
- }
- The FieldLoadSaver Interface
- A document's contents can also be represented by any type that implements the
- FieldLoadSaver interface. This type may be a struct pointer, but it
- does not have to be. The search package will call Load when loading the
- document's contents, and Save when saving them. In addition to a slice of
- Fields, the Load and Save methods also use the DocumentMetadata type to
- provide additional information about a document (such as its Rank, or set of
- Facets). Possible uses for this interface include deriving non-stored fields,
- verifying fields or setting specific languages for string and HTML fields.
- Example code:
- type CustomFieldsExample struct {
- // Item's title and which language it is in.
- Title string
- Lang string
- // Mass, in grams.
- Mass int
- }
- func (x *CustomFieldsExample) Load(fields []search.Field, meta *search.DocumentMetadata) error {
- // Load the title field, failing if any other field is found.
- for _, f := range fields {
- if f.Name != "title" {
- return fmt.Errorf("unknown field %q", f.Name)
- }
- s, ok := f.Value.(string)
- if !ok {
- return fmt.Errorf("unsupported type %T for field %q", f.Value, f.Name)
- }
- x.Title = s
- x.Lang = f.Language
- }
- // Load the mass facet, failing if any other facet is found.
- for _, f := range meta.Facets {
- if f.Name != "mass" {
- return fmt.Errorf("unknown facet %q", f.Name)
- }
- m, ok := f.Value.(float64)
- if !ok {
- return fmt.Errorf("unsupported type %T for facet %q", f.Value, f.Name)
- }
- x.Mass = int(m)
- }
- return nil
- }
- func (x *CustomFieldsExample) Save() ([]search.Field, *search.DocumentMetadata, error) {
- fields := []search.Field{
- {Name: "title", Value: x.Title, Language: x.Lang},
- }
- meta := &search.DocumentMetadata{
- Facets: {
- {Name: "mass", Value: float64(x.Mass)},
- },
- }
- return fields, meta, nil
- }
- */
- package search
|