Spaces:
Running
Running
File size: 4,688 Bytes
b110593 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
// _ _
// __ _____ __ ___ ___ __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
// \ 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
}
|