Spaces:
Running
Running
File size: 3,826 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 153 154 155 |
// _ _
// __ _____ __ ___ ___ __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
// \ V V / __/ (_| |\ V /| | (_| | || __/
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
// CONTACT: [email protected]
//
package schema
import (
"sort"
"github.com/weaviate/weaviate/entities/filters"
"github.com/weaviate/weaviate/entities/models"
libschema "github.com/weaviate/weaviate/entities/schema"
)
// RefFinder is a helper that lists classes and their possible paths to to a
// desired target class.
//
// For example if the target class is "car". It might list:
// - Person, drives, Car
// - Person, owns, Car
// - Person, friendsWith, Person, drives, Car
// etc.
//
// It will stop at a preconfigured depth limit, to avoid infinite results, such
// as:
// - Person, friendsWith, Person, friendsWith, Person, ..., drives Car
type RefFinder struct {
schemaGetter schemaGetterForRefFinder
depthLimit int
}
// NewRefFinder with SchemaGetter and depth limit
func NewRefFinder(getter schemaGetterForRefFinder, depthLimit int) *RefFinder {
return &RefFinder{
schemaGetter: getter,
depthLimit: depthLimit,
}
}
type schemaGetterForRefFinder interface {
GetSchemaSkipAuth() libschema.Schema
}
func (r *RefFinder) Find(className libschema.ClassName) []filters.Path {
schema := r.schemaGetter.GetSchemaSkipAuth()
var classes []*models.Class
if schema.Objects != nil {
classes = append(classes, schema.Objects.Classes...)
}
return r.findInClassList(className, classes, schema)
}
func (r *RefFinder) findInClassList(needle libschema.ClassName, classes []*models.Class,
schema libschema.Schema,
) []filters.Path {
var out []filters.Path
for _, class := range classes {
path, ok := r.hasRefTo(needle, class, schema, 1)
if !ok {
continue
}
out = append(out, path...)
}
return r.sortByPathLen(out)
}
func (r *RefFinder) hasRefTo(needle libschema.ClassName, class *models.Class,
schema libschema.Schema, depth int,
) ([]filters.Path, bool) {
var out []filters.Path
if depth > r.depthLimit {
return nil, false
}
for _, prop := range class.Properties {
dt, err := schema.FindPropertyDataType(prop.DataType)
if err != nil {
// silently ignore, maybe the property was deleted in the meantime
continue
}
if !dt.IsReference() {
continue
}
for _, haystack := range dt.Classes() {
refs := r.refsPerClass(needle, class, prop.Name, haystack, schema, depth)
out = append(out, refs...)
}
}
return out, len(out) > 0
}
func (r *RefFinder) refsPerClass(needle libschema.ClassName, class *models.Class,
propName string, haystack libschema.ClassName, schema libschema.Schema,
depth int,
) []filters.Path {
if haystack == needle {
// direct match
return []filters.Path{
{
Class: libschema.ClassName(class.Class),
Property: libschema.PropertyName(propName),
Child: &filters.Path{
Class: needle,
Property: "id",
},
},
}
}
// could still be an indirect (recursive) match
innerClass := schema.FindClassByName(haystack)
if innerClass == nil {
return nil
}
paths, ok := r.hasRefTo(needle, innerClass, schema, depth+1)
if !ok {
return nil
}
var out []filters.Path
for _, path := range paths {
out = append(out, filters.Path{
Class: libschema.ClassName(class.Class),
Property: libschema.PropertyName(propName),
Child: &path,
})
}
return out
}
func (r *RefFinder) sortByPathLen(in []filters.Path) []filters.Path {
sort.Slice(in, func(i, j int) bool {
return len(in[i].Slice()) < len(in[j].Slice())
})
return in
}
|