Skip to content

Commit

Permalink
GT-199 Implement Search View v2 (#434)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwierzbo committed Sep 20, 2022
1 parent 41a6f37 commit d8dfd63
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,7 @@
- Deprecate fulltext index
- Add support for Pregel API
- Add tests to check support for Enterprise Graphs
- Search View v2 (`search-alias`)

## [1.3.3](https://github.com/arangodb/go-driver/tree/v1.3.3) (2022-07-27)
- Fix `lastValue` field type
Expand Down
8 changes: 7 additions & 1 deletion database_views.go
Expand Up @@ -41,12 +41,18 @@ type DatabaseViews interface {
// with given name and options, and opens a connection to it.
// If a view with given name already exists within the database, a ConflictError is returned.
CreateArangoSearchView(ctx context.Context, name string, options *ArangoSearchViewProperties) (ArangoSearchView, error)

// CreateArangoSearchAliasView creates ArangoSearch alias view with given name and options, and opens a connection to it.
// If a view with given name already exists within the database, a ConflictError is returned.
CreateArangoSearchAliasView(ctx context.Context, name string, options *ArangoSearchAliasViewProperties) (ArangoSearchViewAlias, error)
}

// ViewType is the type of a view.
// ViewType is the type of view.
type ViewType string

const (
// ViewTypeArangoSearch specifies an ArangoSearch view type.
ViewTypeArangoSearch = ViewType("arangosearch")
// ViewTypeArangoSearchAlias specifies an ArangoSearch view type alias.
ViewTypeArangoSearchAlias = ViewType("search-alias")
)
42 changes: 42 additions & 0 deletions database_views_impl.go
Expand Up @@ -158,3 +158,45 @@ func (d *database) CreateArangoSearchView(ctx context.Context, name string, opti

return result, nil
}

// CreateArangoSearchAliasView creates a new view of type search-alias,
// with given name and options, and opens a connection to it.
// If a view with given name already exists within the database, a ConflictError is returned.
func (d *database) CreateArangoSearchAliasView(ctx context.Context, name string, options *ArangoSearchAliasViewProperties) (ArangoSearchViewAlias, error) {
input := struct {
Name string `json:"name"`
Type ViewType `json:"type"`
ArangoSearchAliasViewProperties
}{
Name: name,
Type: ViewTypeArangoSearchAlias,
}
if options != nil {
input.ArangoSearchAliasViewProperties = *options
}
req, err := d.conn.NewRequest("POST", path.Join(d.relPath(), "_api/view"))
if err != nil {
return nil, WithStack(err)
}
if _, err := req.SetBody(input); err != nil {
return nil, WithStack(err)
}
applyContextSettings(ctx, req)
resp, err := d.conn.Do(ctx, req)
if err != nil {
return nil, WithStack(err)
}
if err := resp.CheckStatus(201); err != nil {
return nil, WithStack(err)
}
view, err := newView(name, input.Type, d)
if err != nil {
return nil, WithStack(err)
}
result, err := view.ArangoSearchViewAlias()
if err != nil {
return nil, WithStack(err)
}

return result, nil
}
114 changes: 114 additions & 0 deletions test/view_alias_test.go
@@ -0,0 +1,114 @@
package test

import (
"context"
"testing"

"github.com/arangodb/go-driver"

"github.com/stretchr/testify/require"
)

// ensureArangoSearchView is a helper to check if an arangosearch view exists and create it if needed.
// It will fail the test when an error occurs.
func ensureArangoSearchAliasView(ctx context.Context, db driver.Database, name string, options *driver.ArangoSearchAliasViewProperties, t testEnv) driver.ArangoSearchViewAlias {
v, err := db.View(ctx, name)
if driver.IsNotFound(err) {
v, err = db.CreateArangoSearchAliasView(ctx, name, options)
if err != nil {
t.Fatalf("Failed to create arangosearch view '%s': %s", name, describe(err))
}
} else if err != nil {
t.Fatalf("Failed to open view '%s': %s", name, describe(err))
}
result, err := v.ArangoSearchViewAlias()
if err != nil {
t.Fatalf("Failed to open view '%s' as arangosearch view: %s", name, describe(err))
}
return result
}

// TestSearchViewsAlias tests the arangosearch view alias methods
func TestSearchViewsAlias(t *testing.T) {
ctx := context.Background()
c := createClientFromEnv(t, true)
skipBelowVersion(c, "3.10", t)
skipBelowVersion(c, "3.10", t)
db := ensureDatabase(ctx, c, "search_view_test_basic", nil, t)

nameAlias := "test_add_collection_view_alias"
nameCol := "col_in_alias_view"
nameInvInd := "inv_index_alias_view"

col := ensureCollection(ctx, db, nameCol, nil, t)
v := ensureArangoSearchAliasView(ctx, db, nameAlias, nil, t)

p, err := v.Properties(ctx)
require.NoError(t, err)
require.Equal(t, p.Type, driver.ViewTypeArangoSearchAlias)
require.Equal(t, p.Name, nameAlias)
require.Len(t, p.Indexes, 0)

_, err = v.ArangoSearchView()
require.Error(t, err)

indexOpt := driver.InvertedIndexOptions{
Name: nameInvInd,
PrimarySort: driver.InvertedIndexPrimarySort{
Fields: []driver.ArangoSearchPrimarySortEntry{
{Field: "test1", Ascending: newBool(true)},
{Field: "test2", Ascending: newBool(false)},
},
Compression: driver.PrimarySortCompressionLz4,
},
Fields: []driver.InvertedIndexField{
{Name: "field1", Features: []driver.ArangoSearchAnalyzerFeature{driver.ArangoSearchAnalyzerFeatureFrequency}, Nested: nil},
{Name: "field2", Features: []driver.ArangoSearchAnalyzerFeature{driver.ArangoSearchAnalyzerFeaturePosition}, TrackListPositions: false, Nested: nil},
},
}
idx, created, err := col.EnsureInvertedIndex(ctx, &indexOpt)
require.NoError(t, err)
require.True(t, created)
require.Equal(t, nameInvInd, idx.UserName())

opt := driver.ArangoSearchAliasViewProperties{
Indexes: []driver.ArangoSearchAliasIndex{
{
Collection: nameCol,
Index: nameInvInd,
},
},
}
p, err = v.SetProperties(ctx, opt)
require.NoError(t, err)
require.Equal(t, p.Type, driver.ViewTypeArangoSearchAlias)
require.Equal(t, p.Name, nameAlias)
require.Len(t, p.Indexes, 1)
require.Equal(t, p.Indexes[0].Collection, nameCol)
require.Equal(t, p.Indexes[0].Index, nameInvInd)

p, err = v.Properties(ctx)
require.NoError(t, err)
require.Equal(t, p.Type, driver.ViewTypeArangoSearchAlias)
require.Equal(t, p.Name, nameAlias)
require.Len(t, p.Indexes, 1)

views, err := db.Views(ctx)
require.NoError(t, err)
require.Len(t, views, 1)

exist, err := db.ViewExists(ctx, nameAlias)
require.NoError(t, err)
require.True(t, exist)

vv, err := db.View(ctx, nameAlias)
require.NoError(t, err)
require.Equal(t, vv.Name(), nameAlias)

err = v.Remove(ctx)
require.NoError(t, err)

views, err = db.Views(ctx)
require.NoError(t, err)
require.Len(t, views, 0)
}
4 changes: 4 additions & 0 deletions view.go
Expand Up @@ -39,6 +39,10 @@ type View interface {
// When the type of the view is not ArangoSearch, an error is returned.
ArangoSearchView() (ArangoSearchView, error)

// ArangoSearchViewAlias returns this view as an ArangoSearch view alias.
// When the type of the view is not ArangoSearch alias, an error is returned.
ArangoSearchViewAlias() (ArangoSearchViewAlias, error)

// Database returns the database containing the view.
Database() Database

Expand Down
54 changes: 54 additions & 0 deletions view_arangosearch_alias.go
@@ -0,0 +1,54 @@
//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
"context"
)

// ArangoSearchViewAlias provides access to the information of a view alias
// Views aliases are only available in ArangoDB 3.10 and higher.
type ArangoSearchViewAlias interface {
// View Include generic View functions
View

// Properties fetches extended information about the view.
Properties(ctx context.Context) (ArangoSearchAliasViewProperties, error)

// SetProperties changes properties of the view.
SetProperties(ctx context.Context, options ArangoSearchAliasViewProperties) (ArangoSearchAliasViewProperties, error)
}

type ArangoSearchAliasViewProperties struct {
ArangoSearchViewBase

// Indexes A list of inverted indexes to add to the View.
Indexes []ArangoSearchAliasIndex `json:"indexes,omitempty"`
}

type ArangoSearchAliasIndex struct {
// Collection The name of a collection.
Collection string `json:"collection"`
// Index The name of an inverted index of the collection.
Index string `json:"index"`
}
78 changes: 78 additions & 0 deletions view_arangosearch_alias_impl.go
@@ -0,0 +1,78 @@
//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
"context"
"path"
)

