github.com/altipla-consulting/ravendb-go-client@v0.1.3/readme.old.md (about)

     1  
     2  [![Linux build Status](https://travis-ci.org/ravendb/ravendb-go-client.svg?branch=master)](https://travis-ci.org/ravendb/ravendb-go-client) [![Windows build status](https://ci.appveyor.com/api/projects/status/rf326yoxl1uf444h/branch/master?svg=true)](https://ci.appveyor.com/project/ravendb/ravendb-go-client/branch/master)
     3  
     4  This is information on how to use the library. For docs on working on the library itself see [readme-dev.md](readme-dev.md).
     5  
     6  This library requires go 1.11 or later.
     7  
     8  API reference: https://godoc.org/github.com/altipla-consulting/ravendb-go-client
     9  
    10  This library is in beta state. All the basic functionality works and passes extensive [test suite](/tests), but the API for more esoteric features might change.
    11  
    12  If you encounter bugs, have suggestions or feature requests, please [open an issue](https://github.com/altipla-consulting/ravendb-go-client/issues).
    13  
    14  ## Documentation
    15  
    16  To learn basics of RavenDB, read [RavenDB Documentation](https://ravendb.net/docs/article-page/4.1/csharp) or [Dive into RavenDB](https://demo.ravendb.net/).
    17  
    18  ## Getting started
    19  
    20  Full source code of those examples is in `examples` directory.
    21  
    22  To run a a specific example, e.g. `crudStore`, you can run:
    23  * `.\scripts\run_example.ps1 crudStore` : works on mac / linux if you have powershell installed
    24  * `go run examples\log.go examples\main.go crudStore` : on mac / linux change paths to `examples/log.go` etc.
    25  
    26  1. Import the package
    27  ```go
    28  import (
    29    ravendb "github.com/altipla-consulting/ravendb-go-client"
    30  )
    31  ```
    32  
    33  2. Initialize document store (you should have one DocumentStore instance per application)
    34  ```go
    35  func getDocumentStore(databaseName string) (*ravendb.DocumentStore, error) {
    36    serverNodes := []string{"http://live-test.ravendb.net"}
    37    store := ravendb.NewDocumentStore(serverNodes, databaseName)
    38    if err := store.Initialize(); err != nil {
    39      return nil, err
    40    }
    41    return store, nil
    42  }
    43  ```
    44  
    45  3. Open a session and close it when done
    46  ```go
    47  session, err = store.OpenSession()
    48  if err != nil {
    49    log.Fatalf("store.OpenSession() failed with %s", err)
    50  }
    51  // ... use session
    52  session.Close()
    53  ```
    54  
    55  4. Call `SaveChanges()` to persist changes in a session:
    56  ```go
    57  var e *northwind.Employee
    58  err = session.Load(&e, "employees/7-A")
    59  if err != nil {
    60      log.Fatalf("session.Load() failed with %s\n", err)
    61  }
    62  
    63  origName := e.FirstName
    64  e.FirstName = e.FirstName + "Changed"
    65  err = session.Store(e)
    66  if err != nil {
    67      log.Fatalf("session.Store() failed with %s\n", err)
    68  }
    69  
    70  err = session.SaveChanges()
    71  if err != nil {
    72      log.Fatalf("session.SaveChanges() failed with %s\n", err)
    73  }
    74  
    75  var e2 *northwind.Employee
    76  err = session.Load(&e2, "employees/7-A")
    77  if err != nil {
    78      log.Fatalf("session.Load() failed with %s\n", err)
    79  }
    80  fmt.Printf("Updated Employee.FirstName from '%s' to '%s'\n", origName, e2.FirstName)
    81  ```
    82  See `loadUpdateSave()` in [examples/main.go](examples/main.go) for full example.
    83  
    84  ## CRUD example
    85  
    86  ### Storing documents
    87  ```go
    88  product := &northwind.Product{
    89      Name:         "iPhone X",
    90      PricePerUnit: 999.99,
    91      Category:     "electronis",
    92      ReorderLevel: 15,
    93  }
    94  err = session.Store(product)
    95  if err != nil {
    96      log.Fatalf("session.Store() failed with %s\n", err)
    97  }
    98  ```
    99  See `crudStore()` in [examples/main.go](examples/main.go) for full example.
   100  
   101  
   102  ### Loading documents
   103  
   104  ```go
   105  var e *northwind.Employee
   106  err = session.Load(&e, "employees/7-A")
   107  if err != nil {
   108      log.Fatalf("session.Load() failed with %s\n", err)
   109  }
   110  fmt.Printf("employee: %#v\n", e)
   111  ```
   112  See `crudLoad()` in [examples/main.go](examples/main.go) for full example.
   113  
   114  ### Loading documents with includes
   115  
   116  Some entities point to other entities via id. For example `Employee` has `ReportsTo` field which is an id of `Employee` that it reports to.
   117  
   118  To improve performance by minimizing number of server requests, we can use includes functionality to load such linked entities.
   119  
   120  ```go
   121  // load employee with id "employees/7-A" and entity whose id is ReportsTo
   122  var e *northwind.Employee
   123  err = session.Include("ReportsTo").Load(&e, "employees/5-A")
   124  if err != nil {
   125      log.Fatalf("session.Load() failed with %s\n", err)
   126  }
   127  if e.ReportsTo == "" {
   128      fmt.Printf("Employee with id employees/5-A doesn't report to anyone\n")
   129      return
   130  }
   131  
   132  numRequests := session.GetNumberOfRequests()
   133  var reportsTo *northwind.Employee
   134  err = session.Load(&reportsTo, e.ReportsTo)
   135  if err != nil {
   136      log.Fatalf("session.Load() failed with %s\n", err)
   137  }
   138  if numRequests != session.GetNumberOfRequests() {
   139      fmt.Printf("Something's wrong, this shouldn't send a request to the server\n")
   140  } else {
   141      fmt.Printf("Loading e.ReportsTo employee didn't require a new request to the server because we've loaded it in original requests thanks to using Include functionality\n")
   142  }
   143  ```
   144  See `crudLoadWithInclude()` in [examples/main.go](examples/main.go) for full example.
   145  
   146  ### Updating documents
   147  
   148  ```go
   149  // load entity from the server
   150  var p *northwind.Product
   151  err = session.Load(&p, productID)
   152  if err != nil {
   153      log.Fatalf("session.Load() failed with %s\n", err)
   154  }
   155  
   156  // update price
   157  origPrice = p.PricePerUnit
   158  newPrice = origPrice + 10
   159  p.PricePerUnit = newPrice
   160  err = session.Store(p)
   161  if err != nil {
   162      log.Fatalf("session.Store() failed with %s\n", err)
   163  }
   164  
   165  // persist changes on the server
   166  err = session.SaveChanges()
   167  if err != nil {
   168      log.Fatalf("session.SaveChanges() failed with %s\n", err)
   169  }
   170  ```
   171  See `crudUpdate()` in [examples/main.go](examples/main.go) for full example.
   172  
   173  ### Deleting documents
   174  
   175  Delete using entity:
   176  
   177  ```go
   178  // ... store a product and remember its id in productID
   179  
   180  var p *northwind.Product
   181  err = session.Load(&p, productID)
   182  if err != nil {
   183      log.Fatalf("session.Load() failed with %s\n", err)
   184  }
   185  
   186  err = session.Delete(p)
   187  if err != nil {
   188      log.Fatalf("session.Delete() failed with %s\n", err)
   189  }
   190  
   191  err = session.SaveChanges()
   192  if err != nil {
   193      log.Fatalf("session.SaveChanges() failed with %s\n", err)
   194  }
   195  
   196  ```
   197  See `crudDeleteUsingEntity()` in [examples/main.go](examples/main.go) for full example.
   198  
   199  Entity must be a value that we either stored in the database in the current session via `Store()`
   200  or loaded from database using `Load()`, `LoadMulti()`, query etc.
   201  
   202  Delete using id:
   203  
   204  ```go
   205  // ... store a product and remember its id in productID
   206  
   207  err = session.DeleteByID(productID, "")
   208  if err != nil {
   209      log.Fatalf("session.Delete() failed with %s\n", err)
   210  }
   211  
   212  err = session.SaveChanges()
   213  if err != nil {
   214      log.Fatalf("session.SaveChanges() failed with %s\n", err)
   215  }
   216  ```
   217  Second argument to `DeleteByID` is optional `changeVector`, for fine-grain concurrency control.
   218  
   219  See `crudDeleteUsingID()` in [examples/main.go](examples/main.go) for full example.
   220  
   221  ## Querying documents
   222  
   223  ### Selecting what to query
   224  
   225  First you need to decide what to query.
   226  
   227  RavenDB stores documents in collections. By default each type (struct) is stored in its own collection e.g. all `Employee` structs are stored in `employees` collection.
   228  
   229  You can query by collection name:
   230  
   231  ```go
   232  q := session.QueryCollection("employees")
   233  ```
   234  
   235  See `queryCollectionByName()` in [examples/main.go](examples/main.go) for full example.
   236  
   237  To get a collection name for a given type use `ravendb.GetCollectionNameDefault(&MyStruct{})`.
   238  
   239  You can query a collection for a given type:
   240  
   241  ```go
   242  tp := reflect.TypeOf(&northwind.Employee{})
   243  q := session.QueryCollectionForType(tp)
   244  ```
   245  See `queryCollectionByType()` in [examples/main.go](examples/main.go) for full example.
   246  
   247  You can query an index:
   248  
   249  ```go
   250  q := session.QueryIndex("Orders/ByCompany")
   251  ```
   252  See `queryIndex()` in [examples/main.go](examples/main.go) for full example.
   253  
   254  ### Limit what is returned
   255  
   256  ```go
   257  tp := reflect.TypeOf(&northwind.Product{})
   258  q := session.QueryCollectionForType(tp)
   259  
   260  q = q.WaitForNonStaleResults(0)
   261  q = q.WhereEquals("Name", "iPhone X")
   262  q = q.OrderBy("PricePerUnit")
   263  q = q.Take(2) // limit to 2 results
   264  ```
   265  See `queryComplex()` in [examples/main.go](examples/main.go) for full example.
   266  
   267  ### Obtain the results
   268  
   269  You can get all matching results:
   270  
   271  ```go
   272  var products []*northwind.Product
   273  err = q.GetResults(&products)
   274  ```
   275  See `queryComplex()` in [examples/main.go](examples/main.go) for full example.
   276  
   277  You can get just first one:
   278  ```go
   279  var first *northwind.Employee
   280  err = q.First(&first)
   281  ```
   282  See `queryFirst()` in [examples/main.go](examples/main.go) for full example.
   283  
   284  ## Overview of [DocumentQuery](https://godoc.org/github.com/altipla-consulting/ravendb-go-client#DocumentQuery) methods
   285  
   286  ### SelectFields() - projections using a single field
   287  
   288  ```go
   289  // RQL equivalent: from employees select FirstName
   290  q = q.SelectFields(reflect.TypeOf(""), "FirstName")
   291  
   292  var names []string
   293  err = q.GetResults(&names)
   294  ```
   295  See `querySelectSingleField()` in [examples/main.go](examples/main.go) for full example.
   296  
   297  ### SelectFields() - projections using multiple fields
   298  
   299  ```go
   300  type employeeNameTitle struct {
   301    FirstName string
   302    Title     string
   303  }
   304  
   305  // RQL equivalent: from employees select FirstName, Title
   306  tp := reflect.TypeOf(&northwind.Employee{})
   307  q := session.QueryCollectionForType(tp)
   308  q = q.SelectFields(reflect.TypeOf(&employeeNameTitle{}), "FirstName", "Title")
   309  ```
   310  See `querySelectFields()` in [examples/main.go](examples/main.go) for full example.
   311  
   312  ### Distinct()
   313  
   314  ```go
   315  // RQL equivalent: from employees select distinct Title
   316  tp := reflect.TypeOf(&northwind.Employee{})
   317  q := session.QueryCollectionForType(tp)
   318  q = q.SelectFields(reflect.TypeOf(""), "Title")
   319  q = q.Distinct()
   320  ```
   321  See `queryDistinct()` in [examples/main.go](examples/main.go) for full example.
   322  
   323  ### WhereEquals() / WhereNotEquals()
   324  
   325  ```go
   326  // RQL equivalent: from employees where Title = 'Sales Representative'
   327  tp := reflect.TypeOf(&northwind.Employee{})
   328  q := session.QueryCollectionForType(tp)
   329  q = q.WhereEquals("Title", "Sales Representative")
   330  ```
   331  See `queryEquals()` in [examples/main.go](examples/main.go) for full example.
   332  
   333  ### WhereIn
   334  
   335  ```go
   336  // RQL equivalent: from employees where Title in ['Sales Representative', 'Sales Manager']
   337  tp := reflect.TypeOf(&northwind.Employee{})
   338  q := session.QueryCollectionForType(tp)
   339  q = q.WhereIn("Title", []interface{}{"Sales Representative", "Sales Manager"})
   340  ```
   341  See `queryIn()` in [examples/main.go](examples/main.go) for full example.
   342  
   343  ### WhereStartsWith() / WhereEndsWith()
   344  
   345  ```go
   346  // RQL equivalent:
   347  // from employees where startsWith('Ro')
   348  tp := reflect.TypeOf(&northwind.Employee{})
   349  q := session.QueryCollectionForType(tp)
   350  q = q.WhereStartsWith("FirstName", "Ro")
   351  ```
   352  See `queryStartsWith()` and `queryEndsWith` in [examples/main.go](examples/main.go) for full example.
   353  
   354  ### WhereBetween()
   355  
   356  ```go
   357  // RQL equivalent:
   358  // from orders where Freight between 11 and 13
   359  tp := reflect.TypeOf(&northwind.Order{})
   360  q := session.QueryCollectionForType(tp)
   361  q = q.WhereBetween("Freight", 11, 13)
   362  ```
   363  See `queryBetween()` in [examples/main.go](examples/main.go) for full example.
   364  
   365  ### WhereGreaterThan() / WhereGreaterThanOrEqual() / WhereLessThan() / WhereLessThanOrEqual()
   366  
   367  ```go
   368  // RQL equivalent:
   369  // from orders where Freight Freight > 11
   370  tp := reflect.TypeOf(&northwind.Order{})
   371  q := session.QueryCollectionForType(tp)
   372  // can also be WhereGreaterThanOrEqual(), WhereLessThan(), WhereLessThanOrEqual()
   373  q = q.WhereGreaterThan("Freight", 11)
   374  ```
   375  See `queryGreater()` in [examples/main.go](examples/main.go) for full example.
   376  
   377  ### WhereExists()
   378  
   379  Checks if the field exists.
   380  
   381  ```go
   382  // RQL equivalent:
   383  // from employees where exists ("ReportsTo")
   384  tp := reflect.TypeOf(&northwind.Employee{})
   385  q := session.QueryCollectionForType(tp)
   386  q = q.WhereExists("ReportsTo")
   387  ```
   388  See `queryExists()` in [examples/main.go](examples/main.go) for full example.
   389  
   390  ### ContainsAny() / ContainsAll()
   391  
   392  ```go
   393  // RQL equivalent:
   394  // from employees where FirstName in ("Anne", "Nancy")
   395  tp := reflect.TypeOf(&northwind.Employee{})
   396  q := session.QueryCollectionForType(tp)
   397  q = q.ContainsAny("FirstName", []interface{}{"Anne", "Nancy"})
   398  ```
   399  See `queryContainsAny()` in [examples/main.go](examples/main.go) for full example.
   400  
   401  ### Search()
   402  
   403  Performs full-text search:
   404  
   405  ```go
   406  // RQL equivalent:
   407  // from employees where search(FirstName, 'Anne Nancy')
   408  tp := reflect.TypeOf(&northwind.Employee{})
   409  q := session.QueryCollectionForType(tp)
   410  q = q.Search("FirstName", "Anne Nancy")
   411  ```
   412  See `querySearch()` in [examples/main.go](examples/main.go) for full example.
   413  
   414  ### OpenSubclause() / CloseSubclause()
   415  
   416  ```go
   417  // RQL equivalent:
   418  // from employees where (FirstName = 'Steven') or (Title = 'Sales Representative' and LastName = 'Davolio')
   419  tp := reflect.TypeOf(&northwind.Employee{})
   420  q := session.QueryCollectionForType(tp)
   421  q = q.WhereEquals("FirstName", "Steven")
   422  q = q.OrElse()
   423  q = q.OpenSubclause()
   424  q = q.WhereEquals("Title", "Sales Representative")
   425  q = q.WhereEquals("LastName", "Davolio")
   426  q = q.CloseSubclause()
   427  ```
   428  See `querySubclause()` in [examples/main.go](examples/main.go) for full example.
   429  
   430  ### Not()
   431  
   432  ```go
   433  // RQL equivalent:
   434  // from employees where not FirstName = 'Steven'
   435  tp := reflect.TypeOf(&northwind.Employee{})
   436  q := session.QueryCollectionForType(tp)
   437  q = q.Not()
   438  q = q.WhereEquals("FirstName", "Steven")
   439  ```
   440  See `queryNot()` in [examples/main.go](examples/main.go) for full example.
   441  
   442  ### AndAlso() / OrElse()
   443  
   444  ```go
   445  // RQL equivalent:
   446  // from employees where FirstName = 'Steven' or FirstName  = 'Nancy'
   447  tp := reflect.TypeOf(&northwind.Employee{})
   448  q := session.QueryCollectionForType(tp)
   449  q = q.WhereEquals("FirstName", "Steven")
   450  // can also be AndElse()
   451  q = q.OrElse()
   452  q = q.WhereEquals("FirstName", "Nancy")
   453  ```
   454  See `queryOrElse()` in [examples/main.go](examples/main.go) for full example.
   455  
   456  ### UsingDefaultOperator()
   457  
   458  Sets default operator (which will be used if no `AndAlso()` / `OrElse()` was called. Just after query instantiation, OR is used as default operator. Default operator can be changed only adding any conditions.
   459  
   460  ### OrderBy() / RandomOrdering()
   461  
   462  ```go
   463  // RQL equivalent:
   464  // from employees order by FirstName
   465  tp := reflect.TypeOf(&northwind.Employee{})
   466  q := session.QueryCollectionForType(tp)
   467  // can also be RandomOrdering()
   468  q = q.OrderBy("FirstName")
   469  ```
   470  See `queryOrderBy()` in [examples/main.go](examples/main.go) for full example.
   471  
   472  ### Take()
   473  
   474  ```go
   475  // RQL equivalent:
   476  // from employees order by FirstName desc
   477  tp := reflect.TypeOf(&northwind.Employee{})
   478  q := session.QueryCollectionForType(tp)
   479  q = q.OrderByDescending("FirstName")
   480  q = q.Take(2)
   481  ```
   482  See `queryTake()` in [examples/main.go](examples/main.go) for full example.
   483  
   484  ### Skip()
   485  
   486  ```go
   487  // RQL equivalent:
   488  // from employees order by FirstName desc
   489  tp := reflect.TypeOf(&northwind.Employee{})
   490  q := session.QueryCollectionForType(tp)
   491  q = q.OrderByDescending("FirstName")
   492  q = q.Take(2)
   493  q = q.Skip(1)
   494  ```
   495  See `querySkip()` in [examples/main.go](examples/main.go) for full example.
   496  
   497  ### Getting query statistics
   498  
   499  To obtain query statistics use `Statistics()` method.
   500  
   501  ```go
   502  var stats *ravendb.QueryStatistics
   503  tp := reflect.TypeOf(&northwind.Employee{})
   504  q := session.QueryCollectionForType(tp)
   505  q = q.WhereGreaterThan("FirstName", "Bernard")
   506  q = q.OrderByDescending("FirstName")
   507  q.Statistics(&stats)
   508  ```
   509  Statistics:
   510  ```
   511  Statistics:
   512  {IsStale:           false,
   513   DurationInMs:      0,
   514   TotalResults:      7,
   515   SkippedResults:    0,
   516   Timestamp:         2019-02-13 02:57:31.5226409 +0000 UTC,
   517   IndexName:         "Auto/employees/ByLastNameAndReportsToAndSearch(FirstName)AndTitle",
   518   IndexTimestamp:    2019-02-13 02:57:31.5226409 +0000 UTC,
   519   LastQueryTime:     2019-02-13 03:50:25.7602429 +0000 UTC,
   520   TimingsInMs:       {},
   521   ResultEtag:        7591488513381790088,
   522   ResultSize:        0,
   523   ScoreExplanations: {}}
   524   ```
   525  See `queryStatistics()` in [examples/main.go](examples/main.go) for full example.
   526  
   527  ### GetResults() / First() / Single() / Count()
   528  
   529  `GetResults()` - returns all results
   530  
   531  `First()` - first result
   532  
   533  `Single()` - first result, returns error if there's more entries
   534  
   535  `Count()` - returns the number of the results (not affected by take())
   536  
   537  See `queryFirst()`, `querySingle()` and `queryCount()` in [examples/main.go](examples/main.go) for full example.
   538  
   539  ## Attachments
   540  
   541  ### Store attachments
   542  
   543  ```go
   544  fileStream, err := os.Open(path)
   545  if err != nil {
   546      log.Fatalf("os.Open() failed with '%s'\n", err)
   547  }
   548  defer fileStream.Close()
   549  
   550  fmt.Printf("new employee id: %s\n", e.ID)
   551  err = session.Advanced().Attachments().Store(e, "photo.png", fileStream, "image/png")
   552  
   553  // could also be done using document id
   554  // err = session.Advanced().Attachments().Store(e.ID, "photo.png", fileStream, "image/png")
   555  
   556  if err != nil {
   557      log.Fatalf("session.Advanced().Attachments().Store() failed with '%s'\n", err)
   558  }
   559  
   560  err = session.SaveChanges()
   561  ```
   562  See `storeAttachments()` in [examples/main.go](examples/main.go) for full example.
   563  
   564  ### Get attachments
   565  
   566  ```go
   567  attachment, err := session.Advanced().Attachments().Get(docID, "photo.png")
   568  if err != nil {
   569      log.Fatalf("session.Advanced().Attachments().Get() failed with '%s'\n", err)
   570  }
   571  defer attachment.Close()
   572  fmt.Print("Attachment details:\n")
   573  pretty.Print(attachment.Details)
   574  // read attachment data
   575  // attachment.Data is io.Reader
   576  var attachmentData bytes.Buffer
   577  n, err := io.Copy(&attachmentData, attachment.Data)
   578  if err != nil {
   579      log.Fatalf("io.Copy() failed with '%s'\n", err)
   580  }
   581  fmt.Printf("Attachment size: %d bytes\n", n)
   582  ```
   583  
   584  Attachment details:
   585  ```
   586  {AttachmentName: {Name:        "photo.png",
   587                    Hash:        "MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=",
   588                    ContentType: "image/png",
   589                    Size:        4579},
   590   ChangeVector:   "A:4905-dMAeI9ANZ06DOxCRLnSmNw",
   591   DocumentID:     "employees/44-A"}
   592  Attachment size: 4579 bytes
   593  ```
   594  
   595  See `getAttachments()` in [examples/main.go](examples/main.go) for full example.
   596  
   597  ### Check if attachment exists
   598  
   599  ```go
   600  name := "photo.png"
   601  exists, err := session.Advanced().Attachments().Exists(docID, name)
   602  if err != nil {
   603      log.Fatalf("session.Advanced().Attachments().Exists() failed with '%s'\n", err)
   604  }
   605  ```
   606  See `checkAttachmentExists()` in [examples/main.go](examples/main.go) for full example.
   607  
   608  ### Get attachment names
   609  
   610  ```go
   611  names, err := session.Advanced().Attachments().GetNames(doc)
   612  if err != nil {
   613      log.Fatalf("session.Advanced().Attachments().GetNames() failed with '%s'\n", err)
   614  }
   615  ```
   616  
   617  Attachment names:
   618  ```
   619  [{Name:        "photo.png",
   620    Hash:        "MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=",
   621    ContentType: "image/png",
   622    Size:        4579}]
   623  ```
   624  
   625  See `getAttachmentNames()` in [examples/main.go](examples/main.go) for full example.
   626  
   627  
   628  ## Bulk insert
   629  
   630  When storing multiple documents, use bulk insertion.
   631  
   632  ```go
   633  bulkInsert := store.BulkInsert("")
   634  
   635  names := []string{"Anna", "Maria", "Miguel", "Emanuel", "Dayanara", "Aleida"}
   636  for _, name := range names {
   637      e := &northwind.Employee{
   638          FirstName: name,
   639      }
   640      id, err := bulkInsert.Store(e, nil)
   641      if err != nil {
   642          log.Fatalf("bulkInsert.Store() failed with '%s'\n", err)
   643      }
   644  }
   645  // flush data and finish
   646  err = bulkInsert.Close()
   647  ```
   648  
   649  See `bulkInsert()` in [examples/main.go](examples/main.go) for full example.
   650  
   651  ## Observing changes in the database
   652  
   653  Listen for database changes e.g. document changes.
   654  
   655  ```go
   656  changes := store.Changes("")
   657  
   658  err = changes.EnsureConnectedNow()
   659  if err != nil {
   660      log.Fatalf("changes.EnsureConnectedNow() failed with '%s'\n", err)
   661  }
   662  
   663  cb := func(change *ravendb.DocumentChange) {
   664      fmt.Print("change:\n")
   665      pretty.Print(change)
   666  }
   667  docChangesCancel, err := changes.ForAllDocuments(cb)
   668  if err != nil {
   669      log.Fatalf("changes.ForAllDocuments() failed with '%s'\n", err)
   670  }
   671  
   672  defer docChangesCancel()
   673  
   674  e := &northwind.Employee{
   675      FirstName: "Jon",
   676      LastName:  "Snow",
   677  }
   678  err = session.Store(e)
   679  if err != nil {
   680      log.Fatalf("session.Store() failed with '%s'\n", err)
   681  }
   682  
   683  err = session.SaveChanges()
   684  if err != nil {
   685      log.Fatalf("session.SaveChanges() failed with '%s'\n", err)
   686  }
   687  // cb should now be called notifying there's a new document
   688  ```
   689  
   690  Example change:
   691  ```
   692  {Type:           "Put",
   693   ID:             "Raven/Hilo/employees",
   694   CollectionName: "@hilo",
   695   ChangeVector:   "A:4892-bJERJNLunE+4xQ/yDEuk1Q"}
   696   ```
   697  
   698  See `changes()` in [examples/main.go](examples/main.go) for full example.
   699  
   700  ## Streaming
   701  
   702  Streaming allows interating over documents matching certain criteria.
   703  
   704  It's useful when there's a large number of results as it limits memory
   705  use by reading documents in batches (as opposed to all at once).
   706  
   707  ### Stream documents with ID prefix
   708  
   709  Here we iterate over all documents in `products` collection:
   710  
   711  ```go
   712  args := &ravendb.StartsWithArgs{
   713      StartsWith: "products/",
   714  }
   715  iterator, err := session.Advanced().Stream(args)
   716  if err != nil {
   717      log.Fatalf("session.Advanced().Stream() failed with '%s'\n", err)
   718  }
   719  for {
   720      var p *northwind.Product
   721      streamResult, err := iterator.Next(&p)
   722      if err != nil {
   723          // io.EOF means there are no more results
   724          if err == io.EOF {
   725              err = nil
   726          } else {
   727              log.Fatalf("iterator.Next() failed with '%s'\n", err)
   728          }
   729          break
   730      }
   731      // handle p
   732  }
   733  ```
   734  See `streamWithIDPrefix()` in [examples/main.go](examples/main.go) for full example.
   735  
   736  This returns:
   737  ```
   738  streamResult:
   739  {ID:           "products/1-A",
   740   ChangeVector: "A:96-bJERJNLunE+4xQ/yDEuk1Q",
   741   Metadata:     {},
   742   Document:     ... same as product but as map[string]interface{} ...
   743  
   744  product:
   745  {ID:              "products/1-A",
   746   Name:            "Chai",
   747   Supplier:        "suppliers/1-A",
   748   Category:        "categories/1-A",
   749   QuantityPerUnit: "10 boxes x 20 bags",
   750   PricePerUnit:    18,
   751   UnitsInStock:    1,
   752   UnistsOnOrder:   0,
   753   Discontinued:    false,
   754   ReorderLevel:    10}
   755   ```
   756  
   757  ### Stream query results
   758  
   759  ```go
   760  tp := reflect.TypeOf(&northwind.Product{})
   761  q := session.QueryCollectionForType(tp)
   762  q = q.WhereGreaterThan("PricePerUnit", 15)
   763  q = q.OrderByDescending("PricePerUnit")
   764  
   765  iterator, err := session.Advanced().StreamQuery(q, nil)
   766  if err != nil {
   767      log.Fatalf("session.Advanced().StreamQuery() failed with '%s'\n", err)
   768  }
   769  // rest of processing as above
   770  ```
   771  
   772  See `streamQueryResults()` in [examples/main.go](examples/main.go) for full example.
   773  
   774  ## Revisions
   775  
   776  Note: make sure to enable revisions in a given store using `NewConfigureRevisionsOperation` operation.
   777  
   778  ```go
   779  e := &northwind.Employee{
   780      FirstName: "Jon",
   781      LastName:  "Snow",
   782  }
   783  err = session.Store(e)
   784  if err != nil {
   785      log.Fatalf("session.Store() failed with '%s'\n", err)
   786  }
   787  err = session.SaveChanges()
   788  if err != nil {
   789      log.Fatalf("session.SaveChanges() failed with '%s'\n", err)
   790  }
   791  
   792  // modify document to create a new revision
   793  e.FirstName = "Jhonny"
   794  err = session.SaveChanges()
   795  if err != nil {
   796      log.Fatalf("session.SaveChanges() failed with '%s'\n", err)
   797  }
   798  
   799  var revisions []*northwind.Employee
   800  err = session.Advanced().Revisions().GetFor(&revisions, e.ID)
   801  ```
   802  See `revisions()` in [examples/main.go](examples/main.go) for full example.
   803  
   804  Returns:
   805  ```
   806  [{ID:          "employees/43-A",
   807    LastName:    "Snow",
   808    FirstName:   "Jhonny",
   809    Title:       "",
   810    Address:     nil,
   811    HiredAt:     {},
   812    Birthday:    {},
   813    HomePhone:   "",
   814    Extension:   "",
   815    ReportsTo:   "",
   816    Notes:       [],
   817    Territories: []},
   818   {ID:          "employees/43-A",
   819    LastName:    "Snow",
   820    FirstName:   "Jon",
   821    Title:       "",
   822    Address:     nil,
   823    HiredAt:     {},
   824    Birthday:    {},
   825    HomePhone:   "",
   826    Extension:   "",
   827    ReportsTo:   "",
   828    Notes:       [],
   829    Territories: []}]
   830  ```
   831  
   832  ## Suggestions
   833  
   834  Suggestions provides similarity queries. Here we're asking for `FirstName` values similar to `Micael` and the database suggests `Michael`.
   835  
   836  ```go
   837  index := ravendb.NewIndexCreationTask("EmployeeIndex")
   838  index.Map = "from doc in docs.Employees select new { doc.FirstName }"
   839  index.Suggestion("FirstName")
   840  
   841  err = store.ExecuteIndex(index, "")
   842  if err != nil {
   843      log.Fatalf("store.ExecuteIndex() failed with '%s'\n", err)
   844  }
   845  
   846  tp := reflect.TypeOf(&northwind.Employee{})
   847  q := session.QueryCollectionForType(tp)
   848  su := ravendb.NewSuggestionWithTerm("FirstName")
   849  su.Term = "Micael"
   850  suggestionQuery := q.SuggestUsing(su)
   851  results, err := suggestionQuery.Execute()
   852  ```
   853  See `suggestions()` in [examples/main.go](examples/main.go) for full example.
   854  
   855  Returns:
   856  ```
   857  {FirstName: {Name:        "FirstName",
   858               Suggestions: ["michael"]}}
   859  ```
   860  
   861  ## Advanced patching
   862  
   863  To update documents more efficiently than sending the whole document, you can patch just a given field or atomically add/substract values
   864  of numeric fields.
   865  
   866  ```go
   867  err = session.Advanced().IncrementByID(product.ID, "PricePerUnit", 15)
   868  if err != nil {
   869      log.Fatalf("session.Advanced().IncrementByID() failed with %s\n", err)
   870  }
   871  
   872  err = session.Advanced().Patch(product, "Category", "expensive products")
   873  if err != nil {
   874      log.Fatalf("session.Advanced().PatchEntity() failed with %s\n", err)
   875  }
   876  
   877  err = session.SaveChanges()
   878  if err != nil {
   879      log.Fatalf("session.SaveChanges() failed with %s\n", err)
   880  }
   881  ```
   882  See `advancedPatching()` in [examples/main.go](examples/main.go) for full example.
   883  
   884  ## Subscriptions
   885  
   886  ```go
   887  opts := ravendb.SubscriptionCreationOptions{
   888      Query: "from Products where PricePerUnit > 17 and PricePerUnit < 19",
   889  }
   890  subscriptionName, err := store.Subscriptions().Create(&opts, "")
   891  if err != nil {
   892      log.Fatalf("store.Subscriptions().Create() failed with %s\n", err)
   893  }
   894  wopts := ravendb.NewSubscriptionWorkerOptions(subscriptionName)
   895  worker, err := store.Subscriptions().GetSubscriptionWorker(tp, wopts, "")
   896  if err != nil {
   897      log.Fatalf("store.Subscriptions().GetSubscriptionWorker() failed with %s\n", err)
   898  }
   899  
   900  results := make(chan *ravendb.SubscriptionBatch, 16)
   901  cb := func(batch *ravendb.SubscriptionBatch) error {
   902      results <- batch
   903      return nil
   904  }
   905  err = worker.Run(cb)
   906  if err != nil {
   907      log.Fatalf("worker.Run() failed with %s\n", err)
   908  }
   909  
   910  // wait for first batch result
   911  select {
   912  case batch := <-results:
   913      fmt.Print("Batch of subscription results:\n")
   914      pretty.Print(batch)
   915  case <-time.After(time.Second * 5):
   916      fmt.Printf("Timed out waiting for first subscription batch\n")
   917  
   918  }
   919  
   920  _ = worker.Close()
   921  ```
   922  See `subscriptions()` in [examples/main.go](examples/main.go) for full example.