github.com/weaviate/weaviate@v1.24.6/adapters/handlers/grpc/v1/service.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package v1
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"time"
    18  
    19  	"github.com/sirupsen/logrus"
    20  	enterrors "github.com/weaviate/weaviate/entities/errors"
    21  
    22  	"github.com/weaviate/weaviate/usecases/config"
    23  
    24  	"github.com/weaviate/weaviate/usecases/objects"
    25  
    26  	"github.com/weaviate/weaviate/entities/additional"
    27  	"github.com/weaviate/weaviate/entities/dto"
    28  	"github.com/weaviate/weaviate/entities/schema"
    29  	pb "github.com/weaviate/weaviate/grpc/generated/protocol/v1"
    30  	"github.com/weaviate/weaviate/usecases/auth/authentication/composer"
    31  	schemaManager "github.com/weaviate/weaviate/usecases/schema"
    32  	"github.com/weaviate/weaviate/usecases/traverser"
    33  )
    34  
    35  type Service struct {
    36  	pb.UnimplementedWeaviateServer
    37  	traverser            *traverser.Traverser
    38  	authComposer         composer.TokenFunc
    39  	allowAnonymousAccess bool
    40  	schemaManager        *schemaManager.Manager
    41  	batchManager         *objects.BatchManager
    42  	config               *config.Config
    43  	logger               logrus.FieldLogger
    44  }
    45  
    46  func NewService(traverser *traverser.Traverser, authComposer composer.TokenFunc,
    47  	allowAnonymousAccess bool, schemaManager *schemaManager.Manager,
    48  	batchManager *objects.BatchManager, config *config.Config, logger logrus.FieldLogger,
    49  ) *Service {
    50  	return &Service{
    51  		traverser:            traverser,
    52  		authComposer:         authComposer,
    53  		allowAnonymousAccess: allowAnonymousAccess,
    54  		schemaManager:        schemaManager,
    55  		batchManager:         batchManager,
    56  		config:               config,
    57  		logger:               logger,
    58  	}
    59  }
    60  
    61  func (s *Service) BatchDelete(ctx context.Context, req *pb.BatchDeleteRequest) (*pb.BatchDeleteReply, error) {
    62  	before := time.Now()
    63  	principal, err := s.principalFromContext(ctx)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("extract auth: %w", err)
    66  	}
    67  	replicationProperties := extractReplicationProperties(req.ConsistencyLevel)
    68  	scheme := s.schemaManager.GetSchemaSkipAuth()
    69  
    70  	params, err := batchDeleteParamsFromProto(req, scheme)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("batch delete params: %w", err)
    73  	}
    74  
    75  	tenant := ""
    76  	if req.Tenant != nil {
    77  		tenant = *req.Tenant
    78  	}
    79  
    80  	response, err := s.batchManager.DeleteObjectsFromGRPC(ctx, principal, params, replicationProperties, tenant)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("batch delete: %w", err)
    83  	}
    84  
    85  	result, err := batchDeleteReplyFromObjects(response, req.Verbose)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("batch delete reply: %w", err)
    88  	}
    89  	result.Took = float32(time.Since(before).Seconds())
    90  
    91  	return result, nil
    92  }
    93  
    94  func (s *Service) BatchObjects(ctx context.Context, req *pb.BatchObjectsRequest) (*pb.BatchObjectsReply, error) {
    95  	before := time.Now()
    96  	principal, err := s.principalFromContext(ctx)
    97  	if err != nil {
    98  		return nil, fmt.Errorf("extract auth: %w", err)
    99  	}
   100  	scheme := s.schemaManager.GetSchemaSkipAuth()
   101  
   102  	objs, objOriginalIndex, objectParsingErrors := batchFromProto(req, scheme)
   103  
   104  	replicationProperties := extractReplicationProperties(req.ConsistencyLevel)
   105  
   106  	all := "ALL"
   107  	response, err := s.batchManager.AddObjects(ctx, principal, objs, []*string{&all}, replicationProperties)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	var objErrors []*pb.BatchObjectsReply_BatchError
   112  
   113  	for i, obj := range response {
   114  		if obj.Err != nil {
   115  			objErrors = append(objErrors, &pb.BatchObjectsReply_BatchError{Index: int32(objOriginalIndex[i]), Error: obj.Err.Error()})
   116  		}
   117  	}
   118  
   119  	for i, err := range objectParsingErrors {
   120  		objErrors = append(objErrors, &pb.BatchObjectsReply_BatchError{Index: int32(i), Error: err.Error()})
   121  	}
   122  
   123  	result := &pb.BatchObjectsReply{
   124  		Took:   float32(time.Since(before).Seconds()),
   125  		Errors: objErrors,
   126  	}
   127  	return result, nil
   128  }
   129  
   130  func (s *Service) Search(ctx context.Context, req *pb.SearchRequest) (*pb.SearchReply, error) {
   131  	before := time.Now()
   132  
   133  	principal, err := s.principalFromContext(ctx)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("extract auth: %w", err)
   136  	}
   137  
   138  	scheme := s.schemaManager.GetSchemaSkipAuth()
   139  
   140  	type reply struct {
   141  		Result *pb.SearchReply
   142  		Error  error
   143  	}
   144  
   145  	c := make(chan reply, 1)
   146  	f := func() {
   147  		defer func() {
   148  			if err := recover(); err != nil {
   149  				c <- reply{
   150  					Result: nil,
   151  					Error:  fmt.Errorf("panic occurred: %v", err),
   152  				}
   153  			}
   154  		}()
   155  
   156  		searchParams, err := searchParamsFromProto(req, scheme, s.config)
   157  		if err != nil {
   158  			c <- reply{
   159  				Result: nil,
   160  				Error:  fmt.Errorf("extract params: %w", err),
   161  			}
   162  		}
   163  
   164  		if err := s.validateClassAndProperty(searchParams); err != nil {
   165  			c <- reply{
   166  				Result: nil,
   167  				Error:  err,
   168  			}
   169  		}
   170  
   171  		res, err := s.traverser.GetClass(ctx, principal, searchParams)
   172  		if err != nil {
   173  			c <- reply{
   174  				Result: nil,
   175  				Error:  err,
   176  			}
   177  		}
   178  
   179  		proto, err := searchResultsToProto(res, before, searchParams, scheme, req.Uses_123Api)
   180  		c <- reply{
   181  			Result: proto,
   182  			Error:  err,
   183  		}
   184  	}
   185  	enterrors.GoWrapper(f, s.logger)
   186  	res := <-c
   187  	return res.Result, res.Error
   188  }
   189  
   190  func (s *Service) validateClassAndProperty(searchParams dto.GetParams) error {
   191  	scheme := s.schemaManager.GetSchemaSkipAuth()
   192  	class, err := schema.GetClassByName(scheme.Objects, searchParams.ClassName)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	for _, prop := range searchParams.Properties {
   198  		_, err := schema.GetPropertyByName(class, prop.Name)
   199  		if err != nil {
   200  			return err
   201  		}
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  func extractReplicationProperties(level *pb.ConsistencyLevel) *additional.ReplicationProperties {
   208  	if level == nil {
   209  		return nil
   210  	}
   211  
   212  	switch *level {
   213  	case pb.ConsistencyLevel_CONSISTENCY_LEVEL_ONE:
   214  		return &additional.ReplicationProperties{ConsistencyLevel: "ONE"}
   215  	case pb.ConsistencyLevel_CONSISTENCY_LEVEL_QUORUM:
   216  		return &additional.ReplicationProperties{ConsistencyLevel: "QUORUM"}
   217  	case pb.ConsistencyLevel_CONSISTENCY_LEVEL_ALL:
   218  		return &additional.ReplicationProperties{ConsistencyLevel: "ALL"}
   219  	default:
   220  		return nil
   221  	}
   222  }