github.com/ravendb/ravendb-go-client@v0.0.0-20240229102137-4474ee7aa0fa/dive-into-raven/main.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"io"
     8  	"mime"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/kylelemons/godebug/pretty"
    16  	"github.com/ravendb/ravendb-go-client"
    17  	"github.com/ravendb/ravendb-go-client/examples/northwind"
    18  )
    19  
    20  var (
    21  	// change to false to run the examples against a
    22  	// local server running on  port 8080
    23  	usePublicTestServer = false
    24  
    25  	// if running RavenDB locally on port 8080
    26  	serverLocalURL = "http://localhost:8080"
    27  
    28  	// if using public RavenDB test instance
    29  	serverPublicTestURL = "http://live-test.ravendb.net"
    30  
    31  	testDatabaseName string
    32  )
    33  
    34  // for optional logging
    35  var (
    36  	// if true, we'll show summary of HTTP requests made to the server
    37  	// and dump full info about failed HTTP requests
    38  	verboseLogging = false
    39  
    40  	// if true, logs all http requests/responses to a file for further inspection
    41  	// this is for use in tests so the file has a fixed location:
    42  	// logs/trace_${test_name}_go.txt
    43  	logAllRequests = true
    44  
    45  	// if logAllRequests is true, this is a path of a file where we log
    46  	// info about all HTTP requests
    47  	logAllRequestsPath = "http_requests_log.txt"
    48  )
    49  
    50  var (
    51  	serverURL    = "http://localhost:8080"
    52  	databaseName = "YourDatabaseName"
    53  )
    54  
    55  var (
    56  	globalDocumentStore *ravendb.DocumentStore
    57  )
    58  
    59  func createDocumentStore() (*ravendb.DocumentStore, error) {
    60  	if globalDocumentStore != nil {
    61  		return globalDocumentStore, nil
    62  	}
    63  	urls := []string{serverURL}
    64  	store := ravendb.NewDocumentStore(urls, databaseName)
    65  	err := store.Initialize()
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	globalDocumentStore = store
    70  	return globalDocumentStore, nil
    71  }
    72  
    73  func genUID() string {
    74  	var u [16]byte
    75  	_, _ = io.ReadFull(rand.Reader, u[:])
    76  	return hex.EncodeToString(u[:])
    77  }
    78  
    79  func genRandomDatabaseName() string {
    80  	return "demo-" + genUID()
    81  }
    82  
    83  func waitForIndexing(store *ravendb.DocumentStore, database string, timeout time.Duration) error {
    84  	admin := store.Maintenance().ForDatabase(database)
    85  	if timeout == 0 {
    86  		timeout = time.Minute
    87  	}
    88  
    89  	sp := time.Now()
    90  	for time.Since(sp) < timeout {
    91  		op := ravendb.NewGetStatisticsOperation("")
    92  		err := admin.Send(op)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		databaseStatistics := op.Command.Result
    97  		isDone := true
    98  		hasError := false
    99  		for _, index := range databaseStatistics.Indexes {
   100  			if index.State == ravendb.IndexStateDisabled {
   101  				continue
   102  			}
   103  			if index.IsStale || strings.HasPrefix(index.Name, ravendb.IndexingSideBySideIndexNamePrefix) {
   104  				isDone = false
   105  			}
   106  			if index.State == ravendb.IndexStateError {
   107  				hasError = true
   108  			}
   109  		}
   110  		if isDone {
   111  			return nil
   112  		}
   113  		if hasError {
   114  			break
   115  		}
   116  		time.Sleep(time.Millisecond * 100)
   117  	}
   118  
   119  	op := ravendb.NewGetIndexErrorsOperation(nil)
   120  	err := admin.Send(op)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	return ravendb.NewTimeoutError("The indexes stayed stale for more than %s", timeout)
   125  }
   126  
   127  func createSampleNorthwindDatabase(store *ravendb.DocumentStore) error {
   128  	sampleData := ravendb.NewCreateSampleDataOperation()
   129  	err := store.Maintenance().Send(sampleData)
   130  	if err != nil {
   131  		fmt.Printf("createSampleNorthwindDatabase: store.Maintance().Send() failed with '%s'\n", err)
   132  		return err
   133  	}
   134  	err = waitForIndexing(store, store.GetDatabase(), 0)
   135  	if err != nil {
   136  		fmt.Printf("watiForIndexing() failed with '%s'\n", err)
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  // create a new, randomly named database for just tests and populate with
   143  // sample Northwind data.
   144  // if usePublicTestServer we'll use public RavenDB instance. Otherwise
   145  // we'll talk to local server on port 8080
   146  func createTestDocumentStore() (*ravendb.DocumentStore, error) {
   147  	if globalDocumentStore != nil {
   148  		return globalDocumentStore, nil
   149  	}
   150  	urls := []string{serverLocalURL}
   151  	if usePublicTestServer {
   152  		urls[0] = serverPublicTestURL
   153  	}
   154  
   155  	testDatabaseName = genRandomDatabaseName()
   156  	databaseName = testDatabaseName
   157  
   158  	// "test.manager" is a dummy database
   159  	// we need a store, even if it points to a dummy database,
   160  	// to create a new database and then create a store out of thtat
   161  	storeManager := ravendb.NewDocumentStore(urls, "test.manager")
   162  
   163  	err := storeManager.Initialize()
   164  	if err != nil {
   165  		fmt.Printf("createTestDocumentStore: storeManager.Initialize() failed with '%s'\n", err)
   166  		return nil, err
   167  	}
   168  
   169  	databaseRecord := ravendb.NewDatabaseRecord()
   170  	databaseRecord.DatabaseName = testDatabaseName
   171  
   172  	// replicationFactor seems to be a minimum number of nodes with the data
   173  	// so it must be less than 3 (we have 3 nodes and might kill one, leaving
   174  	// only 2)
   175  	createDatabaseOperation := ravendb.NewCreateDatabaseOperation(databaseRecord, 1)
   176  	err = storeManager.Maintenance().Server().Send(createDatabaseOperation)
   177  	if err != nil {
   178  		fmt.Printf("d.store.Maintenance().Server().Send(createDatabaseOperation) failed with %s\n", err)
   179  		return nil, err
   180  	}
   181  
   182  	store := ravendb.NewDocumentStore(urls, testDatabaseName)
   183  	err = store.Initialize()
   184  	if err != nil {
   185  		fmt.Printf("createTestDocumentStore: store.Initialize() failed with '%s'\n", err)
   186  		return nil, err
   187  	}
   188  
   189  	fmt.Printf("Created a test database '%s'\n", testDatabaseName)
   190  	globalDocumentStore = store
   191  	err = createSampleNorthwindDatabase(store)
   192  	if err != nil {
   193  		fmt.Printf("createTestDocumentStore: createSampleNorthwindDatabase() failed with '%s'\n", err)
   194  		return nil, err
   195  	}
   196  
   197  	return globalDocumentStore, nil
   198  }
   199  
   200  func deleteTestDatabase() error {
   201  	if globalDocumentStore == nil || testDatabaseName == "" {
   202  		return nil
   203  	}
   204  	fmt.Printf("Deleting test database '%s'\n", testDatabaseName)
   205  	op := ravendb.NewDeleteDatabasesOperation(testDatabaseName, true)
   206  	return globalDocumentStore.Maintenance().Server().Send(op)
   207  }
   208  
   209  func createDocument(companyName, companyPhone, contactName, contactTitle string) error {
   210  	newCompany := &northwind.Company{
   211  		Name:  companyName,
   212  		Phone: companyPhone,
   213  		Contact: &northwind.Contact{
   214  			Name:  contactName,
   215  			Title: contactTitle,
   216  		},
   217  	}
   218  
   219  	session, err := globalDocumentStore.OpenSession("")
   220  	if err != nil {
   221  		return err
   222  	}
   223  	defer session.Close()
   224  
   225  	err = session.Store(newCompany)
   226  	if err != nil {
   227  		return err
   228  	}
   229  
   230  	theNewDocumentID := newCompany.ID
   231  
   232  	err = session.SaveChanges()
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	fmt.Printf("Created a new document with id: %s\n", theNewDocumentID)
   238  	return nil
   239  }
   240  
   241  func session() error {
   242  	session, err := globalDocumentStore.OpenSession("")
   243  	if err != nil {
   244  		return err
   245  	}
   246  	//   Run your business logic:
   247  	//
   248  	//   Store documents
   249  	//   Load and Modify documents
   250  	//   Query indexes & collections
   251  	//   Delete documents
   252  	//   .... etc.
   253  
   254  	err = session.SaveChanges()
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	// make sure to close the session
   260  	session.Close()
   261  
   262  	return nil
   263  }
   264  
   265  func editDocument(companyName string) error {
   266  	session, err := globalDocumentStore.OpenSession("")
   267  	if err != nil {
   268  		return err
   269  	}
   270  	defer session.Close()
   271  
   272  	var company *northwind.Company
   273  	err = session.Load(&company, "companies/5-A")
   274  	if err != nil {
   275  		return err
   276  	}
   277  	// if not found, company is not changed and will remain nil
   278  	if company == nil {
   279  		return nil
   280  	}
   281  	company.Name = companyName
   282  
   283  	err = session.SaveChanges()
   284  	if err != nil {
   285  		return err
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  func deleteDocument(documentID string) error {
   292  	session, err := globalDocumentStore.OpenSession("")
   293  	if err != nil {
   294  		return err
   295  	}
   296  	defer session.Close()
   297  
   298  	err = session.DeleteByID(documentID, "")
   299  	if err != nil {
   300  		return err
   301  	}
   302  
   303  	err = session.SaveChanges()
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  func createRelatedDocuments(productName, supplierName, supplierPhone string) error {
   312  	supplier := &northwind.Supplier{
   313  		Name:  supplierName,
   314  		Phone: supplierPhone,
   315  	}
   316  
   317  	category := &northwind.Category{
   318  		Name:        "NoSQL Databases",
   319  		Description: "Non-relational databases",
   320  	}
   321  
   322  	product := &northwind.Product{
   323  		Name: productName,
   324  	}
   325  
   326  	session, err := globalDocumentStore.OpenSession("")
   327  	if err != nil {
   328  		return err
   329  	}
   330  	defer session.Close()
   331  
   332  	err = session.Store(supplier)
   333  	if err != nil {
   334  		return err
   335  	}
   336  	err = session.Store(category)
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	product.Supplier = supplier.ID
   342  	product.Category = category.ID
   343  
   344  	err = session.Store(product)
   345  	if err != nil {
   346  		return err
   347  	}
   348  
   349  	err = session.SaveChanges()
   350  	if err != nil {
   351  		return err
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  func loadRelatedDocuments(pricePerUnit float64, phone string) error {
   358  	session, err := globalDocumentStore.OpenSession("")
   359  	if err != nil {
   360  		return err
   361  	}
   362  	defer session.Close()
   363  
   364  	var product *northwind.Product
   365  	err = session.Include("supplier").Load(&product, "products/34-A")
   366  	if err != nil {
   367  		return err
   368  	}
   369  	if product == nil {
   370  		// not found
   371  		return nil
   372  	}
   373  
   374  	var supplier *northwind.Supplier
   375  	err = session.Load(&supplier, product.Supplier)
   376  	if err != nil || supplier == nil {
   377  		return err
   378  	}
   379  
   380  	product.PricePerUnit = pricePerUnit
   381  	supplier.Phone = phone
   382  
   383  	err = session.SaveChanges()
   384  	if err != nil {
   385  		return err
   386  	}
   387  
   388  	return nil
   389  }
   390  
   391  func queryRelatedDocuments() error {
   392  	session, err := globalDocumentStore.OpenSession("")
   393  	if err != nil {
   394  		return err
   395  	}
   396  	defer session.Close()
   397  
   398  	session.Advanced().SetMaxNumberOfRequestsPerSession(128)
   399  
   400  	queriedType := reflect.TypeOf(&northwind.Order{})
   401  	q := session.QueryCollectionForType(queriedType)
   402  	q = q.Include("Lines.Product")
   403  	q = q.WhereNotEquals("ShippedAt", nil)
   404  	var shippedOrders []*northwind.Order
   405  	err = q.GetResults(&shippedOrders)
   406  	if err != nil {
   407  		return err
   408  	}
   409  
   410  	fmt.Printf("got %d shipped orders\n", len(shippedOrders))
   411  
   412  	for _, shippedOrder := range shippedOrders {
   413  		var productIDs []string
   414  		for _, line := range shippedOrder.Lines {
   415  			productIDs = append(productIDs, line.Product)
   416  		}
   417  
   418  		for i, productID := range productIDs {
   419  			var product *northwind.Product
   420  			err = session.Load(&product, productID)
   421  			if err != nil {
   422  				return err
   423  			}
   424  			product.UnitsOnOrder += shippedOrder.Lines[i].Quantity
   425  		}
   426  	}
   427  
   428  	err = session.SaveChanges()
   429  	if err != nil {
   430  		return err
   431  	}
   432  
   433  	return nil
   434  }
   435  
   436  func indexRelatedDocuments(categoryName string) error {
   437  	index := ravendb.NewIndexCreationTask("Products/ByCategoryName")
   438  	index.Map = `docs.Products.Select(product => new {
   439  		CategoryName = (this.LoadDocument(product.Category, "Categories")).Name
   440  	})
   441  `
   442  	err := globalDocumentStore.ExecuteIndex(index, "")
   443  	if err != nil {
   444  		return err
   445  	}
   446  	err = waitForIndexing(globalDocumentStore, "", 0)
   447  	if err != nil {
   448  		return err
   449  	}
   450  
   451  	session, err := globalDocumentStore.OpenSession("")
   452  	if err != nil {
   453  		return err
   454  	}
   455  	defer session.Close()
   456  
   457  	var productsWithCategoryName []*northwind.Product
   458  	q := session.QueryIndex(index.IndexName)
   459  	q = q.WhereEquals("CategoryName", categoryName)
   460  	err = q.GetResults(&productsWithCategoryName)
   461  	if err != nil {
   462  		return err
   463  	}
   464  	pretty.Print(productsWithCategoryName)
   465  	return nil
   466  }
   467  
   468  func storeAttachment(documentID string, attachmentPath string) error {
   469  	stream, err := os.Open(attachmentPath)
   470  	if err != nil {
   471  		return err
   472  	}
   473  	defer func() {
   474  		_ = stream.Close()
   475  	}()
   476  
   477  	contentType := mime.TypeByExtension(filepath.Ext(attachmentPath))
   478  	attachmentName := filepath.Base(attachmentPath)
   479  
   480  	session, err := globalDocumentStore.OpenSession("")
   481  	if err != nil {
   482  		return err
   483  	}
   484  	defer session.Close()
   485  
   486  	err = session.Advanced().Attachments().StoreByID(documentID, attachmentName, stream, contentType)
   487  	if err != nil {
   488  		return err
   489  	}
   490  
   491  	err = session.SaveChanges()
   492  	if err != nil {
   493  		return err
   494  	}
   495  
   496  	return nil
   497  }
   498  
   499  func enableRevisions(collection1, collection2 string) error {
   500  	dur := ravendb.Duration(time.Hour * 24 * 14)
   501  	defaultConfig := &ravendb.RevisionsCollectionConfiguration{
   502  		Disabled:                 false,
   503  		PurgeOnDelete:            false,
   504  		MinimumRevisionsToKeep:   5,
   505  		MinimumRevisionAgeToKeep: &dur,
   506  	}
   507  	revisiionConfiguration1 := &ravendb.RevisionsCollectionConfiguration{
   508  		Disabled: true,
   509  	}
   510  	revisiionConfiguration2 := &ravendb.RevisionsCollectionConfiguration{
   511  		PurgeOnDelete: true,
   512  	}
   513  	collections := map[string]*ravendb.RevisionsCollectionConfiguration{
   514  		collection1: revisiionConfiguration1,
   515  		collection2: revisiionConfiguration2,
   516  	}
   517  
   518  	myRevisionsConfiguration := &ravendb.RevisionsConfiguration{
   519  		DefaultConfig: defaultConfig,
   520  		Collections:   collections,
   521  	}
   522  
   523  	revisionsConfigurationOperation := ravendb.NewConfigureRevisionsOperation(myRevisionsConfiguration)
   524  	return globalDocumentStore.Maintenance().Send(revisionsConfigurationOperation)
   525  }
   526  
   527  func getRevisions() error {
   528  	myRevisionsConfiguration := &ravendb.RevisionsConfiguration{
   529  		DefaultConfig: &ravendb.RevisionsCollectionConfiguration{
   530  			Disabled: false,
   531  		},
   532  	}
   533  
   534  	revisionsConfigurationOperation := ravendb.NewConfigureRevisionsOperation(myRevisionsConfiguration)
   535  	err := globalDocumentStore.Maintenance().Send(revisionsConfigurationOperation)
   536  	if err != nil {
   537  		return nil
   538  	}
   539  
   540  	session, err := globalDocumentStore.OpenSession("")
   541  	if err != nil {
   542  		return err
   543  	}
   544  	defer session.Close()
   545  
   546  	var company *northwind.Company
   547  	err = session.Load(&company, "companies/7-A")
   548  	if err != nil {
   549  		return err
   550  	}
   551  	company.Name = "Name 1"
   552  	err = session.SaveChanges()
   553  	if err != nil {
   554  		return err
   555  	}
   556  
   557  	company.Name = "Name 2"
   558  	company.Phone = "052-1234-567"
   559  	err = session.SaveChanges()
   560  	if err != nil {
   561  		return err
   562  	}
   563  
   564  	var revisions []*northwind.Company
   565  	err = session.Advanced().Revisions().GetFor(&revisions, "companies/7-A")
   566  	if err != nil {
   567  		return err
   568  	}
   569  	pretty.Print(revisions)
   570  
   571  	return nil
   572  }
   573  
   574  func queryOverview() error {
   575  	session, err := globalDocumentStore.OpenSession("")
   576  	if err != nil {
   577  		return err
   578  	}
   579  	defer session.Close()
   580  
   581  	queriedType := reflect.TypeOf(&northwind.Employee{})
   582  	queryDefinition := session.QueryCollectionForType(queriedType)
   583  
   584  	// Define actions such as:
   585  	// Filter documents by documents fields
   586  	// Filter documents by text criteria
   587  	// Include related documents
   588  	// Get the query stats
   589  	// Sort results
   590  	// Customise the returned entity fields (Projections)
   591  	// Control results paging
   592  
   593  	var queryResults []*northwind.Employee
   594  	err = queryDefinition.GetResults(&queryResults)
   595  	if err != nil {
   596  		return err
   597  	}
   598  	fmt.Printf("Got %d results\n", len(queryResults))
   599  	return nil
   600  }
   601  
   602  // EmployeeDetails describes details of an employee
   603  type EmployeeDetails struct {
   604  	FullName  string       `json:"FullName"`
   605  	FirstName string       `json:"FirstName"`
   606  	Title     string       `json:"Title"`
   607  	HiredAt   ravendb.Time `json:"HiredAt"`
   608  }
   609  
   610  func queryExample() error {
   611  	session, err := globalDocumentStore.OpenSession("")
   612  	if err != nil {
   613  		return err
   614  	}
   615  	defer session.Close()
   616  
   617  	var queryResults []*EmployeeDetails
   618  
   619  	queriedType := reflect.TypeOf(&northwind.Employee{})
   620  	query := session.QueryCollectionForType(queriedType)
   621  	{
   622  		query = query.OpenSubclause()
   623  		query = query.WhereEquals("FirstName", "Steven")
   624  		query = query.OrElse()
   625  		query = query.WhereEquals("Title", "Sales Representative")
   626  		query = query.CloseSubclause()
   627  	}
   628  	query = query.Include("ReportsTo")
   629  
   630  	var statistics *ravendb.QueryStatistics
   631  	query = query.Statistics(&statistics)
   632  
   633  	query = query.OrderByDescending("HiredAt")
   634  
   635  	projectedType := reflect.TypeOf(&EmployeeDetails{})
   636  	fields := []string{
   637  		"FirstName",
   638  		"Title",
   639  		"HiredAt",
   640  	}
   641  	query = query.SelectFields(projectedType, fields...)
   642  	query = query.Take(5)
   643  	err = query.GetResults(&queryResults)
   644  	if err != nil {
   645  		return err
   646  	}
   647  	fmt.Printf("Got %d results\n", len(queryResults))
   648  	if len(queryResults) > 0 {
   649  		pretty.Print(queryResults[0])
   650  	}
   651  	return nil
   652  }
   653  
   654  func fullCollectionQuery() error {
   655  	session, err := globalDocumentStore.OpenSession("")
   656  	if err != nil {
   657  		return err
   658  	}
   659  	defer session.Close()
   660  
   661  	queriedType := reflect.TypeOf(&northwind.Company{})
   662  	fullCollectionQuery := session.QueryCollectionForType(queriedType)
   663  
   664  	var queryResults []*northwind.Company
   665  	err = fullCollectionQuery.GetResults(&queryResults)
   666  	if err != nil {
   667  		return err
   668  	}
   669  	fmt.Printf("Got %d results\n", len(queryResults))
   670  
   671  	return nil
   672  }
   673  
   674  func queryByDocumentID(employeeDocumentID string) error {
   675  	session, err := globalDocumentStore.OpenSession("")
   676  	if err != nil {
   677  		return err
   678  	}
   679  	defer session.Close()
   680  
   681  	queriedType := reflect.TypeOf(&northwind.Employee{})
   682  	queryByDocumentID := session.QueryCollectionForType(queriedType)
   683  	queryByDocumentID = queryByDocumentID.Where("ID", "==", employeeDocumentID)
   684  
   685  	var employee *northwind.Employee
   686  	err = queryByDocumentID.Single(&employee)
   687  	if err != nil {
   688  		return err
   689  	}
   690  	pretty.Print(employee)
   691  
   692  	return nil
   693  }
   694  
   695  func queryFilterResultsBasic() error {
   696  	session, err := globalDocumentStore.OpenSession("")
   697  	if err != nil {
   698  		return err
   699  	}
   700  	defer session.Close()
   701  
   702  	queriedType := reflect.TypeOf(&northwind.Employee{})
   703  	filteredQuery := session.QueryCollectionForType(queriedType)
   704  	filteredQuery = filteredQuery.Where("FirstName", "==", "Anne")
   705  
   706  	var filteredEmployees []*northwind.Employee
   707  	err = filteredQuery.GetResults(&filteredEmployees)
   708  	if err != nil {
   709  		return err
   710  	}
   711  	fmt.Printf("Got %d results\n", len(filteredEmployees))
   712  	if len(filteredEmployees) > 0 {
   713  		pretty.Print(filteredEmployees[0])
   714  
   715  	}
   716  
   717  	return nil
   718  }
   719  
   720  func queryFilterResultsMultipleConditions(country string) error {
   721  	session, err := globalDocumentStore.OpenSession("")
   722  	if err != nil {
   723  		return err
   724  	}
   725  	defer session.Close()
   726  
   727  	queriedType := reflect.TypeOf(&northwind.Employee{})
   728  	filteredQuery := session.QueryCollectionForType(queriedType)
   729  	filteredQuery = filteredQuery.WhereIn("FirstName", []interface{}{"Anne", "John"})
   730  	filteredQuery = filteredQuery.OrElse()
   731  	{
   732  		filteredQuery = filteredQuery.OpenSubclause()
   733  		filteredQuery = filteredQuery.WhereEquals("Address.Country", country)
   734  		filteredQuery = filteredQuery.Where("Territories.Count", ">", 2)
   735  		filteredQuery = filteredQuery.WhereStartsWith("Title", "Sales")
   736  		filteredQuery = filteredQuery.CloseSubclause()
   737  	}
   738  
   739  	var filteredEmployees []*northwind.Employee
   740  	err = filteredQuery.GetResults(&filteredEmployees)
   741  	if err != nil {
   742  		return err
   743  	}
   744  	fmt.Printf("Got %d results\n", len(filteredEmployees))
   745  	if len(filteredEmployees) > 0 {
   746  		pretty.Print(filteredEmployees[0])
   747  	}
   748  
   749  	return nil
   750  }
   751  
   752  // CompanyDetails describes details about a company
   753  type CompanyDetails struct {
   754  	CompanyName string `json:"CompanyName"`
   755  	City        string `json:"City"`
   756  	Country     string `json:"Country"`
   757  }
   758  
   759  func queryProjectingIndividualFields() error {
   760  	session, err := globalDocumentStore.OpenSession("")
   761  	if err != nil {
   762  		return err
   763  	}
   764  	defer session.Close()
   765  
   766  	queriedType := reflect.TypeOf(&northwind.Company{})
   767  	projectedQuery := session.QueryCollectionForType(queriedType)
   768  	projectedType := reflect.TypeOf(&CompanyDetails{})
   769  	fields := []string{"Name", "Address.City", "Address.Country"}
   770  	projections := []string{"CompanyName", "City", "Country"}
   771  	queryData := &ravendb.QueryData{
   772  		Fields:      fields,
   773  		Projections: projections,
   774  	}
   775  	projectedQuery = projectedQuery.SelectFieldsWithQueryData(projectedType, queryData)
   776  	var projectedResults []*CompanyDetails
   777  	err = projectedQuery.GetResults(&projectedResults)
   778  	if err != nil {
   779  		return err
   780  	}
   781  	fmt.Printf("Got %d results\n", len(projectedResults))
   782  	if len(projectedResults) > 0 {
   783  		pretty.Print(projectedResults[0])
   784  	}
   785  	return nil
   786  }
   787  
   788  func queryProjectingUsingFunctions() error {
   789  	session, err := globalDocumentStore.OpenSession("")
   790  	if err != nil {
   791  		return err
   792  	}
   793  	defer session.Close()
   794  
   795  	rawQuery := `declare function output(e) {
   796  	    var format = function(p){ return p.FirstName + " " + p.LastName; };
   797  	    return { FullName : format(e), Title: e.Title, HiredAt: e.HiredAt };
   798  	}
   799  	from Employees as e select output(e)
   800  	`
   801  	query := session.Advanced().RawQuery(rawQuery)
   802  	var projectedResults []*EmployeeDetails
   803  	err = query.GetResults(&projectedResults)
   804  	if err != nil {
   805  		return err
   806  	}
   807  	fmt.Printf("Got %d results\n", len(projectedResults))
   808  	if len(projectedResults) > 0 {
   809  		pretty.Print(projectedResults[0])
   810  	}
   811  	return nil
   812  }
   813  
   814  func staticIndexesOverview() error {
   815  	indexName := "Employees/ByLastName"
   816  	index := ravendb.NewIndexCreationTask(indexName)
   817  	// Define:
   818  	//    Map(s) functions
   819  	//    Reduce function
   820  	//    Additional indexing options per field
   821  	index.Map = "from e in docs.Employees select new { e.LastName }"
   822  
   823  	err := index.Execute(globalDocumentStore, nil, "")
   824  	if err != nil {
   825  		return err
   826  	}
   827  
   828  	session, err := globalDocumentStore.OpenSession("")
   829  	if err != nil {
   830  		return err
   831  	}
   832  	defer session.Close()
   833  
   834  	queryOnIndex := session.QueryIndex(indexName)
   835  	queryOnIndex = queryOnIndex.Where("LastName", "==", "SomeName")
   836  	var queryResults []*northwind.Employee
   837  	err = queryOnIndex.GetResults(&queryResults)
   838  	if err != nil {
   839  		return err
   840  	}
   841  	fmt.Printf("Got %d results\n", len(queryResults))
   842  	if len(queryResults) > 0 {
   843  		pretty.Print(queryResults[0])
   844  	}
   845  	return nil
   846  }
   847  
   848  func mapIndex(startYear int) error {
   849  	mapIndexDef := `
   850  docs.Employees.Select(e => new {
   851  	FullName = e.FirstName + " " + e.LastName,
   852  	Country = e.Address.Country,
   853  	WorkingInCompanySince = e.HiredAt.Year,
   854  	NumberOfTerritories = e.Territories.Count
   855  }
   856  `
   857  	indexName := "Employees/ImportantDetails"
   858  	index := ravendb.NewIndexCreationTask(indexName)
   859  	index.Map = mapIndexDef
   860  
   861  	err := index.Execute(globalDocumentStore, nil, "")
   862  	if err != nil {
   863  		return err
   864  	}
   865  	err = waitForIndexing(globalDocumentStore, "", 0)
   866  	if err != nil {
   867  		return err
   868  	}
   869  
   870  	session, err := globalDocumentStore.OpenSession("")
   871  	if err != nil {
   872  		return err
   873  	}
   874  	defer session.Close()
   875  
   876  	query := session.QueryIndex(indexName)
   877  	query = query.Where("Country", "==", "USA")
   878  	query = query.Where("WorkingInCompanySince", ">", startYear)
   879  
   880  	var employeesFromUSA []*northwind.Employee
   881  	err = query.GetResults(&employeesFromUSA)
   882  	if err != nil {
   883  		return err
   884  	}
   885  	fmt.Printf("Got %d results\n", len(employeesFromUSA))
   886  	if len(employeesFromUSA) > 0 {
   887  		pretty.Print(employeesFromUSA[0])
   888  	}
   889  	return nil
   890  }
   891  
   892  func mapReduceIndex(country string) error {
   893  	indexName := "Employees/ByCountry"
   894  	index := ravendb.NewIndexCreationTask(indexName)
   895  	mapIndexDef := `
   896  map('employees', function(e) {
   897  	return {
   898  		Country: e.Address.Country,
   899  		CountryCount: 1
   900  	}
   901  })
   902  `
   903  	index.Map = mapIndexDef
   904  
   905  	reduceIndexDef := `
   906  groupBy(x => x.Country)
   907  .aggregate(g => {
   908  	return {
   909  		Country: g.key,
   910  		Count: g.values.reduce((count, val) => val.CountryCount + count, 0)
   911  	}
   912  })
   913  `
   914  	index.Reduce = reduceIndexDef
   915  
   916  	err := index.Execute(globalDocumentStore, nil, "")
   917  	if err != nil {
   918  		return err
   919  	}
   920  	err = waitForIndexing(globalDocumentStore, "", 0)
   921  	if err != nil {
   922  		return err
   923  	}
   924  
   925  	session, err := globalDocumentStore.OpenSession("")
   926  	if err != nil {
   927  		return err
   928  	}
   929  	defer session.Close()
   930  
   931  	query := session.QueryIndex(indexName)
   932  	query = query.Where("Country", "==", country)
   933  
   934  	var queryResult *struct {
   935  		Country string
   936  		Count   int
   937  	}
   938  
   939  	err = query.First(&queryResult)
   940  	if err != nil {
   941  		return err
   942  	}
   943  	if queryResult != nil {
   944  		fmt.Printf("Number of employees from country '%s': %d\n", queryResult.Country, queryResult.Count)
   945  	}
   946  	return nil
   947  }
   948  
   949  func autoMapIndex(firstName string) error {
   950  	session, err := globalDocumentStore.OpenSession("")
   951  	if err != nil {
   952  		return err
   953  	}
   954  	defer session.Close()
   955  
   956  	queriedType := reflect.TypeOf(&northwind.Employee{})
   957  	query := session.QueryCollectionForType(queriedType)
   958  	query = query.Where("FirstName", "==", firstName)
   959  
   960  	var employeeResult *northwind.Employee
   961  	err = query.First(&employeeResult)
   962  	if err != nil {
   963  		return err
   964  	}
   965  	if employeeResult != nil {
   966  		pretty.Print(employeeResult)
   967  	} else {
   968  		fmt.Printf("No employee with first name '%s'\n", firstName)
   969  	}
   970  	return nil
   971  }
   972  
   973  func autoMapIndexTest() {
   974  	err := autoMapIndex("Steven")
   975  	if err != nil {
   976  		fmt.Printf("autoMapIndex() failed with '%s'\n", err)
   977  	}
   978  }
   979  
   980  func autoMapIndex2(country string) error {
   981  	session, err := globalDocumentStore.OpenSession("")
   982  	if err != nil {
   983  		return err
   984  	}
   985  	defer session.Close()
   986  
   987  	queriedType := reflect.TypeOf(&northwind.Employee{})
   988  	query := session.QueryCollectionForType(queriedType)
   989  	query = query.WhereStartsWith("Title", "Sales")
   990  	query = query.Where("Address.Country", "==", country)
   991  
   992  	var employeeResult *northwind.Employee
   993  	err = query.First(&employeeResult)
   994  	if err != nil {
   995  		return err
   996  	}
   997  	if employeeResult != nil {
   998  		pretty.Print(employeeResult)
   999  	} else {
  1000  		fmt.Printf("No employee matching query\n")
  1001  	}
  1002  	return nil
  1003  }
  1004  
  1005  func fullTextSearchSingleField(searchTerm string) error {
  1006  	indexName := "Categories/DescriptionText"
  1007  	index := ravendb.NewIndexCreationTask(indexName)
  1008  
  1009  	index.Map = `
  1010  from c in docs.Categories
  1011  select new {
  1012     CategoryDescription = c.Description
  1013  }
  1014  `
  1015  	index.Index("CategoryDescription", ravendb.FieldIndexingSearch)
  1016  
  1017  	err := index.Execute(globalDocumentStore, nil, "")
  1018  	if err != nil {
  1019  		return err
  1020  	}
  1021  	err = waitForIndexing(globalDocumentStore, "", 0)
  1022  	if err != nil {
  1023  		return err
  1024  	}
  1025  
  1026  	session, err := globalDocumentStore.OpenSession("")
  1027  	if err != nil {
  1028  		return err
  1029  	}
  1030  	defer session.Close()
  1031  
  1032  	query := session.QueryIndex(indexName)
  1033  	query = query.Where("CategoryDescription", "==", searchTerm)
  1034  
  1035  	var categoriesWithSearchTerm []*northwind.Category
  1036  	err = query.GetResults(&categoriesWithSearchTerm)
  1037  	if err != nil {
  1038  		return err
  1039  	}
  1040  	fmt.Printf("Get %d results\n", len(categoriesWithSearchTerm))
  1041  	if len(categoriesWithSearchTerm) > 0 {
  1042  		pretty.Print(categoriesWithSearchTerm[0])
  1043  	}
  1044  
  1045  	return nil
  1046  }
  1047  
  1048  // LastFm represents an last fm document
  1049  type LastFm struct {
  1050  	ID        string
  1051  	Artist    string       `json:"Artist"`
  1052  	Title     string       `json:"Title"`
  1053  	TrackID   string       `json:"TrackId"`
  1054  	Tags      []string     `json:"Tags"`
  1055  	TimeStamp ravendb.Time `json:"TimeStamp"`
  1056  }
  1057  
  1058  // TODO: this requires database that I don't know how to create
  1059  func fullTextSearchMultipleField(searchTerm string) error {
  1060  	indexName := "Song/TextData"
  1061  	index := ravendb.NewIndexCreationTask(indexName)
  1062  
  1063  	index.Map = `
  1064  from song in docs.Songs
  1065  select new {
  1066  	SongData = new {
  1067  		song.Artist,
  1068  		song.Title,
  1069  		song.Tags,
  1070  		song.TrackId
  1071  	}
  1072  }
  1073  `
  1074  	index.Index("SongData", ravendb.FieldIndexingSearch)
  1075  
  1076  	err := index.Execute(globalDocumentStore, nil, "")
  1077  	if err != nil {
  1078  		return err
  1079  	}
  1080  	err = waitForIndexing(globalDocumentStore, "", 0)
  1081  	if err != nil {
  1082  		return err
  1083  	}
  1084  
  1085  	session, err := globalDocumentStore.OpenSession("")
  1086  	if err != nil {
  1087  		return err
  1088  	}
  1089  	defer session.Close()
  1090  
  1091  	query := session.QueryIndex(indexName)
  1092  	query = query.Search("SongData", searchTerm)
  1093  	query = query.Take(20)
  1094  
  1095  	var results []*LastFm
  1096  	err = query.GetResults(&results)
  1097  	if err != nil {
  1098  		return err
  1099  	}
  1100  	fmt.Printf("Get %d results\n", len(results))
  1101  	if len(results) > 0 {
  1102  		pretty.Print(results[0])
  1103  	}
  1104  
  1105  	return nil
  1106  }
  1107  
  1108  func createDatabase() error {
  1109  	databaseRecord := ravendb.NewDatabaseRecord()
  1110  	databaseRecord.DatabaseName = "NameOfDatabase"
  1111  
  1112  	createDatabaseOperation := ravendb.NewCreateDatabaseOperation(databaseRecord, 1)
  1113  	err := globalDocumentStore.Maintenance().Server().Send(createDatabaseOperation)
  1114  	if err != nil {
  1115  		if _, ok := err.(*ravendb.ConcurrencyError); ok {
  1116  			fmt.Printf("Database '%s' already exists\n", databaseRecord.DatabaseName)
  1117  			return nil
  1118  		}
  1119  		fmt.Printf("globalDocumentStore.Maintenance().Server().Send(createDatabaseOperation) failed with %s\n", err)
  1120  		return err
  1121  	}
  1122  
  1123  	return nil
  1124  }
  1125  
  1126  func createDatabaseTest() {
  1127  	err := createDatabase()
  1128  	if err != nil {
  1129  		fmt.Printf("createDatabase() failed with '%s'\n", err)
  1130  	}
  1131  }
  1132  
  1133  func fullTextSearchMultipleFieldTest() {
  1134  	err := fullTextSearchMultipleField("Floyd")
  1135  	if err != nil {
  1136  		fmt.Printf("fullTextSearchMultipleField() failed with '%s'\n", err)
  1137  	}
  1138  }
  1139  
  1140  func fullTextSearchSingleFieldTest() {
  1141  	err := fullTextSearchSingleField("pasta")
  1142  	if err != nil {
  1143  		fmt.Printf("fullTextSearchSingleField() failed with '%s'\n", err)
  1144  	}
  1145  }
  1146  
  1147  func autoMapIndex2Test() {
  1148  	err := autoMapIndex2("UK")
  1149  	if err != nil {
  1150  		fmt.Printf("autoMapIndex2() failed with '%s'\n", err)
  1151  	}
  1152  }
  1153  
  1154  func mapIndexTest() {
  1155  	err := mapIndex(1993)
  1156  	if err != nil {
  1157  		fmt.Printf("mapIndex() failed with '%s'\n", err)
  1158  	}
  1159  }
  1160  
  1161  func staticIndexesOverviewTest() {
  1162  	err := staticIndexesOverview()
  1163  	if err != nil {
  1164  		fmt.Printf("staticIndexesOverview() failed with '%s'\n", err)
  1165  	}
  1166  }
  1167  
  1168  func queryProjectingUsingFunctionsTest() {
  1169  	err := queryProjectingUsingFunctions()
  1170  	if err != nil {
  1171  		fmt.Printf("queryProjectingUsingFunctions() failed with '%s'\n", err)
  1172  	}
  1173  }
  1174  
  1175  func queryProjectingIndividualFieldsTest() {
  1176  	err := queryProjectingIndividualFields()
  1177  	if err != nil {
  1178  		fmt.Printf("queryProjectingIndividualFields() failed with '%s'\n", err)
  1179  	}
  1180  }
  1181  
  1182  func queryFilterResultsMultipleConditionsTest() {
  1183  	err := queryFilterResultsMultipleConditions("USA")
  1184  	if err != nil {
  1185  		fmt.Printf("queryFilterResultsMultipleConditions() failed with '%s'\n", err)
  1186  	}
  1187  }
  1188  
  1189  func queryFilterResultsBasicTest() {
  1190  	err := queryFilterResultsBasic()
  1191  	if err != nil {
  1192  		fmt.Printf("queryFilterResultsBasic() failed with '%s'\n", err)
  1193  	}
  1194  }
  1195  
  1196  func queryByDocumentIDTest() {
  1197  	err := queryByDocumentID("employees/1-A")
  1198  	if err != nil {
  1199  		fmt.Printf("queryByDocumentID() failed with '%s'\n", err)
  1200  	}
  1201  }
  1202  
  1203  func fullCollectionQueryTest() {
  1204  	err := fullCollectionQuery()
  1205  	if err != nil {
  1206  		fmt.Printf("fullCollectionQuery() failed with '%s'\n", err)
  1207  	}
  1208  }
  1209  
  1210  func queryExampleTest() {
  1211  	err := queryExample()
  1212  	if err != nil {
  1213  		fmt.Printf("queryExample() failed with '%s'\n", err)
  1214  	}
  1215  }
  1216  
  1217  func queryOverviewTest() {
  1218  	err := queryOverview()
  1219  	if err != nil {
  1220  		fmt.Printf("queryOverview() failed with '%s'\n", err)
  1221  	}
  1222  }
  1223  
  1224  func getRevisionsTest() {
  1225  	err := getRevisions()
  1226  	if err != nil {
  1227  		fmt.Printf("getRevisions() failed with '%s'\n", err)
  1228  	}
  1229  }
  1230  
  1231  func indexRelatedDocumentsTest() {
  1232  	err := indexRelatedDocuments("Produce")
  1233  	if err != nil {
  1234  		fmt.Printf("indexRelatedDocuments() failed with '%s'\n", err)
  1235  	}
  1236  }
  1237  
  1238  func queryRelatedDocumentsTest() {
  1239  	err := queryRelatedDocuments()
  1240  	if err != nil {
  1241  		fmt.Printf("queryRelatedDocuments() failed with '%s'\n", err)
  1242  	}
  1243  }
  1244  
  1245  func sessionTest() {
  1246  	err := session()
  1247  	if err != nil {
  1248  		fmt.Printf("sessionChapter() failed with '%s'\n", err)
  1249  	}
  1250  }
  1251  
  1252  func createDocumentTest() {
  1253  	err := createDocument("Hibernating Rhinos", "(+972)52-5486969", "New Contact Name", "New Contact Title")
  1254  	if err != nil {
  1255  		fmt.Printf("createDocument() failed with '%s'\n", err)
  1256  	}
  1257  }
  1258  
  1259  func editDocumentTest() {
  1260  	err := editDocument("New Company Name")
  1261  	if err != nil {
  1262  		fmt.Printf("editDocument() failed with '%s'\n", err)
  1263  	}
  1264  }
  1265  
  1266  func deleteDocumentTest() {
  1267  	err := deleteDocument("companies/1-A")
  1268  	if err != nil {
  1269  		fmt.Printf("deleteDocument() failed with '%s'\n", err)
  1270  	}
  1271  }
  1272  
  1273  func createRelatedDocumentsTest() {
  1274  	err := createRelatedDocuments("RavenDB", "Hibernating Rhinos", "(+972)52-54869696")
  1275  	if err != nil {
  1276  		fmt.Printf("createRelatedDocuments() failed with '%s'\n", err)
  1277  	}
  1278  }
  1279  
  1280  func loadRelatedDocumentsTest() {
  1281  	err := loadRelatedDocuments(12, "(+972)52-54869696")
  1282  	if err != nil {
  1283  		fmt.Printf("loadRelatedDocuments() failed with '%s'\n", err)
  1284  	}
  1285  }
  1286  
  1287  func storeAttachmentTest() {
  1288  	path := "pic.png"
  1289  	documentID := "companies/2-A"
  1290  	err := storeAttachment(documentID, path)
  1291  	if err != nil {
  1292  		fmt.Printf("storeAttachment() failed with '%s'\n", err)
  1293  	} else {
  1294  		fmt.Printf("stored file '%s' as attachment in a document with ID '%s'\n", path, documentID)
  1295  	}
  1296  }
  1297  
  1298  func enableRevisionsTest() {
  1299  	err := enableRevisions("Orders", "Companies")
  1300  	if err != nil {
  1301  		fmt.Printf("enableRevisions() failed with '%s'\n", err)
  1302  	}
  1303  }
  1304  
  1305  func mapReduceIndexTest() {
  1306  	err := mapReduceIndex("USA")
  1307  	if err != nil {
  1308  		fmt.Printf("mapReduceIndex() failed with '%s'\n", err)
  1309  	}
  1310  }
  1311  
  1312  var (
  1313  	testFunctions = map[string]func(){
  1314  		"createDocument":                       createDocumentTest,
  1315  		"session":                              sessionTest,
  1316  		"editDocument":                         editDocumentTest,
  1317  		"deleteDocument":                       deleteDocumentTest,
  1318  		"createRelatedDocuments":               createRelatedDocumentsTest,
  1319  		"loadRelatedDocuments":                 loadRelatedDocumentsTest,
  1320  		"storeAttachment":                      storeAttachmentTest,
  1321  		"enableRevisions":                      enableRevisionsTest,
  1322  		"indexRelatedDocuments":                indexRelatedDocumentsTest,
  1323  		"queryRelatedDocuments":                queryRelatedDocumentsTest,
  1324  		"getRevisions":                         getRevisionsTest,
  1325  		"queryOverview":                        queryOverviewTest,
  1326  		"queryExample":                         queryExampleTest,
  1327  		"fullCollectionQuery":                  fullCollectionQueryTest,
  1328  		"queryByDocumentID":                    queryByDocumentIDTest,
  1329  		"queryFilterResultsBasic":              queryFilterResultsBasicTest,
  1330  		"queryFilterResultsMultipleConditions": queryFilterResultsMultipleConditionsTest,
  1331  		"queryProjectingIndividualFields":      queryProjectingIndividualFieldsTest,
  1332  		"queryProjectingUsingFunctions":        queryProjectingUsingFunctionsTest,
  1333  		"staticIndexesOverview":                staticIndexesOverviewTest,
  1334  		"mapIndex":                             mapIndexTest,
  1335  		"mapReduceIndex":                       mapReduceIndexTest,
  1336  		"autoMapIndex":                         autoMapIndexTest,
  1337  		"autoMapIndex2":                        autoMapIndex2Test,
  1338  		"fullTextSearchSingleField":            fullTextSearchSingleFieldTest,
  1339  		"fullTextSearchMultipleField":          fullTextSearchMultipleFieldTest,
  1340  		"createDatabase":                       createDatabaseTest,
  1341  	}
  1342  )
  1343  
  1344  func usageAndExit() {
  1345  	fmt.Print(`To run:
  1346  go run dive-into-raven/main.go <testName>
  1347  e.g.
  1348  go run dive-into-raven/main.go indexRelatedDocuments
  1349  	`)
  1350  	os.Exit(1)
  1351  }
  1352  
  1353  func must(err error, format string, args ...interface{}) {
  1354  	if err != nil {
  1355  		fmt.Printf(format, args...)
  1356  		panic(err)
  1357  	}
  1358  }
  1359  
  1360  func main() {
  1361  	if len(os.Args) != 2 {
  1362  		usageAndExit()
  1363  	}
  1364  	testNameArg := os.Args[1]
  1365  	testName := strings.TrimSuffix(testNameArg, "Test")
  1366  	testFn, ok := testFunctions[testName]
  1367  	if !ok {
  1368  		fmt.Printf("'%s' is not a known test function\n", testNameArg)
  1369  		usageAndExit()
  1370  	}
  1371  
  1372  	// setup optional logging
  1373  	setupLogging()
  1374  	defer finishLogging()
  1375  
  1376  	_, err := createTestDocumentStore()
  1377  	defer deleteTestDatabase()
  1378  	must(err, "createTestDocumentStore() failed with %s\n", err)
  1379  	fmt.Printf("Running %s\n", testNameArg)
  1380  	testFn()
  1381  }