KevinStephenson
Adding in weaviate code
b110593
raw
history blame
4.69 kB
// _ _
// __ _____ __ ___ ___ __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
// \ V V / __/ (_| |\ V /| | (_| | || __/
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
// CONTACT: [email protected]
//
package filters
import (
"fmt"
"strings"
"github.com/weaviate/weaviate/entities/schema"
)
// Represents the path in a filter.
// Either RelationProperty or PrimitiveProperty must be empty (e.g. "").
type Path struct {
Class schema.ClassName `json:"class"`
Property schema.PropertyName `json:"property"`
// If nil, then this is the property we're interested in.
// If a pointer to another Path, the constraint applies to that one.
Child *Path `json:"child"`
}
// GetInnerMost recursively searches for child paths, only when no more
// children can be found will the path be returned
func (p *Path) GetInnerMost() *Path {
if p.Child == nil {
return p
}
return p.Child.GetInnerMost()
}
// Slice flattens the nested path into a slice of segments
func (p *Path) Slice() []string {
return appendNestedPath(p, true)
}
func (p *Path) SliceInterface() []interface{} {
path := appendNestedPath(p, true)
out := make([]interface{}, len(path))
for i, element := range path {
out[i] = element
}
return out
}
// TODO: This is now identical with Slice(), so it can be removed once all
// callers have been adopted
func (p *Path) SliceNonTitleized() []string {
return appendNestedPath(p, true)
}
func appendNestedPath(p *Path, omitClass bool) []string {
result := []string{}
if !omitClass {
result = append(result, string(p.Class))
}
if p.Child != nil {
property := string(p.Property)
result = append(result, property)
result = append(result, appendNestedPath(p.Child, false)...)
} else {
result = append(result, string(p.Property))
}
return result
}
// ParsePath Parses the path
// It parses an array of strings in this format
// [0] ClassName -> The root class name we're drilling down from
// [1] propertyName -> The property name we're interested in.
func ParsePath(pathElements []interface{}, rootClass string) (*Path, error) {
// we need to manually insert the root class, as that is omitted from the user
pathElements = append([]interface{}{rootClass}, pathElements...)
// The sentinel is used to bootstrap the inlined recursion.
// we return sentinel.Child at the end.
var sentinel Path
// Keep track of where we are in the path (e.g. always points to latest Path segment)
current := &sentinel
// Now go through the path elements, step over it in increments of two.
// Simple case: ClassName -> property
// Nested path case: ClassName -> HasRef -> ClassOfRef -> Property
for i := 0; i < len(pathElements); i += 2 {
lengthRemaining := len(pathElements) - i
if lengthRemaining < 2 {
return nil, fmt.Errorf("missing an argument after '%s'", pathElements[i])
}
rawClassName, ok := pathElements[i].(string)
if !ok {
return nil, fmt.Errorf("element %v is not a string", i+1)
}
rawPropertyName, ok := pathElements[i+1].(string)
if !ok {
return nil, fmt.Errorf("element %v is not a string", i+2)
}
className, err := schema.ValidateClassName(rawClassName)
if err != nil {
return nil, fmt.Errorf("Expected a valid class name in 'path' field for the filter but got '%s'", rawClassName)
}
var propertyName schema.PropertyName
lengthPropName, isPropLengthFilter := schema.IsPropertyLength(rawPropertyName, 0)
if isPropLengthFilter {
// check if property in len(PROPERTY) is valid
_, err = schema.ValidatePropertyName(lengthPropName)
if err != nil {
return nil, fmt.Errorf("Expected a valid property name in 'path' field for the filter, but got '%s'", lengthPropName)
}
propertyName = schema.PropertyName(rawPropertyName)
} else {
propertyName, err = schema.ValidatePropertyName(rawPropertyName)
// Invalid property name?
// Try to parse it as as a reference or a length.
if err != nil {
untitlizedPropertyName := strings.ToLower(rawPropertyName[0:1]) + rawPropertyName[1:]
propertyName, err = schema.ValidatePropertyName(untitlizedPropertyName)
if err != nil {
return nil, fmt.Errorf("Expected a valid property name in 'path' field for the filter, but got '%s'", rawPropertyName)
}
}
}
current.Child = &Path{
Class: className,
Property: propertyName,
}
// And down we go.
current = current.Child
}
return sentinel.Child, nil
}