1<?php
2require_once('strataquerytest.inc.php');
3
4/**
5 * Tests queries.
6 *
7 * @group plugin_strata
8 * @group plugins
9 */
10class query_test extends Strata_Query_UnitTestCase {
11
12    function setup() {
13        parent::setup();
14
15        $this->_isPerson =  array (
16            'type' => 'triple',
17            'subject' => array (
18                'type' => 'variable',
19                'text' => 'p'
20            ),
21            'predicate' => array (
22                'type' => 'literal',
23                'text' => 'class'
24            ),
25            'object' => array (
26                'type' => 'literal',
27                'text' => 'person'
28            )
29        );
30        $this->_personBob = array (
31            'type' => 'triple',
32            'subject' => array (
33                'type' => 'variable',
34                'text' => 'p'
35            ),
36            'predicate' => array (
37                'type' => 'literal',
38                'text' => 'name'
39            ),
40            'object' => array (
41                'type' => 'literal',
42                'text' => 'Bob'
43            )
44        );
45        $this->_personAlice = array (
46            'type' => 'triple',
47            'subject' => array (
48                'type' => 'variable',
49                'text' => 'p'
50            ),
51            'predicate' => array (
52                'type' => 'literal',
53                'text' => 'name'
54            ),
55            'object' => array (
56                'type' => 'literal',
57                'text' => 'Alice'
58            )
59        );
60        $this->_personCarol = array (
61            'type' => 'triple',
62            'subject' => array (
63                'type' => 'variable',
64                'text' => 'p'
65            ),
66            'predicate' => array (
67                'type' => 'literal',
68                'text' => 'name'
69            ),
70            'object' => array (
71                'type' => 'literal',
72                'text' => 'Carol'
73            )
74        );
75        $this->_bobUnionAlice =  array (
76            'type' => 'union',
77            'lhs' => $this->_personBob,
78            'rhs' => $this->_personAlice
79        );
80        $this->_personRating = array (
81            'type' => 'triple',
82            'subject' => array (
83                'type' => 'variable',
84                'text' => 'p'
85            ),
86            'predicate' => array (
87                'type' => 'literal',
88                'text' => 'is rated'
89            ),
90            'object' => array (
91                'type' => 'variable',
92                'text' => 'rating'
93            )
94        );
95        $this->_personKnows = array (
96            'type' => 'triple',
97            'subject' => array (
98                'type' => 'variable',
99                'text' => 'p'
100            ),
101            'predicate' => array (
102                'type' => 'literal',
103                'text' => 'knows'
104            ),
105            'object' => array (
106                'type' => 'variable',
107                'text' => 'knows'
108            )
109        );
110        $this->_personLikes = array (
111            'type' => 'triple',
112            'subject' => array (
113                'type' => 'variable',
114                'text' => 'p'
115            ),
116            'predicate' => array (
117                'type' => 'literal',
118                'text' => 'likes'
119            ),
120            'object' => array (
121                'type' => 'variable',
122                'text' => 'likes'
123            )
124        );
125        $this->_ratingMinusCarol =  array (
126            'type' => 'minus',
127            'lhs' => $this->_personRating,
128            'rhs' => $this->_personCarol
129        );
130        $this->_knowsOptionalLikes =  array (
131            'type' => 'optional',
132            'lhs' => $this->_personKnows,
133            'rhs' => $this->_personLikes
134        );
135    }
136
137    function testAllPersonsOnce() {
138        $query = array (
139            'type' => 'select',
140            'grouping'=>array(),
141            'group' => array (
142                'type' => 'and',
143                'lhs' => array (
144                    'type' => 'and',
145                    'lhs' => array (
146                        'type' => 'and',
147                        'lhs' => array (
148                            'type' => 'and',
149                            'lhs' => $this->_isPerson,
150                            'rhs' => array (
151                                'type' => 'triple',
152                                'subject' => array (
153                                    'type' => 'variable',
154                                    'text' => 'p'
155                                ),
156                                'predicate' => array (
157                                    'type' => 'literal',
158                                    'text' => 'looks like'
159                                ),
160                                'object' => array (
161                                    'type' => 'variable',
162                                    'text' => 'img'
163                                )
164                            )
165                        ),
166                        'rhs' => $this->_personRating
167                    ),
168                    'rhs' => array (
169                        'type' => 'triple',
170                        'subject' => array (
171                            'type' => 'variable',
172                            'text' => 'p'
173                        ),
174                        'predicate' => array (
175                            'type' => 'literal',
176                            'text' => 'has length'
177                        ),
178                        'object' => array (
179                            'type' => 'variable',
180                            'text' => 'length'
181                        )
182                    )
183                ),
184                'rhs' => array (
185                    'type' => 'triple',
186                    'subject' => array (
187                        'type' => 'variable',
188                        'text' => 'p'
189                    ),
190                    'predicate' => array (
191                        'type' => 'literal',
192                        'text' => 'tax rate'
193                    ),
194                    'object' => array (
195                        'type' => 'variable',
196                        'text' => 'tax'
197                    )
198                )
199            ),
200            'projection' => array (
201                'p',
202                'img',
203                'rating',
204                'length',
205                'tax'
206            ),
207            'ordering' => array (
208                array (
209                    'variable' => 'p',
210                    'direction' => 'asc'
211                )
212            )
213        );
214
215        $expected = array (
216            array (
217                'p' => array('person:alice'),
218                'img' => array('50:alice.svg'),
219                'rating' => array('10'),
220                'length' => array('5 ft 5 in'),
221                'tax' => array('10%')
222            ),
223            array (
224                'p' => array('person:bob'),
225                'img' => array('50:bob.png'),
226                'rating' => array('8'),
227                'length' => array('5 ft 10 in'),
228                'tax' => array('25%')
229            ),
230            array (
231                'p' => array('person:carol'),
232                'img' => array('50:carol.jpg'),
233                'rating' => array('1'),
234                'length' => array('4 ft 11 in'),
235                'tax' => array('2%')
236            )
237        );
238
239        $this->assertQueryResult($query, $expected);
240    }
241
242    function testAllPersons() {
243        $query = array (
244            'type' => 'select',
245            'grouping'=>array(),
246            'group' => array (
247                'type' => 'and',
248                'lhs' => array (
249                    'type' => 'and',
250                    'lhs' => array (
251                        'type' => 'and',
252                        'lhs' => array (
253                            'type' => 'and',
254                            'lhs' => array (
255                                'type' => 'and',
256                                'lhs' => $this->_isPerson,
257                                'rhs' => $this->_personKnows
258                            ),
259                            'rhs' => array (
260                                'type' => 'triple',
261                                'subject' => array (
262                                    'type' => 'variable',
263                                    'text' => 'p'
264                                ),
265                                'predicate' => array (
266                                    'type' => 'literal',
267                                    'text' => 'looks like'
268                                ),
269                                'object' => array (
270                                    'type' => 'variable',
271                                    'text' => 'img'
272                                )
273                            )
274                        ),
275                        'rhs' => $this->_personRating
276                    ),
277                    'rhs' => array (
278                        'type' => 'triple',
279                        'subject' => array (
280                            'type' => 'variable',
281                            'text' => 'p'
282                        ),
283                        'predicate' => array (
284                            'type' => 'literal',
285                            'text' => 'has length'
286                        ),
287                        'object' => array (
288                            'type' => 'variable',
289                            'text' => 'length'
290                        )
291                    )
292                ),
293                'rhs' => array (
294                    'type' => 'triple',
295                    'subject' => array (
296                        'type' => 'variable',
297                        'text' => 'p'
298                    ),
299                    'predicate' => array (
300                        'type' => 'literal',
301                        'text' => 'tax rate'
302                    ),
303                    'object' => array (
304                        'type' => 'variable',
305                        'text' => 'tax'
306                    )
307                )
308            ),
309            'projection' => array (
310                'p',
311                'knows',
312                'img',
313                'rating',
314                'length',
315                'tax'
316            ),
317            'ordering' => array (
318                array (
319                    'variable' => 'p',
320                    'direction' => 'desc'
321                ),
322                array (
323                    'variable' => 'knows',
324                    'direction' => 'asc'
325                )
326            )
327        );
328
329        $expected = array (
330            array (
331                'p' => array('person:carol'),
332                'knows' => array('person:alice'),
333                'img' => array('50:carol.jpg'),
334                'rating' => array('1'),
335                'length' => array('4 ft 11 in'),
336                'tax' => array('2%')
337            ),
338            array (
339                'p' => array('person:carol'),
340                'knows' => array('person:bob'),
341                'img' => array('50:carol.jpg'),
342                'rating' => array('1'),
343                'length' => array('4 ft 11 in'),
344                'tax' => array('2%')
345            ),
346            array (
347                'p' => array('person:bob'),
348                'knows' => array('person:alice'),
349                'img' => array('50:bob.png'),
350                'rating' => array('8'),
351                'length' => array('5 ft 10 in'),
352                'tax' => array('25%')
353            ),
354            array (
355                'p' => array('person:alice'),
356                'knows' => array('person:carol'),
357                'img' => array('50:alice.svg'),
358                'rating' => array('10'),
359                'length' => array('5 ft 5 in'),
360                'tax' => array('10%')
361            )
362        );
363
364        $this->assertQueryResult($query, $expected);
365    }
366
367    function testAllPersonsExceptBob() {
368        // All persons except Bob
369        $query = array (
370            'type' => 'select',
371            'grouping'=>array(),
372            'group' => array (
373                'type' => 'minus',
374                'lhs' => $this->_isPerson,
375                'rhs' => $this->_personBob
376            ),
377            'projection' => array (
378                'p'
379            ),
380            'ordering' => array (
381                array (
382                    'variable' => 'p',
383                    'direction' => 'asc'
384                )
385            )
386        );
387
388        $expected = array (
389            array (
390                'p' => array('person:alice')
391            ),
392            array (
393                'p' => array('person:carol')
394            )
395        );
396
397        $this->assertQueryResult($query, $expected);
398    }
399
400    function testAllPersonsThatKnowAlice() {
401        // All persons that know Alice
402        $query = array (
403            'type' => 'select',
404            'grouping'=>array(),
405            'group' => array (
406                'type' => 'and',
407                'lhs' => array (
408                    'type' => 'and',
409                    'lhs' => $this->_isPerson,
410                    'rhs' => array (
411                        'type' => 'triple',
412                        'subject' => array (
413                            'type' => 'variable',
414                            'text' => 'p'
415                        ),
416                        'predicate' => array (
417                            'type' => 'literal',
418                            'text' => 'knows'
419                        ),
420                        'object' => array (
421                            'type' => 'variable',
422                            'text' => 'relation'
423                        )
424                    )
425                ),
426                'rhs' => array (
427                    'type' => 'triple',
428                    'subject' => array (
429                        'type' => 'variable',
430                        'text' => 'relation'
431                    ),
432                    'predicate' => array (
433                        'type' => 'literal',
434                        'text' => 'name'
435                    ),
436                    'object' => array (
437                        'type' => 'literal',
438                        'text' => 'Alice'
439                    )
440                )
441            ),
442            'projection' => array (
443                'p'
444            ),
445            'ordering' => array (
446                array (
447                    'variable' => 'p',
448                    'direction' => 'asc'
449                )
450            )
451        );
452
453        $expected = array (
454            array (
455                'p' => array('person:bob')
456            ),
457            array (
458                'p' => array('person:carol')
459            )
460        );
461
462        $this->assertQueryResult($query, $expected);
463    }
464
465    function testAllPersonsRelatedToAlice() {
466        // All persons having some relation with Alice
467        $query = array (
468            'type' => 'select',
469            'grouping'=>array(),
470            'group' => array (
471                'type' => 'and',
472                'lhs' => array (
473                    'type' => 'and',
474                    'lhs' => $this->_isPerson,
475                    'rhs' => array (
476                        'type' => 'triple',
477                        'subject' => array (
478                            'type' => 'variable',
479                            'text' => 'p'
480                        ),
481                        'predicate' => array (
482                            'type' => 'variable',
483                            'text' => 'relationWith'
484                        ),
485                        'object' => array (
486                            'type' => 'variable',
487                            'text' => 'relation'
488                        )
489                    )
490                ),
491                'rhs' => array (
492                    'type' => 'triple',
493                    'subject' => array (
494                        'type' => 'variable',
495                        'text' => 'relation'
496                    ),
497                    'predicate' => array (
498                        'type' => 'literal',
499                        'text' => 'name'
500                    ),
501                    'object' => array (
502                        'type' => 'literal',
503                        'text' => 'Alice'
504                    )
505                )
506            ),
507            'projection' => array (
508                'p',
509                'relationWith'
510            ),
511            'ordering' => array (
512                array (
513                    'variable' => 'p',
514                    'direction' => 'asc'
515                )
516            )
517        );
518
519        $expected = array (
520            array (
521                'p' => array('person:bob'),
522                'relationWith' => array('knows')
523            ),
524            array (
525                'p' => array('person:bob'),
526                'relationWith' => array('likes')
527            ),
528            array (
529                'p' => array('person:carol'),
530                'relationWith' => array('knows')
531            )
532        );
533
534        $this->assertQueryResult($query, $expected);
535    }
536
537    function testAllPersonsAndRelationWithAlice() {
538        // All persons, including their relation with Alice
539        $query = array (
540            'type' => 'select',
541            'grouping'=>array(),
542            'group' => array (
543                'type' => 'optional',
544                'lhs' => $this->_isPerson,
545                'rhs' => array (
546                    'type' => 'and',
547                    'lhs' => array (
548                        'type' => 'triple',
549                        'subject' => array (
550                            'type' => 'variable',
551                            'text' => 'p'
552                        ),
553                        'predicate' => array (
554                            'type' => 'variable',
555                            'text' => 'relationWith'
556                        ),
557                        'object' => array (
558                            'type' => 'variable',
559                            'text' => 'relation'
560                        )
561                    ),
562                    'rhs' => array (
563                        'type' => 'triple',
564                        'subject' => array (
565                            'type' => 'variable',
566                            'text' => 'relation'
567                        ),
568                        'predicate' => array (
569                            'type' => 'literal',
570                            'text' => 'name'
571                        ),
572                        'object' => array (
573                            'type' => 'literal',
574                            'text' => 'Alice'
575                        )
576                    )
577                )
578            ),
579            'projection' => array (
580                'p',
581                'relationWith'
582            ),
583            'ordering' => array (
584                array (
585                    'variable' => 'p',
586                    'direction' => 'asc'
587                )
588            )
589        );
590
591        $expected = array (
592            array (
593                'p' => array('person:alice'),
594                'relationWith' => array()
595            ),
596            array (
597                'p' => array('person:bob'),
598                'relationWith' => array('knows')
599            ),
600            array (
601                'p' => array('person:bob'),
602                'relationWith' => array('likes')
603            ),
604            array (
605                'p' => array('person:carol'),
606                'relationWith' => array('knows')
607            )
608        );
609
610        $this->assertQueryResult($query, $expected);
611    }
612
613    function testAllPersonsAndSpecialRelationWithAlice() {
614        // All persons, including their relation with Alice (unless this relation is 'knows')
615        $query = array (
616            'type' => 'select',
617            'grouping'=>array(),
618            'group' => array (
619                'type' => 'optional',
620                'lhs' => $this->_isPerson,
621                'rhs' => array (
622                    'type' => 'filter',
623                    'lhs' => array (
624                        'type' => 'and',
625                        'lhs' => array (
626                            'type' => 'triple',
627                            'subject' => array (
628                                'type' => 'variable',
629                                'text' => 'p'
630                            ),
631                            'predicate' => array (
632                                'type' => 'variable',
633                                'text' => 'relationWith'
634                            ),
635                            'object' => array (
636                                'type' => 'variable',
637                                'text' => 'relation'
638                            )
639                        ),
640                        'rhs' => array (
641                            'type' => 'triple',
642                            'subject' => array (
643                                'type' => 'variable',
644                                'text' => 'relation'
645                            ),
646                            'predicate' => array (
647                                'type' => 'literal',
648                                'text' => 'name'
649                            ),
650                            'object' => array (
651                                'type' => 'literal',
652                                'text' => 'Alice'
653                            )
654                        )
655                    ),
656                    'rhs' => array (
657                        array (
658                            'type' => 'filter',
659                            'lhs' => array (
660                                'type' => 'variable',
661                                'text' => 'relationWith'
662                            ),
663                            'operator' => '!=',
664                            'rhs' => array (
665                                'type' => 'literal',
666                                'text' => 'knows'
667                            )
668                        )
669                    )
670                )
671            ),
672            'projection' => array (
673                'p',
674                'relationWith'
675            ),
676            'ordering' => array (
677                array (
678                    'variable' => 'p',
679                    'direction' => 'asc'
680                )
681            )
682        );
683
684        $expected = array (
685            array (
686                'p' => array('person:alice'),
687                'relationWith' => array()
688            ),
689            array (
690                'p' => array('person:bob'),
691                'relationWith' => array('likes')
692            ),
693            array (
694                'p' => array('person:carol'),
695                'relationWith' => array()
696            )
697        );
698
699        $this->assertQueryResult($query, $expected);
700    }
701
702    function testAllPersonsSpecialRelationWithAlice() {
703        // All persons having a relation with Alice that is not 'knows'
704        $query = array (
705            'type' => 'select',
706            'grouping'=>array(),
707            'group' => array (
708                'type' => 'filter',
709                'lhs' => array (
710                    'type' => 'and',
711                    'lhs' => array (
712                        'type' => 'and',
713                        'lhs' => $this->_isPerson,
714                        'rhs' => array (
715                            'type' => 'triple',
716                            'subject' => array (
717                                'type' => 'variable',
718                                'text' => 'p'
719                            ),
720                            'predicate' => array (
721                                'type' => 'variable',
722                                'text' => 'relationWith'
723                            ),
724                            'object' => array (
725                                'type' => 'variable',
726                                'text' => 'relation'
727                            )
728                        )
729                    ),
730                    'rhs' => array (
731                        'type' => 'triple',
732                        'subject' => array (
733                            'type' => 'variable',
734                            'text' => 'relation'
735                        ),
736                        'predicate' => array (
737                            'type' => 'literal',
738                            'text' => 'name'
739                        ),
740                        'object' => array (
741                            'type' => 'literal',
742                            'text' => 'Alice'
743                        )
744                    )
745                ),
746                'rhs' => array (
747                    array (
748                        'type' => 'filter',
749                        'lhs' => array (
750                            'type' => 'variable',
751                            'text' => 'relationWith'
752                        ),
753                        'operator' => '!=',
754                        'rhs' => array (
755                            'type' => 'literal',
756                            'text' => 'knows'
757                        )
758                    )
759                )
760            ),
761            'projection' => array (
762                'p',
763                'relationWith'
764            ),
765            'ordering' => array (
766                array (
767                    'variable' => 'p',
768                    'direction' => 'asc'
769                )
770            )
771        );
772
773        $expected = array (
774            array (
775                'p' => array('person:bob'),
776                'relationWith' => array('likes')
777            )
778        );
779
780        $this->assertQueryResult($query, $expected);
781    }
782
783    function testBobUnionAlice() {
784        $query = array (
785            'type' => 'select',
786            'grouping'=>array(),
787            'group' => $this->_bobUnionAlice,
788            'projection' => array (
789                'p'
790            ),
791            'ordering' => array (
792                array (
793                    'variable' => 'p',
794                    'direction' => 'asc'
795                )
796            )
797        );
798
799        $expected = array (
800            array (
801                'p' => array('person:alice')
802            ),
803            array (
804                'p' => array('person:bob')
805            )
806        );
807
808        $this->assertQueryResult($query, $expected);
809    }
810
811    function testBobUnionAliceWithRating() {
812        $query = array (
813            'type' => 'select',
814            'grouping'=>array(),
815            'group' => array (
816                'type' => 'union',
817                'lhs' => array (
818                    'type' => 'and',
819                    'lhs' => $this->_personBob,
820                    'rhs' => $this->_personRating
821                ),
822                'rhs' => array (
823                    'type' => 'and',
824                    'lhs' => $this->_personAlice,
825                    'rhs' => $this->_personRating
826                ),
827            ),
828            'projection' => array (
829                'p',
830                'rating'
831            ),
832            'ordering' => array (
833                array (
834                    'variable' => 'p',
835                    'direction' => 'asc'
836                )
837            )
838        );
839
840        $expected = array (
841            array (
842                'p' => array('person:alice'),
843                'rating' => array('10')
844            ),
845            array (
846                'p' => array('person:bob'),
847                'rating' => array('8')
848            )
849        );
850
851        $this->assertQueryResult($query, $expected);
852    }
853
854    function testCarolUnionBobUnionAlice() {
855        $query = array (
856            'type' => 'select',
857            'grouping'=>array(),
858            'group' => array (
859                'type' => 'union',
860                'lhs' => $this->_personCarol,
861                'rhs' => $this->_bobUnionAlice
862            ),
863            'projection' => array (
864                'p'
865            ),
866            'ordering' => array (
867                array (
868                    'variable' => 'p',
869                    'direction' => 'asc'
870                )
871            )
872        );
873
874        $expected = array (
875            array (
876                'p' => array('person:alice')
877            ),
878            array (
879                'p' => array('person:bob')
880            ),
881            array (
882                'p' => array('person:carol')
883            )
884        );
885
886        $this->assertQueryResult($query, $expected);
887    }
888
889    function testBobUnionBobUnionAlice() {
890        $query = array (
891            'type' => 'select',
892            'grouping'=>array(),
893            'group' => array (
894                'type' => 'union',
895                'lhs' => $this->_personBob,
896                'rhs' => $this->_bobUnionAlice
897            ),
898            'projection' => array (
899                'p'
900            ),
901            'ordering' => array (
902                array (
903                    'variable' => 'p',
904                    'direction' => 'asc'
905                )
906            )
907        );
908
909        $expected = array (
910            array (
911                'p' => array('person:alice')
912            ),
913            array (
914                'p' => array('person:bob')
915            )
916        );
917
918        $this->assertQueryResult($query, $expected);
919    }
920
921    function testCarolUnionBobUnionAliceMinusBob() {
922        $query = array (
923            'type' => 'select',
924            'grouping'=>array(),
925            'group' => array (
926                'type' => 'union',
927                'lhs' => $this->_personCarol,
928                'rhs' => array (
929                    'type' => 'minus',
930                    'lhs' => $this->_bobUnionAlice,
931                    'rhs' => $this->_personBob
932                )
933            ),
934            'projection' => array (
935                'p'
936            ),
937            'ordering' => array (
938                array (
939                    'variable' => 'p',
940                    'direction' => 'asc'
941                )
942            )
943        );
944
945        $expected = array (
946            array (
947                'p' => array('person:alice')
948            ),
949            array (
950                'p' => array('person:carol')
951            )
952        );
953
954        $this->assertQueryResult($query, $expected);
955    }
956
957    function testBobUnionAliceOptionalRating() {
958        $query = array (
959            'type' => 'select',
960            'grouping'=>array(),
961            'group' => array (
962                'type' => 'union',
963                'lhs' => $this->_personBob,
964                'rhs' => array (
965                    'type' => 'optional',
966                    'lhs' => $this->_personAlice,
967                    'rhs' => $this->_personRating
968                )
969            ),
970            'projection' => array (
971                'p',
972                'rating'
973            ),
974            'ordering' => array (
975                array (
976                    'variable' => 'p',
977                    'direction' => 'asc'
978                )
979            )
980        );
981
982        $expected = array (
983            array (
984              'p' => array('person:alice'),
985              'rating' => array('10')
986            ),
987            array (
988              'p' => array('person:bob'),
989              'rating' => array()
990            )
991        );
992
993        $this->assertQueryResult($query, $expected);
994    }
995
996    function testPersonOptionalKnowsAndLikes() {
997        $query = array (
998            'type' => 'select',
999            'grouping'=>array(),
1000            'group' => array (
1001                'type' => 'optional',
1002                'lhs' => $this->_isPerson,
1003                'rhs' => array (
1004                    'type' => 'and',
1005                    'lhs' => $this->_personKnows,
1006                    'rhs' => $this->_personLikes
1007                )
1008            ),
1009            'projection' => array (
1010                'p',
1011                'knows',
1012                'likes'
1013            ),
1014            'ordering' => array (
1015                array (
1016                    'variable' => 'p',
1017                    'direction' => 'asc'
1018                )
1019            )
1020        );
1021
1022        $expected = array (
1023            array (
1024                'p' => array('person:alice'),
1025                'knows' => array(),
1026                'likes' => array()
1027            ),
1028            array (
1029                'p' => array('person:bob'),
1030                'knows' => array('person:alice'),
1031                'likes' => array('person:alice')
1032            ),
1033            array (
1034                'p' => array('person:carol'),
1035                'knows' => array(),
1036                'likes' => array()
1037            )
1038        );
1039
1040        $this->assertQueryResult($query, $expected);
1041    }
1042
1043    function testPersonOptionalKnowsOptionalLikes() {
1044        $query = array (
1045            'type' => 'select',
1046            'grouping'=>array(),
1047            'group' => array (
1048                'type' => 'optional',
1049                'lhs' => $this->_isPerson,
1050                'rhs' => $this->_knowsOptionalLikes
1051            ),
1052            'projection' => array (
1053                'p',
1054                'knows',
1055                'likes'
1056            ),
1057            'ordering' => array (
1058                array (
1059                    'variable' => 'p',
1060                    'direction' => 'asc'
1061                ),
1062                array (
1063                    'variable' => 'knows',
1064                    'direction' => 'asc'
1065                )
1066            )
1067        );
1068
1069        $expected = array (
1070            array (
1071                'p' => array('person:alice'),
1072                'knows' => array('person:carol'),
1073                'likes' => array()
1074            ),
1075            array (
1076                'p' => array('person:bob'),
1077                'knows' => array('person:alice'),
1078                'likes' => array('person:alice')
1079            ),
1080            array (
1081                'p' => array('person:carol'),
1082                'knows' => array('person:alice'),
1083                'likes' => array()
1084            ),
1085            array (
1086                'p' => array('person:carol'),
1087                'knows' => array('person:bob'),
1088                'likes' => array()
1089            )
1090        );
1091
1092        $this->assertQueryResult($query, $expected);
1093    }
1094
1095    function testPersonOptionalRatingUnionLikes() {
1096        $query = array (
1097            'type' => 'select',
1098            'grouping'=>array(),
1099            'group' => array (
1100                'type' => 'optional',
1101                'lhs' => $this->_isPerson,
1102                'rhs' => array (
1103                    'type' => 'union',
1104                    'lhs' => $this->_personRating,
1105                    'rhs' => array (
1106                        'type' => 'and',
1107                        'lhs' => array (
1108                            'type' => 'triple',
1109                            'subject' => array (
1110                                'type' => 'variable',
1111                                'text' => 'p'
1112                            ),
1113                            'predicate' => array (
1114                                'type' => 'literal',
1115                                'text' => 'likes'
1116                            ),
1117                            'object' => array (
1118                                'type' => 'variable',
1119                                'text' => 'rating'
1120                            )
1121                        ),
1122                        'rhs' => array (
1123                            'type' => 'triple',
1124                            'subject' => array (
1125                                'type' => 'variable',
1126                                'text' => 'p'
1127                            ),
1128                            'predicate' => array (
1129                                'type' => 'literal',
1130                                'text' => 'likes'
1131                            ),
1132                            'object' => array (
1133                                'type' => 'variable',
1134                                'text' => 'likes'
1135                            )
1136                        )
1137                    )
1138                )
1139            ),
1140            'projection' => array (
1141                'p',
1142                'rating'
1143            ),
1144            'ordering' => array (
1145                array (
1146                    'variable' => 'p',
1147                    'direction' => 'asc'
1148                ),
1149                array (
1150                    'variable' => 'likes',
1151                    'direction' => 'desc'
1152                )
1153            )
1154        );
1155
1156        $expected = array (
1157            array (
1158                'p' => array('person:alice'),
1159                'rating' => array('10')
1160            ),
1161            array (
1162                'p' => array('person:bob'),
1163                'rating' => array('person:alice')
1164            ),
1165            array (
1166                'p' => array('person:bob'),
1167                'rating' => array('8')
1168            ),
1169            array (
1170                'p' => array('person:carol'),
1171                'rating' => array('1')
1172            )
1173        );
1174
1175        $this->assertQueryResult($query, $expected);
1176    }
1177
1178    function testPersonOptionalRatingMinusCarol() {
1179        $query = array (
1180            'type' => 'select',
1181            'grouping'=>array(),
1182            'group' => array (
1183                'type' => 'optional',
1184                'lhs' => $this->_isPerson,
1185                'rhs' => $this->_ratingMinusCarol
1186            ),
1187            'projection' => array (
1188                'p',
1189                'rating'
1190            ),
1191            'ordering' => array (
1192                array (
1193                    'variable' => 'p',
1194                    'direction' => 'asc'
1195                )
1196            )
1197        );
1198
1199        $expected = array (
1200            array (
1201                'p' => array('person:alice'),
1202                'rating' => array('10')
1203            ),
1204            array (
1205                'p' => array('person:bob'),
1206                'rating' => array('8')
1207            ),
1208            array (
1209                'p' => array('person:carol'),
1210                'rating' => array()
1211            )
1212        );
1213
1214        $this->assertQueryResult($query, $expected);
1215    }
1216
1217    function testPersonMinusBobUnionAlice() {
1218        $query = array (
1219            'type' => 'select',
1220            'grouping'=>array(),
1221            'group' => array (
1222                'type' => 'minus',
1223                'lhs' => $this->_isPerson,
1224                'rhs' => $this->_bobUnionAlice
1225            ),
1226            'projection' => array (
1227                'p'
1228            ),
1229            'ordering' => array (
1230                array (
1231                    'variable' => 'p',
1232                    'direction' => 'asc'
1233                )
1234            )
1235        );
1236
1237        $expected = array (
1238            array (
1239                'p' => array('person:carol')
1240            )
1241        );
1242
1243        $this->assertQueryResult($query, $expected);
1244    }
1245
1246    function testPersonMinusRatingMinusCarol() {
1247        $query = array (
1248            'type' => 'select',
1249            'grouping'=>array(),
1250            'group' => array (
1251                'type' => 'minus',
1252                'lhs' => $this->_isPerson,
1253                'rhs' => $this->_ratingMinusCarol
1254            ),
1255            'projection' => array (
1256                'p'
1257            ),
1258            'ordering' => array (
1259                array (
1260                    'variable' => 'p',
1261                    'direction' => 'asc'
1262                )
1263            )
1264        );
1265
1266        $expected = array (
1267            array (
1268                'p' => array('person:carol')
1269            )
1270        );
1271
1272        $this->assertQueryResult($query, $expected);
1273    }
1274
1275    function testPersonAndRatingMinusCarol() {
1276        $query = array (
1277            'type' => 'select',
1278            'grouping'=>array(),
1279            'group' => array (
1280                'type' => 'and',
1281                'lhs' => $this->_isPerson,
1282                'rhs' => $this->_ratingMinusCarol
1283            ),
1284            'projection' => array (
1285                'p',
1286                'rating'
1287            ),
1288            'ordering' => array (
1289                array (
1290                    'variable' => 'p',
1291                    'direction' => 'asc'
1292                )
1293            )
1294        );
1295
1296        $expected = array (
1297            array (
1298                'p' => array('person:alice'),
1299                'rating' => array('10')
1300            ),
1301            array (
1302                'p' => array('person:bob'),
1303                'rating' => array('8')
1304            )
1305        );
1306
1307        $this->assertQueryResult($query, $expected);
1308    }
1309
1310    function testPersonAndBobUnionAlice() {
1311        $query = array (
1312            'type' => 'select',
1313            'grouping'=>array(),
1314            'group' => array (
1315                'type' => 'and',
1316                'lhs' => $this->_isPerson,
1317                'rhs' => $this->_bobUnionAlice
1318            ),
1319            'projection' => array (
1320                'p'
1321            ),
1322            'ordering' => array (
1323                array (
1324                    'variable' => 'p',
1325                    'direction' => 'asc'
1326                )
1327            )
1328        );
1329
1330        $expected = array (
1331            array (
1332                'p' => array('person:alice')
1333            ),
1334            array (
1335                'p' => array('person:bob')
1336            )
1337        );
1338
1339        $this->assertQueryResult($query, $expected);
1340    }
1341
1342    function testPersonAndKnowsOptionalLikes() {
1343        $query = array (
1344            'type' => 'select',
1345            'grouping'=>array(),
1346            'group' => array (
1347                'type' => 'and',
1348                'lhs' => $this->_isPerson,
1349                'rhs' => $this->_knowsOptionalLikes
1350            ),
1351            'projection' => array (
1352                'p',
1353                'knows',
1354                'likes'
1355            ),
1356            'ordering' => array (
1357                array (
1358                    'variable' => 'p',
1359                    'direction' => 'asc'
1360                ),
1361                array (
1362                    'variable' => 'knows',
1363                    'direction' => 'asc'
1364                )
1365            )
1366        );
1367
1368        $expected = array (
1369            array (
1370                'p' => array('person:alice'),
1371                'knows' => array('person:carol'),
1372                'likes' => array()
1373            ),
1374            array (
1375                'p' => array('person:bob'),
1376                'knows' => array('person:alice'),
1377                'likes' => array('person:alice')
1378            ),
1379            array (
1380                'p' => array('person:carol'),
1381                'knows' => array('person:alice'),
1382                'likes' => array()
1383            ),
1384            array (
1385                'p' => array('person:carol'),
1386                'knows' => array('person:bob'),
1387                'likes' => array()
1388            )
1389        );
1390
1391        $this->assertQueryResult($query, $expected);
1392    }
1393
1394    function testVariableNames() {
1395        // Strange strings as variable names
1396        $query = array (
1397            'type' => 'select',
1398            'grouping'=>array(),
1399            'group' => array (
1400                'type' => 'and',
1401                'lhs' => array (
1402                    'type' => 'and',
1403                    'lhs' => array (
1404                        'type' => 'triple',
1405                        'subject' => array (
1406                            'type' => 'variable',
1407                            'text' => '_'
1408                        ),
1409                        'predicate' => array (
1410                            'type' => 'literal',
1411                            'text' => 'class'
1412                        ),
1413                        'object' => array (
1414                            'type' => 'variable',
1415                            'text' => '?c'
1416                        ),
1417                    ),
1418                    'rhs' => array (
1419                        'type' => 'and',
1420                        'lhs' => array (
1421                            'type' => 'triple',
1422                            'subject' => array (
1423                                'type' => 'variable',
1424                                'text' => '_'
1425                            ),
1426                            'predicate' => array (
1427                                'type' => 'literal',
1428                                'text' => 'name'
1429                            ),
1430                            'object' => array (
1431                                'type' => 'variable',
1432                                'text' => 'given name'
1433                            ),
1434                        ),
1435                        'rhs' => array (
1436                            'type' => 'triple',
1437                            'subject' => array (
1438                                'type' => 'variable',
1439                                'text' => '_'
1440                            ),
1441                            'predicate' => array (
1442                                'type' => 'literal',
1443                                'text' => 'knows'
1444                            ),
1445                            'object' => array (
1446                                'type' => 'variable',
1447                                'text' => '2 knów \'`"'
1448                            )
1449                        )
1450                    )
1451                ),
1452            'rhs' => array (
1453                    'type' => 'and',
1454                    'lhs' => array (
1455                        'type' => 'and',
1456                        'lhs' => array (
1457                            'type' => 'triple',
1458                            'subject' => array (
1459                                'type' => 'variable',
1460                                'text' => '_'
1461                            ),
1462                            'predicate' => array (
1463                                'type' => 'literal',
1464                                'text' => 'has length'
1465                            ),
1466                            'object' => array (
1467                                'type' => 'variable',
1468                                'text' => ':l'
1469                            ),
1470                        ),
1471                        'rhs' => array (
1472                            'type' => 'triple',
1473                            'subject' => array (
1474                                'type' => 'variable',
1475                                'text' => '_'
1476                            ),
1477                            'predicate' => array (
1478                                'type' => 'literal',
1479                                'text' => 'tax rate'
1480                            ),
1481                            'object' => array (
1482                                'type' => 'variable',
1483                                'text' => '%t'
1484                            )
1485                        )
1486                    ),
1487                'rhs' => array (
1488                        'type' => 'and',
1489                        'lhs' => array (
1490                            'type' => 'triple',
1491                            'subject' => array (
1492                                'type' => 'variable',
1493                                'text' => '_'
1494                            ),
1495                            'predicate' => array (
1496                                'type' => 'literal',
1497                                'text' => 'is rated'
1498                            ),
1499                            'object' => array (
1500                                'type' => 'variable',
1501                                'text' => '1.10'
1502                            ),
1503                        ),
1504                        'rhs' => array (
1505                            'type' => 'triple',
1506                            'subject' => array (
1507                                'type' => 'variable',
1508                                'text' => '_'
1509                            ),
1510                            'predicate' => array (
1511                                'type' => 'literal',
1512                                'text' => 'looks like'
1513                            ),
1514                            'object' => array (
1515                                'type' => 'variable',
1516                                'text' => '  \\  like ""! '
1517                            )
1518                        )
1519                    )
1520                )
1521            ),
1522            'projection' => array (
1523                '_',
1524                '?c',
1525                'given name',
1526                '2 knów \'`"',
1527                ':l',
1528                '%t',
1529                '1.10',
1530                '  \\  like ""! '
1531            ),
1532            'ordering' => array (
1533                array (
1534                    'variable' => '_',
1535                    'direction' => 'asc'
1536                ),
1537                array (
1538                    'variable' => '2 knów \'`"',
1539                    'direction' => 'asc'
1540                )
1541            )
1542        );
1543
1544        $expected = array (
1545            array (
1546                '_' => array('person:alice'),
1547                '?c' => array('person'),
1548                'given name' => array('Alice'),
1549                '2 knów \'`"' => array('person:carol'),
1550                ':l' => array('5 ft 5 in'),
1551                '%t' => array('10%'),
1552                '1.10' => array('10'),
1553                '  \\  like ""! ' => array('50:alice.svg')
1554            ),
1555            array (
1556                '_' => array('person:bob'),
1557                '?c' => array('person'),
1558                'given name' => array('Bob'),
1559                '2 knów \'`"' => array('person:alice'),
1560                ':l' => array('5 ft 10 in'),
1561                '%t' => array('25%'),
1562                '1.10' => array('8'),
1563                '  \\  like ""! ' => array('50:bob.png')
1564            ),
1565            array (
1566                '_' => array('person:carol'),
1567                '?c' => array('person'),
1568                'given name' => array('Carol'),
1569                '2 knów \'`"' => array('person:alice'),
1570                ':l' => array('4 ft 11 in'),
1571                '%t' => array('2%'),
1572                '1.10' => array('1'),
1573                '  \\  like ""! ' => array('50:carol.jpg')
1574            ),
1575            array (
1576                '_' => array('person:carol'),
1577                '?c' => array('person'),
1578                'given name' => array('Carol'),
1579                '2 knów \'`"' => array('person:bob'),
1580                ':l' => array('4 ft 11 in'),
1581                '%t' => array('2%'),
1582                '1.10' => array('1'),
1583                '  \\  like ""! ' => array('50:carol.jpg')
1584            )
1585        );
1586
1587        $this->assertQueryResult($query, $expected);
1588    }
1589
1590    function testGrouping() {
1591        $query = array (
1592            'type' => 'select',
1593            'grouping' => array (
1594                'knows',
1595                'rating'
1596            ),
1597            'group' => array (
1598                'type' => 'and',
1599                'lhs' => array (
1600                    'type' => 'and',
1601                    'lhs' => array (
1602                        'type' => 'and',
1603                        'lhs' => array (
1604                            'type' => 'and',
1605                            'lhs' => $this->_isPerson,
1606                            'rhs' => $this->_personKnows
1607                        ),
1608                        'rhs' => array (
1609                            'type' => 'triple',
1610                            'subject' => array (
1611                                'type' => 'variable',
1612                                'text' => 'knows'
1613                            ),
1614                            'predicate' => array (
1615                                'type' => 'literal',
1616                                'text' => 'knows'
1617                            ),
1618                            'object' => array (
1619                                'type' => 'variable',
1620                                'text' => 'who knows'
1621                            )
1622                        )
1623                    ),
1624                    'rhs' => $this->_personRating
1625                ),
1626                'rhs' => array (
1627                    'type' => 'triple',
1628                    'subject' => array (
1629                        'type' => 'variable',
1630                        'text' => 'knows'
1631                    ),
1632                    'predicate' => array (
1633                        'type' => 'literal',
1634                        'text' => 'identifier'
1635                    ),
1636                    'object' => array (
1637                        'type' => 'variable',
1638                        'text' => 'knows id'
1639                    )
1640                )
1641            ),
1642            'projection' => array (
1643                'p',
1644                'knows id',
1645                'who knows',
1646            ),
1647            'ordering' => array (
1648                array (
1649                    'variable' => 'rating',
1650                    'direction' => 'desc'
1651                ),
1652                array (
1653                    'variable' => 'knows',
1654                    'direction' => 'asc'
1655                ),
1656                array (
1657                    'variable' => 'who knows',
1658                    'direction' => 'asc'
1659                )
1660            )
1661        );
1662
1663        $expected = array (
1664            array (
1665                'p' => array('person:alice', 'person:alice'),
1666                'knows id' => array('γ', 'γ'),
1667                'who knows' => array('person:alice', 'person:bob'),
1668            ),
1669            array (
1670                'p' => array('person:bob'),
1671                'knows id' => array('α'),
1672                'who knows' => array('person:carol'),
1673            ),
1674            array (
1675                'p' => array('person:carol'),
1676                'knows id' => array('α'),
1677                'who knows' => array('person:carol'),
1678            ),
1679            array (
1680                'p' => array('person:carol'),
1681                'knows id' => array('Β'),
1682                'who knows' => array('person:alice'),
1683            )
1684        );
1685
1686        $this->assertQueryResult($query, $expected);
1687    }
1688}
1689
1690