// viewArangoSearchAlias implements ArangoSearchViewAlias
type viewArangoSearchAlias struct {
view
}

// Properties fetches extended information about the view.
func (v *viewArangoSearchAlias) Properties(ctx context.Context) (ArangoSearchAliasViewProperties, error) {
req, err := v.conn.NewRequest("GET", path.Join(v.relPath(), "properties"))
if err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
applyContextSettings(ctx, req)
resp, err := v.conn.Do(ctx, req)
if err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
if err := resp.CheckStatus(200); err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
var data ArangoSearchAliasViewProperties
if err := resp.ParseBody("", &data); err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
return data, nil
}

// SetProperties changes properties of the view.
func (v *viewArangoSearchAlias) SetProperties(ctx context.Context, options ArangoSearchAliasViewProperties) (ArangoSearchAliasViewProperties, error) {
req, err := v.conn.NewRequest("PUT", path.Join(v.relPath(), "properties"))
if err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
if _, err := req.SetBody(options); err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
applyContextSettings(ctx, req)
resp, err := v.conn.Do(ctx, req)
if err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
if err := resp.CheckStatus(200); err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
var data ArangoSearchAliasViewProperties
if err := resp.ParseBody("", &data); err != nil {
return ArangoSearchAliasViewProperties{}, WithStack(err)
}
return data, nil
}
7 changes: 7 additions & 0 deletions view_impl.go
Expand Up @@ -80,6 +80,13 @@ func (v *view) ArangoSearchView() (ArangoSearchView, error) {
return &viewArangoSearch{view: *v}, nil
}

func (v *view) ArangoSearchViewAlias() (ArangoSearchViewAlias, error) {
if v.viewType != ViewTypeArangoSearchAlias {
return nil, WithStack(newArangoError(http.StatusConflict, 0, fmt.Sprintf("Type must be '%s', got '%s'", ViewTypeArangoSearchAlias, v.viewType)))
}
return &viewArangoSearchAlias{view: *v}, nil
}

// Database returns the database containing the view.
func (v *view) Database() Database {
return v.db
Expand Down

0 comments on commit d8dfd63

Please sign in to comment.