Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Hide map implementation in id.Set |
---|---|
Timelines: | family | ancestors | descendants | both | newset |
Files: | files | file ages | folders |
SHA3-256: |
c43c1b2d0415b12e6200b5b79e11b0fd |
User & Date: | stern 2024-06-23 16:10:51 |
Context
2024-06-23
| ||
16:36 | Refactor id.Set implementation to prepare for slice implementation ... (check-in: 0afce18ab3 user: stern tags: newset) | |
16:10 | Hide map implementation in id.Set ... (check-in: c43c1b2d04 user: stern tags: newset) | |
15:00 | Update goldmark dependency ... (check-in: 7d1c721311 user: stern tags: trunk) | |
Changes
Changes to auth/policy/box.go.
︙ | ︙ | |||
74 75 76 77 78 79 80 | return zettel.Zettel{}, box.NewErrNotAllowed("GetZettel", user, zid) } func (pp *polBox) GetAllZettel(ctx context.Context, zid id.Zid) ([]zettel.Zettel, error) { return pp.box.GetAllZettel(ctx, zid) } | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | return zettel.Zettel{}, box.NewErrNotAllowed("GetZettel", user, zid) } func (pp *polBox) GetAllZettel(ctx context.Context, zid id.Zid) ([]zettel.Zettel, error) { return pp.box.GetAllZettel(ctx, zid) } func (pp *polBox) FetchZids(ctx context.Context) (*id.Set, error) { return nil, box.NewErrNotAllowed("fetch-zids", server.GetUser(ctx), id.Invalid) } func (pp *polBox) GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error) { m, err := pp.box.GetMeta(ctx, zid) if err != nil { return nil, err |
︙ | ︙ |
Changes to box/box.go.
︙ | ︙ | |||
134 135 136 137 138 139 140 | // Box is to be used outside the box package and its descendants. type Box interface { BaseBox WriteBox // FetchZids returns the set of all zettel identifer managed by the box. | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | // Box is to be used outside the box package and its descendants. type Box interface { BaseBox WriteBox // FetchZids returns the set of all zettel identifer managed by the box. FetchZids(ctx context.Context) (*id.Set, error) // GetMeta returns the metadata of the zettel with the given identifier. GetMeta(context.Context, id.Zid) (*meta.Meta, error) // SelectMeta returns a list of metadata that comply to the given selection criteria. // If `metaSeq` is `nil`, the box assumes metadata of all available zettel. SelectMeta(ctx context.Context, metaSeq []*meta.Meta, q *query.Query) ([]*meta.Meta, error) |
︙ | ︙ |
Changes to box/manager/anteroom.go.
︙ | ︙ | |||
25 26 27 28 29 30 31 | arNothing arAction = iota arReload arZettel ) type anteroom struct { next *anteroom | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | arNothing arAction = iota arReload arZettel ) type anteroom struct { next *anteroom waiting *id.Set curLoad int reload bool } type anteroomQueue struct { mx sync.Mutex first *anteroom |
︙ | ︙ | |||
54 55 56 57 58 59 60 | ar.last = ar.first return } for room := ar.first; room != nil; room = room.next { if room.reload { continue // Do not put zettel in reload room } | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ar.last = ar.first return } for room := ar.first; room != nil; room = room.next { if room.reload { continue // Do not put zettel in reload room } if room.waiting.Contains(zid) { // Zettel is already waiting. Nothing to do. return } } if room := ar.last; !room.reload && (ar.maxLoad == 0 || room.curLoad < ar.maxLoad) { room.waiting.Add(zid) room.curLoad++ |
︙ | ︙ | |||
84 85 86 87 88 89 90 | func (ar *anteroomQueue) Reset() { ar.mx.Lock() defer ar.mx.Unlock() ar.first = &anteroom{next: nil, waiting: nil, curLoad: 0, reload: true} ar.last = ar.first } | | | | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | func (ar *anteroomQueue) Reset() { ar.mx.Lock() defer ar.mx.Unlock() ar.first = &anteroom{next: nil, waiting: nil, curLoad: 0, reload: true} ar.last = ar.first } func (ar *anteroomQueue) Reload(allZids *id.Set) { ar.mx.Lock() defer ar.mx.Unlock() ar.deleteReloadedRooms() if !allZids.IsEmpty() { ar.first = &anteroom{next: ar.first, waiting: allZids, curLoad: allZids.Length(), reload: true} if ar.first.next == nil { ar.last = ar.first } } else { ar.first = nil ar.last = nil } |
︙ | ︙ | |||
120 121 122 123 124 125 126 | defer ar.mx.Unlock() first := ar.first if first != nil { if first.waiting == nil && first.reload { ar.removeFirst() return arReload, id.Invalid, false } | | | < | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | defer ar.mx.Unlock() first := ar.first if first != nil { if first.waiting == nil && first.reload { ar.removeFirst() return arReload, id.Invalid, false } if zid, found := first.waiting.Pop(); found { if first.waiting.IsEmpty() { ar.removeFirst() } return arZettel, zid, first.reload } ar.removeFirst() } return arNothing, id.Invalid, false |
︙ | ︙ |
Changes to box/manager/box.go.
︙ | ︙ | |||
109 110 111 112 113 114 115 | result = append(result, z) } } return result, nil } // FetchZids returns the set of all zettel identifer managed by the box. | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | result = append(result, z) } } return result, nil } // FetchZids returns the set of all zettel identifer managed by the box. func (mgr *Manager) FetchZids(ctx context.Context) (*id.Set, error) { mgr.mgrLog.Debug().Msg("FetchZids") if mgr.State() != box.StartStateStarted { return nil, box.ErrStopped } result := id.NewSet() mgr.mgrMx.RLock() defer mgr.mgrMx.RUnlock() for _, p := range mgr.boxes { err := p.ApplyZid(ctx, func(zid id.Zid) { result.Add(zid) }, func(id.Zid) bool { return true }) if err != nil { return nil, err } |
︙ | ︙ | |||
174 175 176 177 178 179 180 | compSearch := q.RetrieveAndCompile(ctx, mgr, metaSeq) if result := compSearch.Result(); result != nil { mgr.mgrLog.Trace().Int("count", int64(len(result))).Msg("found without ApplyMeta") return result, nil } selected := map[id.Zid]*meta.Meta{} for _, term := range compSearch.Terms { | | | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | compSearch := q.RetrieveAndCompile(ctx, mgr, metaSeq) if result := compSearch.Result(); result != nil { mgr.mgrLog.Trace().Int("count", int64(len(result))).Msg("found without ApplyMeta") return result, nil } selected := map[id.Zid]*meta.Meta{} for _, term := range compSearch.Terms { rejected := id.NewSet() handleMeta := func(m *meta.Meta) { zid := m.Zid if rejected.Contains(zid) { mgr.mgrLog.Trace().Zid(zid).Msg("SelectMeta/alreadyRejected") return } if _, ok := selected[zid]; ok { mgr.mgrLog.Trace().Zid(zid).Msg("SelectMeta/alreadySelected") return } |
︙ | ︙ |
Changes to box/manager/collect.go.
︙ | ︙ | |||
19 20 21 22 23 24 25 | "zettelstore.de/z/ast" "zettelstore.de/z/box/manager/store" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" ) type collectData struct { | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | "zettelstore.de/z/ast" "zettelstore.de/z/box/manager/store" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" ) type collectData struct { refs *id.Set words store.WordSet urls store.WordSet } func (data *collectData) initialize() { data.refs = id.NewSet() data.words = store.NewWordSet() |
︙ | ︙ |
Changes to box/manager/indexer.go.
︙ | ︙ | |||
27 28 29 30 31 32 33 | "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // SearchEqual returns all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. | | | | | | | | | | 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 | "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // SearchEqual returns all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. func (mgr *Manager) SearchEqual(word string) *id.Set { found := mgr.idxStore.SearchEqual(word) mgr.idxLog.Debug().Str("word", word).Int("found", int64(found.Length())).Msg("SearchEqual") if msg := mgr.idxLog.Trace(); msg.Enabled() { msg.Str("ids", fmt.Sprint(found)).Msg("IDs") } return found } // SearchPrefix returns all zettel that have a word with the given prefix. // The prefix must be normalized through Unicode NKFD, trimmed and not empty. func (mgr *Manager) SearchPrefix(prefix string) *id.Set { found := mgr.idxStore.SearchPrefix(prefix) mgr.idxLog.Debug().Str("prefix", prefix).Int("found", int64(found.Length())).Msg("SearchPrefix") if msg := mgr.idxLog.Trace(); msg.Enabled() { msg.Str("ids", fmt.Sprint(found)).Msg("IDs") } return found } // SearchSuffix returns all zettel that have a word with the given suffix. // The suffix must be normalized through Unicode NKFD, trimmed and not empty. func (mgr *Manager) SearchSuffix(suffix string) *id.Set { found := mgr.idxStore.SearchSuffix(suffix) mgr.idxLog.Debug().Str("suffix", suffix).Int("found", int64(found.Length())).Msg("SearchSuffix") if msg := mgr.idxLog.Trace(); msg.Enabled() { msg.Str("ids", fmt.Sprint(found)).Msg("IDs") } return found } // SearchContains returns all zettel that contains the given string. // The string must be normalized through Unicode NKFD, trimmed and not empty. func (mgr *Manager) SearchContains(s string) *id.Set { found := mgr.idxStore.SearchContains(s) mgr.idxLog.Debug().Str("s", s).Int("found", int64(found.Length())).Msg("SearchContains") if msg := mgr.idxLog.Trace(); msg.Enabled() { msg.Str("ids", fmt.Sprint(found)).Msg("IDs") } return found } // idxIndexer runs in the background and updates the index data structures. |
︙ | ︙ | |||
205 206 207 208 209 210 211 | } } else { stWords.Add(value) } } func (mgr *Manager) idxProcessData(ctx context.Context, zi *store.ZettelIndex, cData *collectData) { | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | } } else { stWords.Add(value) } } func (mgr *Manager) idxProcessData(ctx context.Context, zi *store.ZettelIndex, cData *collectData) { cData.refs.ForEach(func(ref id.Zid) { if mgr.HasZettel(ctx, ref) { zi.AddBackRef(ref) } else { zi.AddDeadRef(ref) } }) zi.SetWords(cData.words) zi.SetUrls(cData.urls) } func (mgr *Manager) idxUpdateValue(ctx context.Context, inverseKey, value string, zi *store.ZettelIndex) { zid, err := id.Parse(value) if err != nil { |
︙ | ︙ | |||
242 243 244 245 246 247 248 | } func (mgr *Manager) idxDeleteZettel(ctx context.Context, zid id.Zid) { toCheck := mgr.idxStore.DeleteZettel(ctx, zid) mgr.idxCheckZettel(toCheck) } | | | | | 242 243 244 245 246 247 248 249 250 251 252 253 | } func (mgr *Manager) idxDeleteZettel(ctx context.Context, zid id.Zid) { toCheck := mgr.idxStore.DeleteZettel(ctx, zid) mgr.idxCheckZettel(toCheck) } func (mgr *Manager) idxCheckZettel(s *id.Set) { s.ForEach(func(zid id.Zid) { mgr.idxAr.EnqueueZettel(zid) }) } |
Changes to box/manager/mapstore/mapstore.go.
︙ | ︙ | |||
123 124 125 126 127 128 129 | updated = true } return updated } // SearchEqual returns all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | updated = true } return updated } // SearchEqual returns all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchEqual(word string) *id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := id.NewSet() if refs, ok := ms.words[word]; ok { result.CopySlice(refs) } if refs, ok := ms.urls[word]; ok { |
︙ | ︙ | |||
148 149 150 151 152 153 154 | addBackwardZids(result, zid, zi) return result } // SearchPrefix returns all zettel that have a word with the given prefix. // The prefix must be normalized through Unicode NKFD, trimmed and not empty. | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | addBackwardZids(result, zid, zi) return result } // SearchPrefix returns all zettel that have a word with the given prefix. // The prefix must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchPrefix(prefix string) *id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(prefix, strings.HasPrefix) l := len(prefix) if l > 14 { return result } |
︙ | ︙ | |||
179 180 181 182 183 184 185 | } } return result } // SearchSuffix returns all zettel that have a word with the given suffix. // The suffix must be normalized through Unicode NKFD, trimmed and not empty. | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | } } return result } // SearchSuffix returns all zettel that have a word with the given suffix. // The suffix must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchSuffix(suffix string) *id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(suffix, strings.HasSuffix) l := len(suffix) if l > 14 { return result } |
︙ | ︙ | |||
205 206 207 208 209 210 211 | } } return result } // SearchContains returns all zettel that contains the given string. // The string must be normalized through Unicode NKFD, trimmed and not empty. | | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | } } return result } // SearchContains returns all zettel that contains the given string. // The string must be normalized through Unicode NKFD, trimmed and not empty. func (ms *memStore) SearchContains(s string) *id.Set { ms.mx.RLock() defer ms.mx.RUnlock() result := ms.selectWithPred(s, strings.Contains) if len(s) > 14 { return result } if _, err := id.ParseUint(s); err != nil { return result } for zid, zi := range ms.idx { if strings.Contains(zid.String(), s) { addBackwardZids(result, zid, zi) } } return result } func (ms *memStore) selectWithPred(s string, pred func(string, string) bool) *id.Set { // Must only be called if ms.mx is read-locked! result := id.NewSet() for word, refs := range ms.words { if !pred(word, s) { continue } result.CopySlice(refs) } for u, refs := range ms.urls { if !pred(u, s) { continue } result.CopySlice(refs) } return result } func addBackwardZids(result *id.Set, zid id.Zid, zi *zettelData) { // Must only be called if ms.mx is read-locked! result.Add(zid) result.CopySlice(zi.backward) for _, mref := range zi.otherRefs { result.CopySlice(mref.backward) } } |
︙ | ︙ | |||
268 269 270 271 272 273 274 | } } } } return back } | | | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | } } } } return back } func (ms *memStore) UpdateReferences(_ context.Context, zidx *store.ZettelIndex) *id.Set { ms.mx.Lock() defer ms.mx.Unlock() m := ms.makeMeta(zidx) zi, ziExist := ms.idx[zidx.Zid] if !ziExist || zi == nil { zi = &zettelData{} ziExist = false } // Is this zettel an old dead reference mentioned in other zettel? var toCheck *id.Set if refs, ok := ms.dead[zidx.Zid]; ok { // These must be checked later again toCheck = id.NewSet(refs...) delete(ms.dead, zidx.Zid) } zi.meta = m |
︙ | ︙ | |||
353 354 355 356 357 358 359 | ms.dead[ref] = remRef(ms.dead[ref], zidx.Zid) } for _, ref := range newRefs { ms.dead[ref] = addRef(ms.dead[ref], zidx.Zid) } } | | | | | | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | ms.dead[ref] = remRef(ms.dead[ref], zidx.Zid) } for _, ref := range newRefs { ms.dead[ref] = addRef(ms.dead[ref], zidx.Zid) } } func (ms *memStore) updateForwardBackwardReferences(zidx *store.ZettelIndex, zi *zettelData) *id.Set { // Must only be called if ms.mx is write-locked! brefs := zidx.GetBackRefs() newRefs, remRefs := refsDiff(brefs, zi.forward) zi.forward = brefs var toCheck *id.Set for _, ref := range remRefs { bzi := ms.getOrCreateEntry(ref) bzi.backward = remRef(bzi.backward, zidx.Zid) if bzi.meta == nil { toCheck = toCheck.Add(ref) } } for _, ref := range newRefs { bzi := ms.getOrCreateEntry(ref) bzi.backward = addRef(bzi.backward, zidx.Zid) if bzi.meta == nil { toCheck = toCheck.Add(ref) } } return toCheck } func (ms *memStore) updateMetadataReferences(zidx *store.ZettelIndex, zi *zettelData) *id.Set { // Must only be called if ms.mx is write-locked! inverseRefs := zidx.GetInverseRefs() for key, mr := range zi.otherRefs { if _, ok := inverseRefs[key]; ok { continue } ms.removeInverseMeta(zidx.Zid, key, mr.forward) } if zi.otherRefs == nil { zi.otherRefs = make(map[string]bidiRefs) } var toCheck *id.Set for key, mrefs := range inverseRefs { mr := zi.otherRefs[key] newRefs, remRefs := refsDiff(mrefs, mr.forward) mr.forward = mrefs zi.otherRefs[key] = mr for _, ref := range newRefs { |
︙ | ︙ | |||
447 448 449 450 451 452 453 | return zi } zi := &zettelData{} ms.idx[zid] = zi return zi } | | | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | return zi } zi := &zettelData{} ms.idx[zid] = zi return zi } func (ms *memStore) RenameZettel(_ context.Context, curZid, newZid id.Zid) *id.Set { ms.mx.Lock() defer ms.mx.Unlock() curZi, curFound := ms.idx[curZid] _, newFound := ms.idx[newZid] if !curFound || newFound { return nil |
︙ | ︙ | |||
517 518 519 520 521 522 523 | msStringMap[s] = addRef(msStringMap[s], newZid) } return result } return nil } | | | | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | msStringMap[s] = addRef(msStringMap[s], newZid) } return result } return nil } func (ms *memStore) DeleteZettel(_ context.Context, zid id.Zid) *id.Set { ms.mx.Lock() defer ms.mx.Unlock() return ms.doDeleteZettel(zid) } func (ms *memStore) doDeleteZettel(zid id.Zid) *id.Set { // Must only be called if ms.mx is write-locked! zi, ok := ms.idx[zid] if !ok { return nil } ms.deleteDeadSources(zid, zi) |
︙ | ︙ | |||
555 556 557 558 559 560 561 | } else { delete(ms.dead, ref) } } } } | | | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | } else { delete(ms.dead, ref) } } } } func (ms *memStore) deleteForwardBackward(zid id.Zid, zi *zettelData) *id.Set { // Must only be called if ms.mx is write-locked! for _, ref := range zi.forward { if fzi, ok := ms.idx[ref]; ok { fzi.backward = remRef(fzi.backward, zid) } } var toCheck *id.Set for _, ref := range zi.backward { if bzi, ok := ms.idx[ref]; ok { bzi.forward = remRef(bzi.forward, zid) toCheck = toCheck.Add(ref) } } return toCheck |
︙ | ︙ |
Changes to box/manager/store/store.go.
︙ | ︙ | |||
47 48 49 50 51 52 53 | GetMeta(context.Context, id.Zid) (*meta.Meta, error) // Entrich metadata with data from store. Enrich(ctx context.Context, m *meta.Meta) // UpdateReferences for a specific zettel. // Returns set of zettel identifier that must also be checked for changes. | | | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | GetMeta(context.Context, id.Zid) (*meta.Meta, error) // Entrich metadata with data from store. Enrich(ctx context.Context, m *meta.Meta) // UpdateReferences for a specific zettel. // Returns set of zettel identifier that must also be checked for changes. UpdateReferences(context.Context, *ZettelIndex) *id.Set // RenameZettel changes all references of current zettel identifier to new // zettel identifier. RenameZettel(_ context.Context, curZid, newZid id.Zid) *id.Set // DeleteZettel removes index data for given zettel. // Returns set of zettel identifier that must also be checked for changes. DeleteZettel(context.Context, id.Zid) *id.Set // ReadStats populates st with store statistics. ReadStats(st *Stats) // Dump the content to a Writer. Dump(io.Writer) } |
Changes to box/manager/store/zettel.go.
︙ | ︙ | |||
16 17 18 19 20 21 22 | import ( "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // ZettelIndex contains all index data of a zettel. type ZettelIndex struct { | | | | | | | | 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 | import ( "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // ZettelIndex contains all index data of a zettel. type ZettelIndex struct { Zid id.Zid // zid of the indexed zettel meta *meta.Meta // full metadata backrefs *id.Set // set of back references inverseRefs map[string]*id.Set // references of inverse keys deadrefs *id.Set // set of dead references words WordSet urls WordSet } // NewZettelIndex creates a new zettel index. func NewZettelIndex(m *meta.Meta) *ZettelIndex { return &ZettelIndex{ Zid: m.Zid, meta: m, backrefs: id.NewSet(), inverseRefs: make(map[string]*id.Set), deadrefs: id.NewSet(), } } // AddBackRef adds a reference to a zettel where the current zettel links to // without any more information. func (zi *ZettelIndex) AddBackRef(zid id.Zid) { |
︙ | ︙ |
Changes to query/context.go.
︙ | ︙ | |||
115 116 117 118 119 120 121 | old[n-1].meta = nil // avoid memory leak *q = old[0 : n-1] return item } type contextTask struct { port ContextPort | | | | | 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 | old[n-1].meta = nil // avoid memory leak *q = old[0 : n-1] return item } type contextTask struct { port ContextPort seen *id.Set queue ztlCtxQueue maxCost float64 limit int tagMetas map[string][]*meta.Meta tagZids map[string]*id.Set // just the zids of tagMetas metaZid map[id.Zid]*meta.Meta // maps zid to meta for all meta retrieved with tags } func newQueue(startSeq []*meta.Meta, maxCost float64, limit int, port ContextPort) *contextTask { result := &contextTask{ port: port, seen: id.NewSet(), maxCost: maxCost, limit: limit, tagMetas: make(map[string][]*meta.Meta), tagZids: make(map[string]*id.Set), metaZid: make(map[id.Zid]*meta.Meta), } queue := make(ztlCtxQueue, 0, len(startSeq)) for _, m := range startSeq { queue = append(queue, ztlCtxItem{cost: 1, meta: m}) } |
︙ | ︙ | |||
196 197 198 199 200 201 202 | } } func (ct *contextTask) addMeta(m *meta.Meta, newCost float64) { // If len(zc.seen) <= 1, the initial zettel is processed. In this case allow all // other zettel that are directly reachable, without taking the cost into account. // Of course, the limit ist still relevant. | | | | | | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | } } func (ct *contextTask) addMeta(m *meta.Meta, newCost float64) { // If len(zc.seen) <= 1, the initial zettel is processed. In this case allow all // other zettel that are directly reachable, without taking the cost into account. // Of course, the limit ist still relevant. if !ct.hasLimit() && (ct.seen.Length() <= 1 || ct.maxCost == 0 || newCost <= ct.maxCost) { if ct.seen.Contains(m.Zid) { heap.Push(&ct.queue, ztlCtxItem{cost: newCost, meta: m}) } } } func (ct *contextTask) addIDSet(ctx context.Context, newCost float64, value string) { elems := meta.ListFromValue(value) refCost := referenceCost(newCost, len(elems)) for _, val := range elems { ct.addID(ctx, refCost, val) } } func referenceCost(baseCost float64, numReferences int) float64 { nRefs := float64(numReferences) return nRefs*math.Log2(nRefs+1) + baseCost } func (ct *contextTask) addTags(ctx context.Context, tags []string, baseCost float64) { var zidSet *id.Set for _, tag := range tags { zs := ct.updateTagData(ctx, tag) zidSet = zidSet.Copy(zs) } for _, zid := range zidSet.Sorted() { // .Sorted() to stay deterministic minCost := math.MaxFloat64 costFactor := 1.1 for _, tag := range tags { tagZids := ct.tagZids[tag] if tagZids.Contains(zid) { cost := tagCost(baseCost, tagZids.Length()) if cost < minCost { minCost = cost } costFactor /= 1.1 } } ct.addMeta(ct.metaZid[zid], minCost*costFactor) } } func (ct *contextTask) updateTagData(ctx context.Context, tag string) *id.Set { if _, found := ct.tagMetas[tag]; found { return ct.tagZids[tag] } q := Parse(api.KeyTags + api.SearchOperatorHas + tag + " ORDER REVERSE " + api.KeyID) ml, err := ct.port.SelectMeta(ctx, nil, q) if err != nil { ml = nil |
︙ | ︙ | |||
274 275 276 277 278 279 280 | if ct.hasLimit() { return nil, -1 } for len(ct.queue) > 0 { item := heap.Pop(&ct.queue).(ztlCtxItem) m := item.meta zid := m.Zid | | | | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | if ct.hasLimit() { return nil, -1 } for len(ct.queue) > 0 { item := heap.Pop(&ct.queue).(ztlCtxItem) m := item.meta zid := m.Zid if ct.seen.Contains(zid) { continue } ct.seen.Add(zid) return m, item.cost } return nil, -1 } func (ct *contextTask) hasLimit() bool { limit := ct.limit return limit > 0 && ct.seen.Length() >= limit } |
Changes to query/parser.go.
︙ | ︙ | |||
82 83 84 85 86 87 88 | for { pos := inp.Pos zid, found := ps.scanZid() if !found { inp.SetPos(pos) break } | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | for { pos := inp.Pos zid, found := ps.scanZid() if !found { inp.SetPos(pos) break } if !zidSet.Contains(zid) { zidSet.Add(zid) q = createIfNeeded(q) q.zids = append(q.zids, zid) } ps.skipSpace() if ps.mustStop() { q.zids = nil |
︙ | ︙ |
Changes to query/query.go.
︙ | ︙ | |||
23 24 25 26 27 28 29 | "zettelstore.de/z/zettel/meta" ) // Searcher is used to select zettel identifier based on search criteria. type Searcher interface { // Select all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. | | | | | | 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 | "zettelstore.de/z/zettel/meta" ) // Searcher is used to select zettel identifier based on search criteria. type Searcher interface { // Select all zettel that contains the given exact word. // The word must be normalized through Unicode NKFD, trimmed and not empty. SearchEqual(word string) *id.Set // Select all zettel that have a word with the given prefix. // The prefix must be normalized through Unicode NKFD, trimmed and not empty. SearchPrefix(prefix string) *id.Set // Select all zettel that have a word with the given suffix. // The suffix must be normalized through Unicode NKFD, trimmed and not empty. SearchSuffix(suffix string) *id.Set // Select all zettel that contains the given string. // The string must be normalized through Unicode NKFD, trimmed and not empty. SearchContains(s string) *id.Set } // Query specifies a mechanism for querying zettel. type Query struct { // Präfixed zettel identifier. zids []id.Zid |
︙ | ︙ | |||
407 408 409 410 411 412 413 | cTerm.Match = matchAlways } result.Terms = append(result.Terms, cTerm) } return result } | | | | | | | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 | cTerm.Match = matchAlways } result.Terms = append(result.Terms, cTerm) } return result } func metaList2idSet(ml []*meta.Meta) *id.Set { if ml == nil { return nil } result := id.NewSetCap(len(ml)) for _, m := range ml { result = result.Add(m.Zid) } return result } func (ct *conjTerms) retrieveAndCompileTerm(searcher Searcher, startSet *id.Set) CompiledTerm { match := ct.compileMeta() // Match might add some searches var pred RetrievePredicate if searcher != nil { pred = ct.retrieveIndex(searcher) if startSet != nil { if pred == nil { pred = startSet.ContainsOrNil } else { predSet := id.NewSetCap(startSet.Length()) startSet.ForEach(func(zid id.Zid) { if pred(zid) { predSet = predSet.Add(zid) } }) pred = predSet.ContainsOrNil } } } return CompiledTerm{Match: match, Retrieve: pred} } |
︙ | ︙ | |||
457 458 459 460 461 462 463 | positives := retrievePositives(normCalls, plainCalls) if positives == nil { // No positive search for words, must contain only words for a negative search. // Otherwise len(search) == 0 (see above) negatives := retrieveNegatives(negCalls) return func(zid id.Zid) bool { return !negatives.ContainsOrNil(zid) } } | | | | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | positives := retrievePositives(normCalls, plainCalls) if positives == nil { // No positive search for words, must contain only words for a negative search. // Otherwise len(search) == 0 (see above) negatives := retrieveNegatives(negCalls) return func(zid id.Zid) bool { return !negatives.ContainsOrNil(zid) } } if positives.IsEmpty() { // Positive search didn't found anything. We can omit the negative search. return neverIncluded } if len(negCalls) == 0 { // Positive search found something, but there is no negative search. return positives.Contains } negatives := retrieveNegatives(negCalls) if negatives == nil { return positives.Contains } return func(zid id.Zid) bool { return positives.Contains(zid) && !negatives.ContainsOrNil(zid) } } // Limit returns only s.GetLimit() elements of the given list. func (q *Query) Limit(metaList []*meta.Meta) []*meta.Meta { if q == nil { return metaList } return limitElements(metaList, q.limit) } |
Changes to query/retrieve.go.
︙ | ︙ | |||
23 24 25 26 27 28 29 | "zettelstore.de/z/zettel/id" ) type searchOp struct { s string op compareOp } | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | "zettelstore.de/z/zettel/id" ) type searchOp struct { s string op compareOp } type searchFunc func(string) *id.Set type searchCallMap map[searchOp]searchFunc var cmpPred = map[compareOp]func(string, string) bool{ cmpEqual: stringEqual, cmpPrefix: strings.HasPrefix, cmpSuffix: strings.HasSuffix, cmpMatch: strings.Contains, |
︙ | ︙ | |||
100 101 102 103 104 105 106 | if _, found := plainCalls[val]; found { return true } } return false } | | | | | | | 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 | if _, found := plainCalls[val]; found { return true } } return false } func retrievePositives(normCalls, plainCalls searchCallMap) *id.Set { if isSuperset(normCalls, plainCalls) { var normResult *id.Set for c, sf := range normCalls { normResult = normResult.IntersectOrSet(sf(c.s)) } return normResult } type searchResults map[searchOp]*id.Set var cache searchResults var plainResult *id.Set for c, sf := range plainCalls { result := sf(c.s) if _, found := normCalls[c]; found { if cache == nil { cache = make(searchResults) } cache[c] = result } plainResult = plainResult.IntersectOrSet(result) } var normResult *id.Set for c, sf := range normCalls { if cache != nil { if result, found := cache[c]; found { normResult = normResult.IntersectOrSet(result) continue } } |
︙ | ︙ | |||
144 145 146 147 148 149 150 | if _, found := normCalls[c]; !found { return false } } return true } | | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | if _, found := normCalls[c]; !found { return false } } return true } func retrieveNegatives(negCalls searchCallMap) *id.Set { var negatives *id.Set for val, sf := range negCalls { negatives = negatives.Copy(sf(val.s)) } return negatives } func getSearchFunc(searcher Searcher, op compareOp) searchFunc { |
︙ | ︙ |
Changes to usecase/query.go.
︙ | ︙ | |||
170 171 172 173 174 175 176 | } } } candidates = filterByZid(candidates, refZids) return uc.filterCandidates(ctx, candidates, words) } | | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | } } } candidates = filterByZid(candidates, refZids) return uc.filterCandidates(ctx, candidates, words) } func filterByZid(candidates []*meta.Meta, ignoreSeq *id.Set) []*meta.Meta { result := make([]*meta.Meta, 0, len(candidates)) for _, m := range candidates { if !ignoreSeq.Contains(m.Zid) { result = append(result, m) } } return result } func (uc *Query) filterCandidates(ctx context.Context, candidates []*meta.Meta, words []string) []*meta.Meta { |
︙ | ︙ |
Changes to web/adapter/webui/sxn_code.go.
︙ | ︙ | |||
57 58 59 60 61 62 63 | type getMetaFunc func(context.Context, id.Zid) (*meta.Meta, error) func buildSxnCodeDigraph(ctx context.Context, startZid id.Zid, getMeta getMetaFunc) id.Digraph { m, err := getMeta(ctx, startZid) if err != nil { return nil } | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | type getMetaFunc func(context.Context, id.Zid) (*meta.Meta, error) func buildSxnCodeDigraph(ctx context.Context, startZid id.Zid, getMeta getMetaFunc) id.Digraph { m, err := getMeta(ctx, startZid) if err != nil { return nil } var marked *id.Set stack := []*meta.Meta{m} dg := id.Digraph(nil).AddVertex(startZid) for pos := len(stack) - 1; pos >= 0; pos = len(stack) - 1 { curr := stack[pos] stack = stack[:pos] if marked.Contains(curr.Zid) { continue |
︙ | ︙ |
Changes to zettel/id/digraph.go.
︙ | ︙ | |||
15 16 17 18 19 20 21 | import ( "maps" "slices" ) // Digraph relates zettel identifier in a directional way. | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import ( "maps" "slices" ) // Digraph relates zettel identifier in a directional way. type Digraph map[Zid]*Set // AddVertex adds an edge / vertex to the digraph. func (dg Digraph) AddVertex(zid Zid) Digraph { if dg == nil { return Digraph{zid: nil} } if _, found := dg[zid]; !found { |
︙ | ︙ | |||
42 43 44 45 46 47 48 | } } // AddEdge adds a connection from `zid1` to `zid2`. // Both vertices must be added before. Otherwise the function may panic. func (dg Digraph) AddEdge(fromZid, toZid Zid) Digraph { if dg == nil { | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | } } // AddEdge adds a connection from `zid1` to `zid2`. // Both vertices must be added before. Otherwise the function may panic. func (dg Digraph) AddEdge(fromZid, toZid Zid) Digraph { if dg == nil { return Digraph{fromZid: (*Set)(nil).Add(toZid), toZid: nil} } dg[fromZid] = dg[fromZid].Add(toZid) return dg } // AddEgdes adds all given `Edge`s to the digraph. // |
︙ | ︙ | |||
68 69 70 71 72 73 74 | dg = dg.AddEdge(edge.From, edge.To) } return dg } // Equal returns true if both digraphs have the same vertices and edges. func (dg Digraph) Equal(other Digraph) bool { | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | dg = dg.AddEdge(edge.From, edge.To) } return dg } // Equal returns true if both digraphs have the same vertices and edges. func (dg Digraph) Equal(other Digraph) bool { return maps.EqualFunc(dg, other, func(cg, co *Set) bool { return cg.Equal(co) }) } // Clone a digraph. func (dg Digraph) Clone() Digraph { if len(dg) == 0 { return nil } |
︙ | ︙ | |||
93 94 95 96 97 98 99 | return false } _, found := dg[zid] return found } // Vertices returns the set of all vertices. | | | | | | | | | | | | | | | | | | | 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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | return false } _, found := dg[zid] return found } // Vertices returns the set of all vertices. func (dg Digraph) Vertices() *Set { if len(dg) == 0 { return nil } verts := NewSetCap(len(dg)) for vert := range dg { verts.Add(vert) } return verts } // Edges returns an unsorted slice of the edges of the digraph. func (dg Digraph) Edges() (es EdgeSlice) { for vert, closure := range dg { closure.ForEach(func(next Zid) { es = append(es, Edge{From: vert, To: next}) }) } return es } // Originators will return the set of all vertices that are not referenced // a the to-part of an edge. func (dg Digraph) Originators() *Set { if len(dg) == 0 { return nil } origs := dg.Vertices() for _, closure := range dg { origs.Substract(closure) } return origs } // Terminators returns the set of all vertices that does not reference // other vertices. func (dg Digraph) Terminators() (terms *Set) { for vert, closure := range dg { if closure.IsEmpty() { terms = terms.Add(vert) } } return terms } // TransitiveClosure calculates the sub-graph that is reachable from `zid`. func (dg Digraph) TransitiveClosure(zid Zid) (tc Digraph) { if len(dg) == 0 { return nil } var marked *Set stack := Slice{zid} for pos := len(stack) - 1; pos >= 0; pos = len(stack) - 1 { curr := stack[pos] stack = stack[:pos] if marked.Contains(curr) { continue } tc = tc.AddVertex(curr) dg[curr].ForEach(func(next Zid) { tc = tc.AddVertex(next) tc = tc.AddEdge(curr, next) stack = append(stack, next) }) marked = marked.Add(curr) } return tc } // ReachableVertices calculates the set of all vertices that are reachable // from the given `zid`. func (dg Digraph) ReachableVertices(zid Zid) (tc *Set) { if len(dg) == 0 { return nil } stack := dg[zid].Sorted() for last := len(stack) - 1; last >= 0; last = len(stack) - 1 { curr := stack[last] stack = stack[:last] if tc.Contains(curr) { continue } closure, found := dg[curr] if !found { continue } tc = tc.Add(curr) closure.ForEach(func(next Zid) { stack = append(stack, next) }) } return tc } // IsDAG returns a vertex and false, if the graph has a cycle containing the vertex. func (dg Digraph) IsDAG() (Zid, bool) { for vertex := range dg { if dg.ReachableVertices(vertex).Contains(vertex) { return vertex, false } } return Invalid, true } // Reverse returns a graph with reversed edges. func (dg Digraph) Reverse() (revDg Digraph) { for vertex, closure := range dg { revDg = revDg.AddVertex(vertex) closure.ForEach(func(next Zid) { revDg = revDg.AddVertex(next) revDg = revDg.AddEdge(next, vertex) }) } return revDg } // SortReverse returns a deterministic, topological, reverse sort of the // digraph. // // Works only if digraph is a DAG. Otherwise the algorithm will not terminate // or returns an arbitrary value. func (dg Digraph) SortReverse() (sl Slice) { if len(dg) == 0 { return nil } tempDg := dg.Clone() for len(tempDg) > 0 { terms := tempDg.Terminators() if terms.IsEmpty() { break } termSlice := terms.Sorted() slices.Reverse(termSlice) sl = append(sl, termSlice...) terms.ForEach(func(t Zid) { tempDg.RemoveVertex(t) }) } return sl } |
Changes to zettel/id/digraph_test.go.
︙ | ︙ | |||
26 27 28 29 30 31 32 | } func TestDigraphOriginators(t *testing.T) { t.Parallel() testcases := []struct { name string dg id.EdgeSlice | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | } func TestDigraphOriginators(t *testing.T) { t.Parallel() testcases := []struct { name string dg id.EdgeSlice orig *id.Set term *id.Set }{ {"empty", nil, nil, nil}, {"single", zps{{0, 1}}, id.NewSet(0), id.NewSet(1)}, {"chain", zps{{0, 1}, {1, 2}, {2, 3}}, id.NewSet(0), id.NewSet(3)}, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { |
︙ | ︙ | |||
52 53 54 55 56 57 58 | func TestDigraphReachableVertices(t *testing.T) { t.Parallel() testcases := []struct { name string pairs id.EdgeSlice start id.Zid | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | func TestDigraphReachableVertices(t *testing.T) { t.Parallel() testcases := []struct { name string pairs id.EdgeSlice start id.Zid exp *id.Set }{ {"nil", nil, 0, nil}, {"0-2", zps{{1, 2}, {2, 3}}, 1, id.NewSet(2, 3)}, {"1,2", zps{{1, 2}, {2, 3}}, 2, id.NewSet(3)}, {"0-2,1-2", zps{{1, 2}, {2, 3}, {1, 3}}, 1, id.NewSet(2, 3)}, {"0-2,1-2/1", zps{{1, 2}, {2, 3}, {1, 3}}, 2, id.NewSet(3)}, {"0-2,1-2/2", zps{{1, 2}, {2, 3}, {1, 3}}, 3, nil}, |
︙ | ︙ |
Changes to zettel/id/set.go.
︙ | ︙ | |||
15 16 17 18 19 20 21 | import ( "maps" "strings" ) // Set is a set of zettel identifier | > | | > | | | | | | | > > > > > > > > > > > | > > | | | | | | | | | | | | > | > | | | > > > | | | > > > | | | | | | | | | | | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | 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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | import ( "maps" "strings" ) // Set is a set of zettel identifier type Set struct { data map[Zid]struct{} } // String returns a string representation of the map. func (s *Set) String() string { if s == nil || len(s.data) == 0 { return "{}" } var sb strings.Builder sb.WriteByte('{') for i, zid := range s.Sorted() { if i > 0 { sb.WriteByte(' ') } sb.Write(zid.Bytes()) } sb.WriteByte('}') return sb.String() } // NewSet returns a new set of identifier with the given initial values. func NewSet(zids ...Zid) *Set { l := len(zids) if l < 8 { l = 8 } result := Set{data: make(map[Zid]struct{}, l)} result.CopySlice(zids) return &result } // NewSetCap returns a new set of identifier with the given capacity and initial values. func NewSetCap(c int, zids ...Zid) *Set { l := len(zids) if c < l { c = l } if c < 8 { c = 8 } result := Set{data: make(map[Zid]struct{}, c)} result.CopySlice(zids) return &result } // IsEmpty returns true, if the set conains no element. func (s *Set) IsEmpty() bool { return s == nil || len(s.data) == 0 } // Length returns the number of elements in this set. func (s *Set) Length() int { if s == nil { return 0 } return len(s.data) } // Clone returns a copy of the given set. func (s *Set) Clone() *Set { if s == nil || len(s.data) == 0 { return nil } return &Set{data: maps.Clone(s.data)} } // Add adds a Add to the set. func (s *Set) Add(zid Zid) *Set { if s == nil { return NewSet(zid) } s.data[zid] = struct{}{} return s } // Contains return true if the set is non-nil and the set contains the given Zettel identifier. func (s *Set) Contains(zid Zid) bool { if s != nil { _, found := s.data[zid] return found } return false } // ContainsOrNil return true if the set is nil or if the set contains the given Zettel identifier. func (s *Set) ContainsOrNil(zid Zid) bool { if s != nil { _, found := s.data[zid] return found } return true } // Copy adds all member from the other set. func (s *Set) Copy(other *Set) *Set { if s == nil { if other == nil || len(other.data) == 0 { return nil } s = NewSetCap(len(other.data)) } if other != nil { maps.Copy(s.data, other.data) } return s } // CopySlice adds all identifier of the given slice to the set. func (s *Set) CopySlice(sl Slice) *Set { if s == nil { s = NewSetCap(len(sl)) } for _, zid := range sl { s.data[zid] = struct{}{} } return s } // Sorted returns the set as a sorted slice of zettel identifier. func (s *Set) Sorted() Slice { if s == nil { return nil } if l := len(s.data); l > 0 { result := make(Slice, 0, l) for zid := range s.data { result = append(result, zid) } result.Sort() return result } return nil } // IntersectOrSet removes all zettel identifier that are not in the other set. // Both sets can be modified by this method. One of them is the set returned. // It contains the intersection of both, if s is not nil. // // If s == nil, then the other set is always returned. func (s *Set) IntersectOrSet(other *Set) *Set { if s == nil { return other } if other == nil { return nil } if len(s.data) > len(other.data) { s, other = other, s } for zid := range s.data { _, otherOk := other.data[zid] if !otherOk { delete(s.data, zid) } } return s } // Substract removes all zettel identifier from 's' that are in the set 'other'. func (s *Set) Substract(other *Set) { if s == nil || len(s.data) == 0 || other == nil || len(other.data) == 0 { return } for zid := range other.data { delete(s.data, zid) } } // Remove the identifier from the set. func (s *Set) Remove(zid Zid) *Set { if s == nil || len(s.data) == 0 { return nil } delete(s.data, zid) if len(s.data) == 0 { return nil } return s } // Equal returns true if the other set is equal to the given set. func (s *Set) Equal(other *Set) bool { if s == nil { return other == nil } if other == nil { return false } return maps.Equal(s.data, other.data) } // ForEach calls the given function for each element of the set. func (s *Set) ForEach(fn func(zid Zid)) { if s != nil { for zid := range s.data { fn(zid) } } } // Pop return one arbitrary element of the set. func (s *Set) Pop() (Zid, bool) { if s != nil { for zid := range s.data { delete(s.data, zid) return zid, true } } return Invalid, false } |
Changes to zettel/id/set_test.go.
︙ | ︙ | |||
18 19 20 21 22 23 24 | "zettelstore.de/z/zettel/id" ) func TestSetContains(t *testing.T) { t.Parallel() testcases := []struct { | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | "zettelstore.de/z/zettel/id" ) func TestSetContains(t *testing.T) { t.Parallel() testcases := []struct { s *id.Set zid id.Zid exp bool }{ {nil, id.Invalid, true}, {nil, 14, true}, {id.NewSet(), id.Invalid, false}, {id.NewSet(), 1, false}, |
︙ | ︙ | |||
40 41 42 43 44 45 46 | } } } func TestSetAdd(t *testing.T) { t.Parallel() testcases := []struct { | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | } } } func TestSetAdd(t *testing.T) { t.Parallel() testcases := []struct { s1, s2 *id.Set exp id.Slice }{ {nil, nil, nil}, {id.NewSet(), nil, nil}, {id.NewSet(), id.NewSet(), nil}, {nil, id.NewSet(1), id.Slice{1}}, {id.NewSet(1), nil, id.Slice{1}}, |
︙ | ︙ | |||
65 66 67 68 69 70 71 | } } } func TestSetSorted(t *testing.T) { t.Parallel() testcases := []struct { | | | | 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 | } } } func TestSetSorted(t *testing.T) { t.Parallel() testcases := []struct { set *id.Set exp id.Slice }{ {nil, nil}, {id.NewSet(), nil}, {id.NewSet(9, 4, 6, 1, 7), id.Slice{1, 4, 6, 7, 9}}, } for i, tc := range testcases { got := tc.set.Sorted() if !got.Equal(tc.exp) { t.Errorf("%d: %v.Sorted() should be %v, but got %v", i, tc.set, tc.exp, got) } } } func TestSetIntersectOrSet(t *testing.T) { t.Parallel() testcases := []struct { s1, s2 *id.Set exp id.Slice }{ {nil, nil, nil}, {id.NewSet(), nil, nil}, {nil, id.NewSet(), nil}, {id.NewSet(), id.NewSet(), nil}, {id.NewSet(1), nil, nil}, |
︙ | ︙ | |||
111 112 113 114 115 116 117 | } } } func TestSetRemove(t *testing.T) { t.Parallel() testcases := []struct { | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | } } } func TestSetRemove(t *testing.T) { t.Parallel() testcases := []struct { s1, s2 *id.Set exp id.Slice }{ {nil, nil, nil}, {id.NewSet(), nil, nil}, {id.NewSet(), id.NewSet(), nil}, {id.NewSet(1), nil, id.Slice{1}}, {id.NewSet(1), id.NewSet(), id.Slice{1}}, |
︙ | ︙ | |||
143 144 145 146 147 148 149 | // for range b.N { // s[id.Zid(i)] = true // } // } func BenchmarkSet(b *testing.B) { s := id.Set{} for i := range b.N { | | | 143 144 145 146 147 148 149 150 151 152 | // for range b.N { // s[id.Zid(i)] = true // } // } func BenchmarkSet(b *testing.B) { s := id.Set{} for i := range b.N { s.Add(id.Zid(i)) } } |