<commit_before>package classification

import (
	"context"
	"fmt"

	"github.com/go-openapi/strfmt"
	"github.com/semi-technologies/weaviate/entities/filters"
	"github.com/semi-technologies/weaviate/entities/models"
	"github.com/semi-technologies/weaviate/entities/schema"
	"github.com/semi-technologies/weaviate/entities/schema/crossref"
	"github.com/semi-technologies/weaviate/entities/schema/kind"
	"github.com/semi-technologies/weaviate/entities/search"
	"github.com/semi-technologies/weaviate/usecases/traverser"
)

func (c *Classifier) classifyItemContextual(item search.Result, kind kind.Kind, params models.Classification) error {
	s := c.schemaGetter.GetSchemaSkipAuth()
	// safe to skip nil-check as we passed validation

	var classified []string
	for _, propName := range params.ClassifyProperties {
		prop, err := s.GetProperty(kind, schema.ClassName(params.Class), schema.PropertyName(propName))
		if err != nil {
			return fmt.Errorf("contextual: get target prop '%s': %v", propName, err)
		}

		dataType, err := s.FindPropertyDataType(prop.DataType)
		if err != nil {
			return fmt.Errorf("contextual: extract dataType of prop '%s': %v", propName, err)
		}

		// we have passed validation, so it is safe to assume that this is a ref prop
		targetClasses := dataType.Classes()

		// len=1 is guaranteed from validation
		targetClass := targetClasses[0]
		targetKind, _ := s.GetKindOfClass(targetClass)

		res, err := c.vectorRepo.VectorClassSearch(context.Background(), traverser.GetParams{
			SearchVector: item.Vector,
			ClassName:    targetClass.String(),
			Kind:         targetKind,
			Pagination: &filters.Pagination{
				Limit: 1,
			},
			Properties: traverser.SelectProperties{
				traverser.SelectProperty{
					Name: "uuid",
				},
			},
		})
		if err != nil {
			return fmt.Errorf("contextual: search closest target: %v", err)
		}

		if res == nil || len(res) == 0 {
			return fmt.Errorf("contexual: no potential targets found of class '%s' (%s)", targetClass, targetKind)
		}

		targetBeacon := crossref.New("localhost", res[0].ID, res[0].Kind).String()
		item.Schema.(map[string]interface{})[propName] = models.MultipleRef{
			&models.SingleRef{
				Beacon: strfmt.URI(targetBeacon),
				Meta: &models.ReferenceMeta{
					Classification: &models.ReferenceMetaClassification{
						WinningDistance: float64(9000), // TODO
					},
				},
			},
		}

		// append list of actually classified (can differ from scope!) properties,
		// so we can build the object meta information
		classified = append(classified, propName)
	}

	c.extendItemWithObjectMeta(&item, params, classified)
	err := c.store(item)
	if err != nil {
		return fmt.Errorf("store %s/%s: %v", item.ClassName, item.ID, err)
	}

	return nil
}<commit_msg>gh-1033 clean up classify item contextually<commit_after>package classification

import (
	"context"
	"fmt"

	"github.com/go-openapi/strfmt"
	"github.com/semi-technologies/weaviate/entities/filters"
	"github.com/semi-technologies/weaviate/entities/models"
	"github.com/semi-technologies/weaviate/entities/schema"
	"github.com/semi-technologies/weaviate/entities/schema/crossref"
	"github.com/semi-technologies/weaviate/entities/schema/kind"
	"github.com/semi-technologies/weaviate/entities/search"
	"github.com/semi-technologies/weaviate/usecases/traverser"
)

type contextualItemClassifier struct {
	item       search.Result
	kind       kind.Kind
	params     models.Classification
	classifier *Classifier
	schema     schema.Schema
}

func (c *Classifier) classifyItemContextual(item search.Result, kind kind.Kind, params models.Classification) error {
	schema := c.schemaGetter.GetSchemaSkipAuth()
	run := &contextualItemClassifier{
		item:       item,
		kind:       kind,
		params:     params,
		classifier: c,
		schema:     schema,
	}

	err := run.do()
	if err != nil {
		return fmt.Errorf("contextual: %v", err)
	}

	return nil
}

func (c *contextualItemClassifier) do() error {
	var classified []string
	for _, propName := range c.params.ClassifyProperties {
		current, err := c.property(propName)
		if err != nil {
			return fmt.Errorf("prop '%s': %v", propName, err)
		}

		// append list of actually classified (can differ from scope!) properties,
		// so we can build the object meta information
		classified = append(classified, current)
	}

	c.classifier.extendItemWithObjectMeta(&c.item, c.params, classified)
	err := c.classifier.store(c.item)
	if err != nil {
		return fmt.Errorf("store %s/%s: %v", c.item.ClassName, c.item.ID, err)
	}

	return nil
}

func (c *contextualItemClassifier) property(propName string) (string, error) {
	targetClass, targetKind, err := c.classAndKindOfTarget(propName)
	if err != nil {
		return "", fmt.Errorf("inspect target: %v", err)
	}

	res, err := c.findTarget(targetClass, targetKind)
	if err != nil {
		return "", fmt.Errorf("find target: %v", err)
	}

	targetBeacon := crossref.New("localhost", res.ID, res.Kind).String()
	c.item.Schema.(map[string]interface{})[propName] = models.MultipleRef{
		&models.SingleRef{
			Beacon: strfmt.URI(targetBeacon),
			Meta: &models.ReferenceMeta{
				Classification: &models.ReferenceMetaClassification{
					WinningDistance: float64(9000), // TODO
				},
			},
		},
	}

	return propName, nil
}

func (c *contextualItemClassifier) classAndKindOfTarget(propName string) (schema.ClassName, kind.Kind, error) {
	prop, err := c.schema.GetProperty(c.kind, schema.ClassName(c.params.Class), schema.PropertyName(propName))
	if err != nil {
		return "", "", fmt.Errorf("get target prop '%s': %v", propName, err)
	}

	dataType, err := c.schema.FindPropertyDataType(prop.DataType)
	if err != nil {
		return "", "", fmt.Errorf("extract dataType of prop '%s': %v", propName, err)
	}

	// we have passed validation, so it is safe to assume that this is a ref prop
	targetClasses := dataType.Classes()

	// len=1 is guaranteed from validation
	targetClass := targetClasses[0]
	targetKind, _ := c.schema.GetKindOfClass(targetClass)

	return targetClass, targetKind, nil
}

func (c *contextualItemClassifier) findTarget(targetClass schema.ClassName, targetKind kind.Kind) (*search.Result, error) {
	res, err := c.classifier.vectorRepo.VectorClassSearch(context.Background(), traverser.GetParams{
		SearchVector: c.item.Vector,
		ClassName:    targetClass.String(),
		Kind:         targetKind,
		Pagination: &filters.Pagination{
			Limit: 1,
		},
		Properties: traverser.SelectProperties{
			traverser.SelectProperty{
				Name: "uuid",
			},
		},
	})
	if err != nil {
		return nil, fmt.Errorf("search closest target: %v", err)
	}

	if res == nil || len(res) == 0 {
		return nil, fmt.Errorf("no potential targets found of class '%s' (%s)", targetClass, targetKind)
	}

	return &res[0], nil
}
{'fraction_non_alphanumeric': 0.13475600656618414, 'fraction_numerical': 0.004029249365766304, 'mean_word_length': 4.22369446609509, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6099233', 'n_tokens_mistral': 2359, 'n_tokens_neox': 2193, 'n_words': 506}