cayleygraph / cayley

An open-source graph database
https://cayley.io
Apache License 2.0
14.8k stars 1.25k forks source link

Filter path by empty label field (FilterContext) #977

Open paralin opened 1 year ago

paralin commented 1 year ago

Example program:

package main

import (
    "fmt"

    "github.com/cayleygraph/cayley"
    "github.com/cayleygraph/quad"
)

func main() {
    store, err := cayley.NewMemoryGraph()
    if err != nil {
        panic(err)
    }

    store.AddQuad(quad.MakeIRI("node1", "predicate", "node2", ""))
    store.AddQuad(quad.MakeIRI("node1", "predicate", "node3", "myvalue"))
    store.AddQuad(quad.MakeIRI("node1", "predicate", "node4", "othervalue"))

    // Expected output, <node2>
    p := cayley.
        StartPath(store, quad.IRI("node1")).
        // TODO: limit to just node2 (empty label field)
        // LabelContext(""). - doesn't work, lists none
        // LabelContext(quad.IRI("")). - doesn't work, lists none
        // LabelContext(quad.Raw("")). - doesn't work, lists node3 and node4 (???)
        // LabelContext(nil). - doesn't work, lists node3 and node4 (??)
        Out(quad.IRI("predicate"))
    it := p.Iterate(nil)
    err = it.EachValue(nil, func(value quad.Value) error {
        nativeValue := quad.NativeOf(value) // this converts RDF values to normal Go types
        fmt.Println(nativeValue)
        return nil
    })
    if err != nil {
        fmt.Println(err.Error())
    }
}

None of these successfully limit to just the quad with the empty Label:

        // LabelContext(""). - doesn't work, lists none
        // LabelContext(quad.IRI("")). - doesn't work, lists none
        // LabelContext(quad.Raw("")). - doesn't work, lists node3 and node4 (???)
        // LabelContext(nil). - doesn't work, lists node3 and node4 (??)

How can I do that?

Additionally, I don't understand why empty string "" and nil both return the values that have populated Label fields but not the one that has an empty Label field. Shouldn't it be the other way around?

dennwc commented 12 months ago

Hmm, this one is tricky, since we never defined what will happen with empty values.

Definitely, the returns don't make any sense from user's point of view. I think LabelContext is trying to be nil-safe and ignores the empty argument in the last two cases.

I think we should enforce the following behavior:

The problem is that we need to check the code to make sure it doesn't expect something like this to work:

var label quad.Value
if something {
   label = quad.IRI("x")
}
// expects that if label is nil, the filter will be skipped
p = p.LabelContext(label)