github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/vector/hnsw/heuristic.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 hnsw 13 14 import ( 15 "context" 16 17 "github.com/pkg/errors" 18 "github.com/weaviate/weaviate/adapters/repos/db/helpers" 19 "github.com/weaviate/weaviate/adapters/repos/db/priorityqueue" 20 "github.com/weaviate/weaviate/entities/storobj" 21 ) 22 23 func (h *hnsw) selectNeighborsHeuristic(input *priorityqueue.Queue[any], 24 max int, denyList helpers.AllowList, 25 ) error { 26 if input.Len() < max { 27 return nil 28 } 29 30 // TODO, if this solution stays we might need something with fewer allocs 31 ids := make([]uint64, input.Len()) 32 33 closestFirst := h.pools.pqHeuristic.GetMin(input.Len()) 34 i := uint64(0) 35 for input.Len() > 0 { 36 elem := input.Pop() 37 closestFirst.InsertWithValue(elem.ID, elem.Dist, i) 38 ids[i] = elem.ID 39 i++ 40 } 41 42 var returnList []priorityqueue.Item[uint64] 43 44 if h.compressed.Load() { 45 bag := h.compressor.NewBag() 46 for _, id := range ids { 47 err := bag.Load(context.Background(), id) 48 if err != nil { 49 return err 50 } 51 } 52 53 returnList = h.pools.pqItemSlice.Get().([]priorityqueue.Item[uint64]) 54 for closestFirst.Len() > 0 && len(returnList) < max { 55 curr := closestFirst.Pop() 56 if denyList != nil && denyList.Contains(curr.ID) { 57 continue 58 } 59 distToQuery := curr.Dist 60 61 good := true 62 for _, item := range returnList { 63 peerDist, err := bag.Distance(curr.ID, item.ID) 64 if err != nil { 65 return err 66 } 67 68 if peerDist < distToQuery { 69 good = false 70 break 71 } 72 } 73 74 if good { 75 returnList = append(returnList, curr) 76 } 77 78 } 79 } else { 80 81 vecs, errs := h.multiVectorForID(context.TODO(), ids) 82 83 returnList = h.pools.pqItemSlice.Get().([]priorityqueue.Item[uint64]) 84 85 for closestFirst.Len() > 0 && len(returnList) < max { 86 curr := closestFirst.Pop() 87 if denyList != nil && denyList.Contains(curr.ID) { 88 continue 89 } 90 distToQuery := curr.Dist 91 92 currVec := vecs[curr.Value] 93 if err := errs[curr.Value]; err != nil { 94 var e storobj.ErrNotFound 95 if errors.As(err, &e) { 96 h.handleDeletedNode(e.DocID) 97 continue 98 } else { 99 // not a typed error, we can recover from, return with err 100 return errors.Wrapf(err, 101 "unrecoverable error for docID %d", curr.ID) 102 } 103 } 104 good := true 105 for _, item := range returnList { 106 peerDist, _, _ := h.distancerProvider.SingleDist(currVec, 107 vecs[item.Value]) 108 109 if peerDist < distToQuery { 110 good = false 111 break 112 } 113 } 114 115 if good { 116 returnList = append(returnList, curr) 117 } 118 119 } 120 } 121 122 h.pools.pqHeuristic.Put(closestFirst) 123 124 for _, retElem := range returnList { 125 input.Insert(retElem.ID, retElem.Dist) 126 } 127 128 // rewind and return to pool 129 returnList = returnList[:0] 130 131 //nolint:staticcheck 132 h.pools.pqItemSlice.Put(returnList) 133 134 return nil 135 }