gonum.org/v1/gonum@v0.14.0/graph/formats/rdf/query_test.go (about) 1 // Copyright ©2022 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package rdf 6 7 import ( 8 "io" 9 "reflect" 10 "strings" 11 "testing" 12 13 "golang.org/x/exp/rand" 14 ) 15 16 var andTests = []struct { 17 name string 18 a, b []Term 19 want []Term 20 }{ 21 { 22 name: "identical", 23 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 24 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 25 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 26 }, 27 { 28 name: "identical with excess a", 29 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 30 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 31 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 32 }, 33 { 34 name: "identical with excess b", 35 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 36 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 37 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 38 }, 39 { 40 name: "b less", 41 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 42 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}}, 43 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}}, 44 }, 45 { 46 name: "a less", 47 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:c>", UID: 3}}, 48 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 49 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:c>", UID: 3}}, 50 }, 51 } 52 53 func TestQueryAnd(t *testing.T) { 54 src := rand.NewSource(1) 55 for _, test := range andTests { 56 for i := 0; i < 10; i++ { 57 a := Query{terms: permutedTerms(test.a, src)} 58 b := Query{terms: permutedTerms(test.b, src)} 59 60 got := a.And(b).Result() 61 sortByID(got) 62 sortByID(test.want) 63 64 if !reflect.DeepEqual(got, test.want) { 65 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 66 test.name, got, test.want) 67 } 68 } 69 } 70 } 71 72 var orTests = []struct { 73 name string 74 a, b []Term 75 want []Term 76 }{ 77 { 78 name: "identical", 79 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 80 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 81 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 82 }, 83 { 84 name: "identical with excess a", 85 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 86 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 87 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 88 }, 89 { 90 name: "identical with excess b", 91 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 92 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 93 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 94 }, 95 { 96 name: "b less", 97 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 98 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}}, 99 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 100 }, 101 { 102 name: "a less", 103 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:c>", UID: 3}}, 104 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 105 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 106 }, 107 } 108 109 func TestQueryOr(t *testing.T) { 110 src := rand.NewSource(1) 111 for _, test := range orTests { 112 for i := 0; i < 10; i++ { 113 a := Query{terms: permutedTerms(test.a, src)} 114 b := Query{terms: permutedTerms(test.b, src)} 115 116 got := a.Or(b).Result() 117 sortByID(got) 118 sortByID(test.want) 119 120 if !reflect.DeepEqual(got, test.want) { 121 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 122 test.name, got, test.want) 123 } 124 } 125 } 126 } 127 128 var notTests = []struct { 129 name string 130 a, b []Term 131 want []Term 132 }{ 133 { 134 name: "identical", 135 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 136 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 137 want: nil, 138 }, 139 { 140 name: "identical with excess a", 141 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 142 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 143 want: nil, 144 }, 145 { 146 name: "identical with excess b", 147 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 148 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 149 want: nil, 150 }, 151 { 152 name: "b less", 153 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 154 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}}, 155 want: []Term{{Value: "<ex:c>", UID: 3}}, 156 }, 157 { 158 name: "a less", 159 a: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:c>", UID: 3}}, 160 b: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 161 want: nil, 162 }, 163 } 164 165 func TestQueryNot(t *testing.T) { 166 src := rand.NewSource(1) 167 for _, test := range notTests { 168 for i := 0; i < 10; i++ { 169 a := Query{terms: permutedTerms(test.a, src)} 170 b := Query{terms: permutedTerms(test.b, src)} 171 172 got := a.Not(b).Result() 173 sortByID(got) 174 sortByID(test.want) 175 176 if !reflect.DeepEqual(got, test.want) { 177 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 178 test.name, got, test.want) 179 } 180 } 181 } 182 } 183 184 func TestQueryRepeat(t *testing.T) { 185 const filterTestGraph = ` 186 <ex:a> <p:1> <ex:b> . 187 <ex:b> <p:1> <ex:c> . 188 <ex:c> <p:1> <ex:d> . 189 <ex:d> <p:1> <ex:e> . 190 <ex:e> <p:1> <ex:f> . 191 <ex:a> <p:2> <ex:_b> . 192 <ex:b> <p:2> <ex:_c> . 193 <ex:c> <p:2> <ex:_d> . 194 <ex:d> <p:2> <ex:_e> . 195 <ex:e> <p:2> <ex:_f> . 196 ` 197 198 want := []string{"<ex:a>", "<ex:b>", "<ex:c>", "<ex:d>", "<ex:e>", "<ex:f>"} 199 200 g, err := graphFromStatements(filterTestGraph) 201 if err != nil { 202 t.Fatalf("unexpected error constructing graph: %v", err) 203 } 204 start, ok := g.TermFor("<ex:a>") 205 if !ok { 206 t.Fatal("could not get start term") 207 } 208 for _, limit := range []int{0, 1, 2, 5, 100} { 209 got := []string{} 210 var i int 211 result := g.Query(start).Repeat(func(q Query) (Query, bool) { 212 if i >= limit { 213 return q, false 214 } 215 i++ 216 q = q.Out(func(s *Statement) bool { 217 ok := s.Predicate.Value == "<p:1>" 218 if ok { 219 got = append(got, s.Object.Value) 220 } 221 return ok 222 }) 223 return q, true 224 }).Unique().Result() 225 226 n := limit 227 if n >= len(want) { 228 n = len(want) - 1 229 } 230 if !reflect.DeepEqual(got, want[1:n+1]) { 231 t.Errorf("unexpected capture for limit=%d: got:%v want:%v", 232 limit, got, want[:n]) 233 } 234 235 switch { 236 case limit < len(want): 237 if len(result) == 0 || result[0].Value != want[i] { 238 t.Errorf("unexpected result for limit=%d: got:%v want:%v", 239 limit, result[0], want[i]) 240 } 241 default: 242 if len(result) != 0 { 243 t.Errorf("unexpected result for limit=%d: got: %v want:none", 244 limit, result[0]) 245 } 246 } 247 } 248 } 249 250 var uniqueTests = []struct { 251 name string 252 in []Term 253 want []Term 254 }{ 255 { 256 name: "excess a", 257 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 258 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 259 }, 260 { 261 name: "excess b", 262 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 263 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 264 }, 265 { 266 name: "excess c", 267 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}, {Value: "<ex:c>", UID: 3}}, 268 want: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 269 }, 270 } 271 272 func TestQueryUnique(t *testing.T) { 273 src := rand.NewSource(1) 274 for _, test := range uniqueTests { 275 for i := 0; i < 10; i++ { 276 a := Query{terms: permutedTerms(test.in, src)} 277 278 got := a.Unique().Result() 279 sortByID(got) 280 sortByID(test.want) 281 282 if !reflect.DeepEqual(got, test.want) { 283 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 284 test.name, got, test.want) 285 } 286 } 287 } 288 } 289 290 // filterTestGraph is used to test Has*Out and Has*In. It has a symmetry 291 // that means that the in an out tests have the same form, just with opposite 292 // directions. 293 const filterTestGraph = ` 294 <ex:a> <p:1> <ex:d> . 295 <ex:a> <p:2> <ex:f> . 296 <ex:b> <p:2> <ex:d> . 297 <ex:c> <p:2> <ex:d> . 298 <ex:a> <o:n> <ex:d> . 299 # symmetry line. 300 <ex:e> <p:1> <ex:a> . 301 <ex:g> <p:2> <ex:a> . 302 <ex:e> <p:2> <ex:b> . 303 <ex:e> <p:2> <ex:c> . 304 <ex:e> <o:n> <ex:a> . 305 ` 306 307 var hasOutTests = []struct { 308 name string 309 in []Term 310 fn func(*Statement) bool 311 cons func(q Query) Query 312 wantAll []Term 313 wantAny []Term 314 }{ 315 { 316 name: "all", 317 in: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 318 fn: func(s *Statement) bool { return true }, 319 cons: func(q Query) Query { 320 cond := func(s *Statement) bool { return true } 321 return q.Out(cond).In(cond).Unique() 322 }, 323 wantAll: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 324 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 325 }, 326 { 327 name: "none", 328 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 329 fn: func(s *Statement) bool { return false }, 330 cons: func(q Query) Query { 331 cond := func(s *Statement) bool { return false } 332 return q.Out(cond).In(cond).Unique() 333 }, 334 wantAll: nil, 335 wantAny: nil, 336 }, 337 { 338 name: ". <p:1> .", 339 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 340 fn: func(s *Statement) bool { return s.Predicate.Value == "<p:1>" }, 341 cons: func(q Query) Query { 342 cond1 := func(s *Statement) bool { return s.Predicate.Value == "<p:1>" } 343 cond2 := func(s *Statement) bool { return s.Predicate.Value != "<p:1>" } 344 return q.Out(cond1).In(cond1).Not(q.Out(cond2).In(cond2)).Unique() 345 }, 346 wantAll: nil, 347 wantAny: []Term{{Value: "<ex:a>"}}, 348 }, 349 { 350 name: "!(. <p:1> .)", 351 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 352 fn: func(s *Statement) bool { return s.Predicate.Value != "<p:1>" }, 353 cons: func(q Query) Query { 354 cond1 := func(s *Statement) bool { return s.Predicate.Value != "<p:1>" } 355 cond2 := func(s *Statement) bool { return s.Predicate.Value == "<p:1>" } 356 return q.Out(cond1).In(cond1).Not(q.Out(cond2).In(cond2)).Unique() 357 }, 358 wantAll: []Term{{Value: "<ex:b>"}, {Value: "<ex:c>"}}, 359 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 360 }, 361 { 362 name: "!(. <p:2> .)", 363 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 364 fn: func(s *Statement) bool { return s.Predicate.Value != "<p:2>" }, 365 cons: func(q Query) Query { 366 cond1 := func(s *Statement) bool { return s.Predicate.Value != "<p:2>" } 367 cond2 := func(s *Statement) bool { return s.Predicate.Value == "<p:2>" } 368 return q.Out(cond1).In(cond1).Not(q.Out(cond2).In(cond2)).Unique() 369 }, 370 wantAll: nil, 371 wantAny: []Term{{Value: "<ex:a>"}}, 372 }, 373 { 374 name: "!(. <p:2> <ex:f>)", 375 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 376 fn: func(s *Statement) bool { 377 return s.Predicate.Value != "<p:2>" || (s.Predicate.Value == "<p:2>" && s.Object.Value != "<ex:f>") 378 }, 379 cons: func(q Query) Query { 380 cond := func(s *Statement) bool { 381 return s.Predicate.Value == "<p:2>" && s.Object.Value != "<ex:f>" 382 } 383 return q.Out(cond).In(cond).Unique() 384 }, 385 wantAll: []Term{{Value: "<ex:b>"}, {Value: "<ex:c>"}}, 386 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 387 }, 388 { 389 name: "!(. <p:2> !<ex:f>)", 390 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 391 fn: func(s *Statement) bool { 392 return s.Predicate.Value != "<p:2>" || (s.Predicate.Value == "<p:2>" && s.Object.Value == "<ex:f>") 393 }, 394 cons: func(q Query) Query { 395 cond := func(s *Statement) bool { 396 return s.Predicate.Value == "<p:2>" && s.Object.Value == "<ex:f>" 397 } 398 return q.Out(cond).In(cond).Unique() 399 }, 400 wantAll: []Term{{Value: "<ex:a>"}}, 401 wantAny: []Term{{Value: "<ex:a>"}}, 402 }, 403 } 404 405 func TestQueryHasAllOut(t *testing.T) { 406 g, err := graphFromStatements(filterTestGraph) 407 if err != nil { 408 t.Fatalf("unexpected error constructing graph: %v", err) 409 } 410 for _, test := range hasOutTests { 411 ids := make(map[string]int64) 412 for i, v := range test.in { 413 term, ok := g.TermFor(v.Value) 414 if !ok { 415 t.Fatalf("unexpected error constructing graph: could not get UID for term: %v", v.Value) 416 } 417 test.in[i].UID = term.UID 418 ids[term.Value] = term.UID 419 } 420 for i, v := range test.wantAll { 421 test.wantAll[i].UID = ids[v.Value] 422 } 423 424 a := Query{g: g, terms: test.in} 425 426 got := a.HasAllOut(test.fn).Result() 427 sortByID(got) 428 sortByID(test.wantAll) 429 430 if !reflect.DeepEqual(got, test.wantAll) { 431 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 432 test.name, got, test.wantAll) 433 } 434 435 cons := test.cons(a).Result() 436 sortByID(cons) 437 if !reflect.DeepEqual(got, cons) { 438 t.Errorf("unexpected construction result for test %q:\ngot: %v\nwant:%v", 439 test.name, got, cons) 440 } 441 } 442 } 443 444 func TestQueryHasAnyOut(t *testing.T) { 445 g, err := graphFromStatements(filterTestGraph) 446 if err != nil { 447 t.Fatalf("unexpected error constructing graph: %v", err) 448 } 449 for _, test := range hasOutTests { 450 ids := make(map[string]int64) 451 for i, v := range test.in { 452 term, ok := g.TermFor(v.Value) 453 if !ok { 454 t.Fatalf("unexpected error constructing graph: could not get UID for term: %v", v.Value) 455 } 456 test.in[i].UID = term.UID 457 ids[term.Value] = term.UID 458 } 459 for i, v := range test.wantAny { 460 test.wantAny[i].UID = ids[v.Value] 461 } 462 463 a := Query{g: g, terms: test.in} 464 465 got := a.HasAnyOut(test.fn).Result() 466 sortByID(got) 467 sortByID(test.wantAny) 468 469 if !reflect.DeepEqual(got, test.wantAny) { 470 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 471 test.name, got, test.wantAny) 472 } 473 } 474 } 475 476 var hasInTests = []struct { 477 name string 478 in []Term 479 fn func(*Statement) bool 480 cons func(q Query) Query 481 wantAll []Term 482 wantAny []Term 483 }{ 484 { 485 name: "all", 486 in: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 487 fn: func(s *Statement) bool { return true }, 488 cons: func(q Query) Query { 489 cond := func(s *Statement) bool { return true } 490 return q.In(cond).Out(cond).Unique() 491 }, 492 wantAll: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 493 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 494 }, 495 { 496 name: "none", 497 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 498 fn: func(s *Statement) bool { return false }, 499 cons: func(q Query) Query { 500 cond := func(s *Statement) bool { return false } 501 return q.In(cond).Out(cond).Unique() 502 }, 503 wantAll: nil, 504 wantAny: nil, 505 }, 506 { 507 name: ". <p:1> .", 508 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 509 fn: func(s *Statement) bool { return s.Predicate.Value == "<p:1>" }, 510 cons: func(q Query) Query { 511 cond1 := func(s *Statement) bool { return s.Predicate.Value == "<p:1>" } 512 cond2 := func(s *Statement) bool { return s.Predicate.Value != "<p:1>" } 513 return q.In(cond1).Out(cond1).Not(q.In(cond2).Out(cond2)).Unique() 514 }, 515 wantAll: nil, 516 wantAny: []Term{{Value: "<ex:a>"}}, 517 }, 518 { 519 name: "!(. <p:1> .)", 520 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 521 fn: func(s *Statement) bool { return s.Predicate.Value != "<p:1>" }, 522 cons: func(q Query) Query { 523 cond1 := func(s *Statement) bool { return s.Predicate.Value != "<p:1>" } 524 cond2 := func(s *Statement) bool { return s.Predicate.Value == "<p:1>" } 525 return q.In(cond1).Out(cond1).Not(q.In(cond2).Out(cond2)).Unique() 526 }, 527 wantAll: []Term{{Value: "<ex:b>"}, {Value: "<ex:c>"}}, 528 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 529 }, 530 { 531 name: "!(. <p:2> .)", 532 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 533 fn: func(s *Statement) bool { return s.Predicate.Value != "<p:2>" }, 534 cons: func(q Query) Query { 535 cond1 := func(s *Statement) bool { return s.Predicate.Value != "<p:2>" } 536 cond2 := func(s *Statement) bool { return s.Predicate.Value == "<p:2>" } 537 return q.In(cond1).Out(cond1).Not(q.In(cond2).Out(cond2)).Unique() 538 }, 539 wantAll: nil, 540 wantAny: []Term{{Value: "<ex:a>"}}, 541 }, 542 { 543 name: "!(<ex:f> <p:2> .)", 544 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 545 fn: func(s *Statement) bool { 546 return s.Predicate.Value != "<p:2>" || (s.Predicate.Value == "<p:2>" && s.Subject.Value != "<ex:g>") 547 }, 548 cons: func(q Query) Query { 549 cond := func(s *Statement) bool { 550 return s.Predicate.Value == "<p:2>" && s.Subject.Value != "<ex:g>" 551 } 552 return q.In(cond).Out(cond).Unique() 553 }, 554 wantAll: []Term{{Value: "<ex:b>"}, {Value: "<ex:c>"}}, 555 wantAny: []Term{{Value: "<ex:a>"}, {Value: "<ex:b>"}, {Value: "<ex:c>"}}, 556 }, 557 { 558 name: "!(!<ex:f> <p:2> .)", 559 in: []Term{{Value: "<ex:a>", UID: 1}, {Value: "<ex:b>", UID: 2}, {Value: "<ex:c>", UID: 3}}, 560 fn: func(s *Statement) bool { 561 return s.Predicate.Value != "<p:2>" || (s.Predicate.Value == "<p:2>" && s.Subject.Value == "<ex:g>") 562 }, 563 cons: func(q Query) Query { 564 cond := func(s *Statement) bool { 565 return s.Predicate.Value == "<p:2>" && s.Subject.Value == "<ex:g>" 566 } 567 return q.In(cond).Out(cond).Unique() 568 }, 569 wantAll: []Term{{Value: "<ex:a>"}}, 570 wantAny: []Term{{Value: "<ex:a>"}}, 571 }, 572 } 573 574 func TestQueryHasAllIn(t *testing.T) { 575 g, err := graphFromStatements(filterTestGraph) 576 if err != nil { 577 t.Fatalf("unexpected error constructing graph: %v", err) 578 } 579 for _, test := range hasInTests { 580 ids := make(map[string]int64) 581 for i, v := range test.in { 582 term, ok := g.TermFor(v.Value) 583 if !ok { 584 t.Fatalf("unexpected error constructing graph: could not get UID for term: %v", v.Value) 585 } 586 test.in[i].UID = term.UID 587 ids[term.Value] = term.UID 588 } 589 for i, v := range test.wantAll { 590 test.wantAll[i].UID = ids[v.Value] 591 } 592 593 a := Query{g: g, terms: test.in} 594 595 got := a.HasAllIn(test.fn).Result() 596 sortByID(got) 597 sortByID(test.wantAll) 598 599 if !reflect.DeepEqual(got, test.wantAll) { 600 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 601 test.name, got, test.wantAll) 602 } 603 604 cons := test.cons(a).Result() 605 sortByID(cons) 606 if !reflect.DeepEqual(got, cons) { 607 t.Errorf("unexpected construction result for test %q:\ngot: %v\nwant:%v", 608 test.name, got, cons) 609 } 610 } 611 } 612 613 func TestQueryHasAnyIn(t *testing.T) { 614 g, err := graphFromStatements(filterTestGraph) 615 if err != nil { 616 t.Fatalf("unexpected error constructing graph: %v", err) 617 } 618 for _, test := range hasInTests { 619 ids := make(map[string]int64) 620 for i, v := range test.in { 621 term, ok := g.TermFor(v.Value) 622 if !ok { 623 t.Fatalf("unexpected error constructing graph: could not get UID for term: %v", v.Value) 624 } 625 test.in[i].UID = term.UID 626 ids[term.Value] = term.UID 627 } 628 for i, v := range test.wantAny { 629 test.wantAny[i].UID = ids[v.Value] 630 } 631 632 a := Query{g: g, terms: test.in} 633 634 got := a.HasAnyIn(test.fn).Result() 635 sortByID(got) 636 sortByID(test.wantAny) 637 638 if !reflect.DeepEqual(got, test.wantAny) { 639 t.Errorf("unexpected result for test %q:\ngot: %v\nwant:%v", 640 test.name, got, test.wantAny) 641 } 642 } 643 } 644 645 func graphFromStatements(s string) (*Graph, error) { 646 g := NewGraph() 647 dec := NewDecoder(strings.NewReader(s)) 648 for { 649 s, err := dec.Unmarshal() 650 if err != nil { 651 if err != io.EOF { 652 return nil, err 653 } 654 break 655 } 656 g.AddStatement(s) 657 } 658 return g, nil 659 } 660 661 func permutedTerms(t []Term, src rand.Source) []Term { 662 rnd := rand.New(src) 663 p := make([]Term, len(t)) 664 for i, j := range rnd.Perm(len(t)) { 665 p[i] = t[j] 666 } 667 return p 668 }