1 00:00:00,280 --> 00:00:01,490 In this video, we'll demonstrate 2 00:00:02,350 --> 00:00:03,360 XPath by running a number of 3 00:00:03,510 --> 00:00:04,950 queries over our bookstore data. 4 00:00:05,760 --> 00:00:06,590 Let's first take a look at 5 00:00:06,670 --> 00:00:08,100 the data, we've expanded it slightly 6 00:00:08,520 --> 00:00:09,540 over what we've been using in 7 00:00:09,640 --> 00:00:10,940 previous videos, but it 8 00:00:11,020 --> 00:00:12,880 continues to have pretty much the same structure. 9 00:00:13,850 --> 00:00:14,740 We have a number of books. 10 00:00:15,160 --> 00:00:17,160 Books have attributes, ISBN, price, 11 00:00:17,820 --> 00:00:19,080 sometimes in addition, they have 12 00:00:19,190 --> 00:00:22,640 a title sub element, authors with first name and last name. 13 00:00:23,390 --> 00:00:26,330 So we have our first course book and our complete book. 14 00:00:27,640 --> 00:00:29,890 And our complete book also has a remark as you may recall. 15 00:00:30,910 --> 00:00:32,740 Then I've added a couple more books. 16 00:00:33,150 --> 00:00:34,330 I've added Hector and Jeff's 17 00:00:34,700 --> 00:00:35,860 Database Hints by Jeffrey 18 00:00:36,250 --> 00:00:37,880 Ullman and Hector Garcia Molina with 19 00:00:38,070 --> 00:00:40,230 a remark, an indispensible companion to your textbook. 20 00:00:41,260 --> 00:00:42,690 I've also 21 00:00:43,050 --> 00:00:44,770 added Jennifer's Economical Database 22 00:00:45,260 --> 00:00:46,210 Hints, for that at a 23 00:00:46,300 --> 00:00:49,120 mere price of $25 you get some hints. 24 00:00:49,880 --> 00:00:50,880 And then finally just to demonstrate 25 00:00:51,600 --> 00:00:53,730 certain expressions, I've inserted 26 00:00:54,190 --> 00:00:56,680 three magazines, two National Geographics and a Newsweek. 27 00:00:57,580 --> 00:00:58,660 And finally, a magazine seen 28 00:00:58,890 --> 00:01:00,880 called Hector and Jeff's Database Hints. 29 00:01:02,110 --> 00:01:04,420 So with this data in mind, let's move to the queries. 30 00:01:05,110 --> 00:01:07,920 We'll start with simple queries, and get more complicated as we proceed. 31 00:01:08,470 --> 00:01:10,280 In this window, we'll 32 00:01:10,380 --> 00:01:11,660 be putting our XPath expressions 33 00:01:12,260 --> 00:01:13,480 in the upper pane, then we'll 34 00:01:13,750 --> 00:01:16,120 execute the query and we'll see the results in the lower pane. 35 00:01:17,020 --> 00:01:18,580 The way XPath works, the 36 00:01:19,160 --> 00:01:20,370 first part of every expression 37 00:01:21,370 --> 00:01:22,820 specifies the document document 38 00:01:22,960 --> 00:01:24,860 over which the XPath expression is to be evaluated. 39 00:01:26,010 --> 00:01:28,560 So we have the data that you saw in a document called bookstoreq.xml 40 00:01:30,030 --> 00:01:30,900 and you'll see in each of 41 00:01:31,020 --> 00:01:31,980 our expressions that we begin 42 00:01:32,330 --> 00:01:34,020 by specifying that document, and 43 00:01:34,130 --> 00:01:36,200 then move ahead to the rest of the XPath expression. 44 00:01:37,280 --> 00:01:39,350 Our first expression is a very simple path expression. 45 00:01:40,220 --> 00:01:41,590 It says navigate through the XML 46 00:01:42,230 --> 00:01:43,170 by going first to the 47 00:01:43,290 --> 00:01:45,120 root element called bookstore, then 48 00:01:45,330 --> 00:01:46,410 look at all the books... so 49 00:01:46,900 --> 00:01:47,820 the elements of book store 50 00:01:48,200 --> 00:01:49,630 and finally all the titles of elements. 51 00:01:50,560 --> 00:01:53,070 Let's run the query and we will see our results below. 52 00:01:53,910 --> 00:01:54,950 So as we can see our results 53 00:01:55,380 --> 00:01:57,600 here is actually written in XML little header appears. 54 00:01:58,360 --> 00:01:59,760 And then we see the four titles 55 00:02:00,540 --> 00:02:01,850 of books that are in our database. 56 00:02:03,560 --> 00:02:05,040 Now let's modify our path expression. 57 00:02:05,970 --> 00:02:07,140 Instead of only getting book 58 00:02:07,370 --> 00:02:09,810 titles, let's get book or magazine titles. 59 00:02:10,650 --> 00:02:12,040 We do that by extending 60 00:02:12,760 --> 00:02:14,810 our middle matching element here 61 00:02:15,390 --> 00:02:16,270 to use a sort of regular 62 00:02:16,640 --> 00:02:18,790 expression like syntax, book or 63 00:02:18,980 --> 00:02:20,630 magazine, and we put it in parentheses. 64 00:02:21,320 --> 00:02:22,810 So now it says match any 65 00:02:23,140 --> 00:02:24,380 path of the data that starts 66 00:02:24,880 --> 00:02:26,810 at the bookstore element, follows either 67 00:02:27,120 --> 00:02:28,640 a book or magazine sub-element, and 68 00:02:29,080 --> 00:02:30,230 then finally a title sub-element. 69 00:02:31,140 --> 00:02:32,560 When we run the query, we see 70 00:02:32,750 --> 00:02:33,690 now that we get not only 71 00:02:34,000 --> 00:02:35,190 the titles of our books, but 72 00:02:35,580 --> 00:02:36,690 also the titles of our magazines. 73 00:02:38,510 --> 00:02:40,020 So far, we've mentioned element names 74 00:02:40,420 --> 00:02:41,580 explicitly in our path expressions. 75 00:02:42,250 --> 00:02:43,490 But as I mentioned in the 76 00:02:43,820 --> 00:02:44,890 introductory video, we can also 77 00:02:45,290 --> 00:02:46,370 use what's known as a wild 78 00:02:46,700 --> 00:02:47,920 card symbol, the symbol star. 79 00:02:49,360 --> 00:02:51,270 Star says to match any element name. 80 00:02:51,970 --> 00:02:53,040 So now we're going to start 81 00:02:53,330 --> 00:02:55,050 again with bookstore, match any 82 00:02:55,390 --> 00:02:57,080 element below bookstore, and finally, 83 00:02:58,100 --> 00:03:00,250 find titles of elements below those any elements. 84 00:03:01,010 --> 00:03:02,030 Now it so happens that the 85 00:03:02,170 --> 00:03:03,890 only elements below bookstore are 86 00:03:03,970 --> 00:03:05,250 books and magazines, so when 87 00:03:05,570 --> 00:03:06,720 we run the query, we will get 88 00:03:07,150 --> 00:03:08,320 exactly the same result. 89 00:03:09,880 --> 00:03:11,190 So far we've been navigating with 90 00:03:11,320 --> 00:03:13,030 the single slash operator which tells 91 00:03:13,260 --> 00:03:14,570 us to go one element at a time. 92 00:03:14,990 --> 00:03:16,490 We're at a particular element and 93 00:03:16,880 --> 00:03:19,770 then we match sub-elements with the specific tag that we typed. 94 00:03:20,080 --> 00:03:22,180 There is also the double slash operator. 95 00:03:23,090 --> 00:03:24,360 As you recall from the introductory 96 00:03:24,770 --> 00:03:26,370 video, double slash says 97 00:03:27,050 --> 00:03:28,740 match myself or any 98 00:03:28,980 --> 00:03:30,620 descendants of myself to any length. 99 00:03:31,490 --> 00:03:32,600 So if I put a double 100 00:03:32,980 --> 00:03:34,250 slash title, what we'll 101 00:03:34,380 --> 00:03:35,640 be matching is any title 102 00:03:36,060 --> 00:03:38,260 element anywhere at all in the XML tree. 103 00:03:39,040 --> 00:03:40,110 We run the query, and again 104 00:03:40,390 --> 00:03:41,570 we get exactly the same result 105 00:03:41,950 --> 00:03:43,100 because we had already been getting 106 00:03:43,440 --> 00:03:44,660 all of the titles which were 107 00:03:44,740 --> 00:03:46,400 sub-elements of books or magazines. 108 00:03:48,040 --> 00:03:49,400 Now let's get something a little different. 109 00:03:50,700 --> 00:03:51,800 Let's put slash, slash, star. 110 00:03:52,080 --> 00:03:53,060 Now that's kind of a 111 00:03:53,240 --> 00:03:54,770 wild thing to put, because it 112 00:03:54,990 --> 00:03:55,970 says I'm going to match any 113 00:03:56,170 --> 00:03:57,190 element in the entire tree, 114 00:03:57,840 --> 00:03:58,740 and furthermore, it can be 115 00:03:58,880 --> 00:04:01,930 of any the element type, let's run the query and see what happens. 116 00:04:04,340 --> 00:04:06,670 What we get is a huge result. 117 00:04:06,960 --> 00:04:08,500 Let me just scroll down so you can see the result. 118 00:04:09,390 --> 00:04:10,360 In fact, what we're getting 119 00:04:10,990 --> 00:04:12,960 is every element at 120 00:04:13,110 --> 00:04:13,900 every level of the tree, 121 00:04:14,320 --> 00:04:15,610 including all of its sub elements. 122 00:04:16,420 --> 00:04:17,850 So in fact, the first element 123 00:04:18,210 --> 00:04:19,290 in our result is our entire 124 00:04:19,880 --> 00:04:21,310 tree because it's our book store element. 125 00:04:21,760 --> 00:04:24,400 And we'll go all the way down to the end of the book store. 126 00:04:25,210 --> 00:04:26,170 The next element in our 127 00:04:26,280 --> 00:04:28,900 result is some children of the book store, so we get the book elements. 128 00:04:29,950 --> 00:04:32,080 And we're also going to get their children in the answer. 129 00:04:32,590 --> 00:04:33,510 And as we keep scrolling 130 00:04:33,930 --> 00:04:34,810 down, we'll see that we 131 00:04:34,990 --> 00:04:37,100 get every element of the entire database. 132 00:04:37,970 --> 00:04:39,280 That's not a useful query but 133 00:04:39,410 --> 00:04:41,070 it does demonstrate the construct, the 134 00:04:41,130 --> 00:04:42,580 double slash matching any element 135 00:04:42,960 --> 00:04:44,140 in the tree and the 136 00:04:44,370 --> 00:04:46,060 star matching any tag of any element. 137 00:04:46,420 --> 00:04:48,950 Now let's turn to attributes. 138 00:04:49,810 --> 00:04:51,230 Let's suppose we're interested in 139 00:04:51,690 --> 00:04:53,040 returning all the ISBN number 140 00:04:53,490 --> 00:04:54,390 in the database, so we'll go 141 00:04:54,530 --> 00:04:55,530 back to saying book store, 142 00:04:56,420 --> 00:04:57,960 books of elements and then 143 00:04:58,220 --> 00:04:59,990 we'll get the attribute ISBN. 144 00:05:00,740 --> 00:05:02,680 So we type at sign and ISBN. 145 00:05:04,180 --> 00:05:06,280 Let's run the query and we get an error. 146 00:05:06,870 --> 00:05:08,200 It turns out that attributes 147 00:05:08,930 --> 00:05:10,060 cannot be what is called 148 00:05:10,460 --> 00:05:11,850 serialized in order to 149 00:05:12,460 --> 00:05:14,170 return them in an XML-looking result. 150 00:05:15,070 --> 00:05:15,960 So what we need to do 151 00:05:16,210 --> 00:05:17,380 actually is obtain the data 152 00:05:17,990 --> 00:05:19,730 from the attribute itself, and once 153 00:05:19,940 --> 00:05:22,160 we do that, we'll see that we're getting the answer that we desire. 154 00:05:22,970 --> 00:05:23,950 So we'll ask for the data 155 00:05:24,400 --> 00:05:25,670 of the attribute, run the query, 156 00:05:26,120 --> 00:05:28,660 and now we see we have all its ISBN numbers. 157 00:05:29,480 --> 00:05:31,050 Now the attribute data is 158 00:05:31,270 --> 00:05:32,530 just strings, so we're returning 159 00:05:33,100 --> 00:05:34,710 the ISBN numbers as a 160 00:05:35,040 --> 00:05:37,800 set of strings with blanks between them. 161 00:05:37,940 --> 00:05:38,750 So some of these are sort 162 00:05:38,970 --> 00:05:41,620 of peculiarities of how XPath works. 163 00:05:42,380 --> 00:05:43,410 Again, we were not able to 164 00:05:43,520 --> 00:05:44,720 return an attribute because it 165 00:05:44,780 --> 00:05:45,780 didn't know how to structure the 166 00:05:45,870 --> 00:05:46,750 result, but once we extracted 167 00:05:47,250 --> 00:05:49,710 the data from the attribute, it returned it as string values. 168 00:05:51,330 --> 00:05:52,390 So far, we've only seen 169 00:05:52,670 --> 00:05:54,040 path expressions with no conditions 170 00:05:54,580 --> 00:05:56,030 involved, so let's throw in a condition. 171 00:05:56,960 --> 00:05:58,190 Let's say that we're interested in 172 00:05:58,300 --> 00:06:00,650 returning books that cost less than $90. 173 00:06:01,150 --> 00:06:02,340 So what we're going 174 00:06:02,520 --> 00:06:04,260 to do here is navigate to 175 00:06:04,440 --> 00:06:05,540 the book, and then we're 176 00:06:05,650 --> 00:06:07,080 gonna use the square bracket which 177 00:06:07,290 --> 00:06:08,880 says start evaluate a 178 00:06:09,180 --> 00:06:10,880 condition at the current point of the navigation. 179 00:06:11,920 --> 00:06:12,860 So the condition that I'm going 180 00:06:13,050 --> 00:06:14,010 to put is that the 181 00:06:14,070 --> 00:06:15,680 price of the book is less than 90. 182 00:06:16,390 --> 00:06:17,770 We'll run that query 183 00:06:18,510 --> 00:06:19,860 and we'll see that we have 184 00:06:21,100 --> 00:06:22,160 two books whose price...three books, 185 00:06:22,240 --> 00:06:24,410 I apologize, whose price 186 00:06:25,050 --> 00:06:25,950 is less than 90. 187 00:06:26,750 --> 00:06:28,200 Now here we return 188 00:06:28,580 --> 00:06:30,420 the book that satisfied the condition. 189 00:06:31,180 --> 00:06:32,040 What if what we actually want 190 00:06:32,240 --> 00:06:34,780 to return is the title of the books whose price is less than 90? 191 00:06:35,270 --> 00:06:36,240 What we can do is 192 00:06:36,530 --> 00:06:38,370 after evaluating this condition on 193 00:06:38,640 --> 00:06:40,340 the book, we can actually continue our navigation. 194 00:06:41,510 --> 00:06:43,040 So we just put slash title here. 195 00:06:43,730 --> 00:06:44,800 It says find the books, only 196 00:06:45,040 --> 00:06:45,820 keep the ones that match 197 00:06:46,180 --> 00:06:47,790 the condition, and then continue navigating 198 00:06:48,350 --> 00:06:50,740 down to their titles and return that as the result of the query. 199 00:06:51,460 --> 00:06:52,750 We run the query and we see our result. 200 00:06:54,510 --> 00:06:55,610 Now another type of condition 201 00:06:56,030 --> 00:06:57,070 that we can put in square brackets 202 00:06:57,710 --> 00:06:59,950 is an existence condition instead of a comparison. 203 00:07:01,220 --> 00:07:02,800 If we put, for example, just 204 00:07:03,360 --> 00:07:05,310 the label remark inside our 205 00:07:05,390 --> 00:07:06,890 square brackets, that says 206 00:07:07,140 --> 00:07:09,640 that we should match books that have a remark. 207 00:07:09,930 --> 00:07:11,500 So putting an element 208 00:07:11,850 --> 00:07:13,160 name in square brackets is an 209 00:07:13,240 --> 00:07:15,370 existence condition on that sub element existing. 210 00:07:16,570 --> 00:07:17,660 Once we've isolated the books 211 00:07:17,900 --> 00:07:20,230 that have a remark, we'll return the title of the books. 212 00:07:20,850 --> 00:07:23,760 We run the query and we discover that two of our books have a remark. 213 00:07:24,090 --> 00:07:26,450 You can go back and check the data and you'll see that's indeed the case. 214 00:07:28,010 --> 00:07:29,870 Let's get a little bit more complicated now. 215 00:07:30,140 --> 00:07:32,200 Let's get rid of this query and put in a whole new one. 216 00:07:33,030 --> 00:07:34,000 In this query we're going 217 00:07:34,310 --> 00:07:35,550 to find the titles of 218 00:07:35,650 --> 00:07:36,520 books where the price is less 219 00:07:36,770 --> 00:07:39,210 than 90 and where Ullman is one of the authors. 220 00:07:39,840 --> 00:07:41,120 So now we have in 221 00:07:41,360 --> 00:07:42,900 our square brackets a longer condition. 222 00:07:43,490 --> 00:07:44,350 The price is less than ninety 223 00:07:45,090 --> 00:07:47,070 and there exists, and implicitly 224 00:07:47,710 --> 00:07:48,770 this is an exist, there exists 225 00:07:49,740 --> 00:07:51,090 a sub part from the 226 00:07:51,150 --> 00:07:52,630 book, author slash author 227 00:07:53,240 --> 00:07:55,530 slash last name where the value of that is Ullman. 228 00:07:56,350 --> 00:07:57,760 If we satisfy both of 229 00:07:57,900 --> 00:07:59,090 those conditions it will return the 230 00:07:59,150 --> 00:08:00,530 title of the book, so we 231 00:08:00,720 --> 00:08:01,590 run the query and we discover 232 00:08:01,990 --> 00:08:02,990 two books that are less 233 00:08:03,210 --> 00:08:04,750 than 90 where Ullman is one of the authors. 234 00:08:05,290 --> 00:08:08,860 Now let's expand the query by adding another condition. 235 00:08:10,570 --> 00:08:11,910 We want not only the last 236 00:08:12,120 --> 00:08:14,070 name of the author to be Ullman but the first name to be Jeffrey. 237 00:08:14,740 --> 00:08:17,240 So now we're looking for books where Jeffrey Ullman is one of the authors, 238 00:08:18,290 --> 00:08:19,350 and the books are less than 90. 239 00:08:19,760 --> 00:08:20,780 So we run the query 240 00:08:21,020 --> 00:08:21,890 and we get the same result, 241 00:08:22,310 --> 00:08:24,660 not surprisingly, since Ullman is always paired with Jeffrey. 242 00:08:25,820 --> 00:08:27,410 But actually this is not 243 00:08:28,030 --> 00:08:28,850 doing quite what we're expecting, 244 00:08:29,350 --> 00:08:31,300 and I'm gonna explain why by demonstrating some changes. 245 00:08:32,510 --> 00:08:34,010 Let's say that we change 246 00:08:34,180 --> 00:08:35,520 our query to not look for 247 00:08:35,630 --> 00:08:38,000 Jeffrey Ullman as an author but to look for Jeffrey Widom. 248 00:08:38,760 --> 00:08:40,430 Hopefully, we'll get no answers, but 249 00:08:40,610 --> 00:08:41,610 when we run the query we 250 00:08:41,800 --> 00:08:44,250 see still get a book, "The First Course In Database Systems." 251 00:08:45,000 --> 00:08:46,310 So the two authors of 252 00:08:46,360 --> 00:08:47,110 that book, if you look back 253 00:08:47,340 --> 00:08:49,490 at the data, are Jeffrey Ullman and Jennifer Widom. 254 00:08:50,240 --> 00:08:52,620 So let's see why that book was returned in this query. 255 00:08:53,520 --> 00:08:54,680 The reason is, if we 256 00:08:54,820 --> 00:08:56,130 look closely at this condition, 257 00:08:56,720 --> 00:08:57,840 what we're saying is we're looking 258 00:08:58,210 --> 00:08:59,110 for books where the price is less 259 00:08:59,350 --> 00:09:00,530 than 90 and there exists 260 00:09:00,730 --> 00:09:02,820 an author's author last 261 00:09:03,160 --> 00:09:04,250 name path where the value is 262 00:09:04,380 --> 00:09:06,270 Widom and there exists an 263 00:09:06,450 --> 00:09:07,930 author's author first name 264 00:09:08,170 --> 00:09:10,450 path where the value is Jeffrey. 265 00:09:11,150 --> 00:09:12,100 Well, that in fact is true. 266 00:09:12,260 --> 00:09:13,360 We have one author whose 267 00:09:13,520 --> 00:09:14,600 last name is Widom and another 268 00:09:14,970 --> 00:09:16,150 author whose first name is Jeffrey. 269 00:09:17,330 --> 00:09:19,360 Let's try to formulate the correct that query now. 270 00:09:20,240 --> 00:09:21,620 So instead of matching the entire 271 00:09:22,100 --> 00:09:23,360 path to the last name and 272 00:09:23,510 --> 00:09:24,500 then the entire path to the 273 00:09:24,560 --> 00:09:25,800 first name separately through the 274 00:09:25,920 --> 00:09:27,380 author's sub elements, what we 275 00:09:27,540 --> 00:09:28,240 want to do so if we 276 00:09:28,340 --> 00:09:29,320 want to look at each author 277 00:09:29,760 --> 00:09:31,270 at a time and within that 278 00:09:31,450 --> 00:09:33,250 author look at the last name and first name together. 279 00:09:34,140 --> 00:09:35,420 So to modify our query to 280 00:09:35,550 --> 00:09:37,810 do that wer're going to use a condition within the condition. 281 00:09:38,690 --> 00:09:40,670 Specifically within the author 282 00:09:41,090 --> 00:09:42,740 slash author, we'll look 283 00:09:42,940 --> 00:09:44,670 at the last name and the first name. 284 00:09:44,890 --> 00:09:46,870 This syntax error is temporary once 285 00:09:47,150 --> 00:09:49,130 we finish the query, everything will look good. 286 00:09:50,050 --> 00:09:52,090 So we put a 287 00:09:52,130 --> 00:09:53,410 second bracket there and let 288 00:09:53,510 --> 00:09:54,460 me show again what I've done, 289 00:09:54,990 --> 00:09:55,830 that said we're looking for books 290 00:09:56,150 --> 00:09:57,190 where the price is less than 90 291 00:09:57,630 --> 00:09:59,130 and there exists an author 292 00:09:59,720 --> 00:10:01,270 slash author sub element where 293 00:10:01,420 --> 00:10:03,280 the last name is Widom and the first name is Jeffrey. 294 00:10:03,640 --> 00:10:05,340 Hopefully, we'll get an empty answer here. 295 00:10:06,020 --> 00:10:08,670 We execute the query and indeed we do. 296 00:10:08,990 --> 00:10:10,360 Now our original goal was 297 00:10:10,560 --> 00:10:12,170 to have Jeffrey Ullman, so finally 298 00:10:12,660 --> 00:10:14,260 we'll change Jeffrey Ullman, run 299 00:10:14,510 --> 00:10:16,380 the query, and now we get the correct answer. 300 00:10:17,310 --> 00:10:18,280 Incidentally, it's a very 301 00:10:18,800 --> 00:10:20,030 common mistake when we have 302 00:10:20,280 --> 00:10:22,100 a condition to put a slash before the condition. 303 00:10:22,640 --> 00:10:24,260 If we did that we would get a syntax error. 304 00:10:24,900 --> 00:10:25,890 When we write the square 305 00:10:26,270 --> 00:10:27,860 bracket, it essentially acts like 306 00:10:28,130 --> 00:10:29,130 a slash so when we reference 307 00:10:30,010 --> 00:10:31,320 a sub-element name within a 308 00:10:31,470 --> 00:10:34,240 square bracket, we're implicitly navigating that sub-element. 309 00:10:35,660 --> 00:10:37,990 Next, we're going to try a similar query with a twist. 310 00:10:38,400 --> 00:10:39,460 We're going to try to find books 311 00:10:39,590 --> 00:10:41,820 where Ullman is an author and Widom is not an author. 312 00:10:43,010 --> 00:10:44,650 So we, we navigate the books 313 00:10:44,770 --> 00:10:46,020 as usual and we look 314 00:10:46,190 --> 00:10:47,430 for cases where there's an 315 00:10:47,510 --> 00:10:48,820 authors author last name equals 316 00:10:49,070 --> 00:10:50,190 Ullman and there's an authors 317 00:10:50,370 --> 00:10:51,900 author last name not equal to Widom. 318 00:10:52,950 --> 00:10:54,150 Now you may already detect that 319 00:10:54,280 --> 00:10:56,320 the this is not the correct query, but let's go ahead and run. 320 00:10:56,430 --> 00:10:57,420 And we see that we 321 00:10:57,520 --> 00:10:58,490 got three books but we 322 00:10:58,670 --> 00:10:59,570 know the first two books, 323 00:10:59,920 --> 00:11:03,310 Widom is an author so as you may detected this is not correct. 324 00:11:03,760 --> 00:11:05,210 What this asks for are books 325 00:11:05,820 --> 00:11:06,980 where there's an author whose 326 00:11:07,150 --> 00:11:08,600 last name is Ullman and there's 327 00:11:09,350 --> 00:11:11,460 some author whose last name is not Widom. 328 00:11:12,320 --> 00:11:13,680 well, in fact, every book with 329 00:11:13,870 --> 00:11:16,430 Ullman as an author has some author whose last name is not Widom. 330 00:11:17,050 --> 00:11:17,350 That would be Ullman. 331 00:11:17,820 --> 00:11:18,800 So even if I took 332 00:11:18,970 --> 00:11:20,460 away this condition and ran 333 00:11:20,780 --> 00:11:23,060 the query again, I'll get exactly for the same results. 334 00:11:24,170 --> 00:11:25,730 Well, actually I got a syntax error. 335 00:11:27,070 --> 00:11:29,190 I forgot to erase 336 00:11:29,620 --> 00:11:30,710 the and, so let's get rid 337 00:11:30,820 --> 00:11:31,700 of that, run the query, 338 00:11:32,090 --> 00:11:34,520 and now we do in fact get the exact same result. 339 00:11:35,450 --> 00:11:36,230 So as a reminder, we were 340 00:11:36,350 --> 00:11:37,360 trying to find books where the 341 00:11:37,460 --> 00:11:38,520 last, where Ullman is an 342 00:11:38,680 --> 00:11:40,200 author and Widom is not, in fact 343 00:11:40,750 --> 00:11:43,470 we do not have construct yet to write that query. 344 00:11:43,700 --> 00:11:44,510 A little later in the demo 345 00:11:44,770 --> 00:11:45,620 we'll see how we can in 346 00:11:45,690 --> 00:11:47,460 a kind of tricky fashion but 347 00:11:47,700 --> 00:11:48,660 for what we've seen so far 348 00:11:48,850 --> 00:11:49,980 with path expressions and conditions, 349 00:11:50,710 --> 00:11:52,310 we're unable to write that specific query. 350 00:11:54,130 --> 00:11:55,100 So far, we've seen two 351 00:11:55,320 --> 00:11:56,420 types of conditions in brackets, 352 00:11:56,890 --> 00:11:58,480 we saw comparisons and we 353 00:11:58,630 --> 00:12:00,260 saw existence constraints where we 354 00:12:00,360 --> 00:12:03,070 checked to see whether a particular sub element existed. 355 00:12:04,100 --> 00:12:04,940 As you might remember from the 356 00:12:05,160 --> 00:12:06,130 intro, we can also put numbers 357 00:12:06,720 --> 00:12:08,470 inside in square brackets and those 358 00:12:08,670 --> 00:12:11,270 numbers tell us to return the F sub element. 359 00:12:11,970 --> 00:12:12,910 Specifically, if we look at 360 00:12:13,010 --> 00:12:14,370 this query, we're using slash, slash 361 00:12:14,660 --> 00:12:16,010 to navigate directly to authors 362 00:12:16,340 --> 00:12:17,410 elements and then we want 363 00:12:17,680 --> 00:12:18,920 to return the second author 364 00:12:19,430 --> 00:12:21,210 sub element of each author's element. 365 00:12:21,960 --> 00:12:23,130 So we run the query and we'll 366 00:12:23,380 --> 00:12:24,190 see if we look that our 367 00:12:24,310 --> 00:12:25,820 data that Jennifer Widom, Jeffrey 368 00:12:26,130 --> 00:12:27,750 Ullman and Hector Garcia Melina 369 00:12:27,910 --> 00:12:29,930 each appear once as the 370 00:12:30,090 --> 00:12:31,520 second author of a book 371 00:12:31,780 --> 00:12:33,150 or a magazine, if we 372 00:12:33,280 --> 00:12:34,730 changed this to three, we'll 373 00:12:35,080 --> 00:12:36,460 be returning third authors only 374 00:12:36,930 --> 00:12:39,360 and we can see only Jennifer Widom as a third author. 375 00:12:40,260 --> 00:12:41,230 If we change this to ten, 376 00:12:41,820 --> 00:12:43,060 hopefully, we'll get an empty 377 00:12:43,360 --> 00:12:44,820 result and in fact, we do. 378 00:12:46,260 --> 00:12:48,430 Now let's take a look at some built-in functions and predicates. 379 00:12:49,480 --> 00:12:50,660 In this query, we're going to 380 00:12:50,760 --> 00:12:52,210 find all books where there's 381 00:12:52,440 --> 00:12:54,640 a remark about the book that contains the word great. 382 00:12:55,350 --> 00:12:56,590 So we're going to navigate using slash, 383 00:12:56,840 --> 00:12:58,020 slash directly to book 384 00:12:58,150 --> 00:12:59,490 elements and within the 385 00:12:59,590 --> 00:13:00,540 book element, we'll have a condition 386 00:13:01,030 --> 00:13:02,250 that invokes the built in 387 00:13:02,400 --> 00:13:04,060 predicate contains, which I 388 00:13:04,130 --> 00:13:05,900 mentioned in the introductory video, which 389 00:13:06,070 --> 00:13:07,180 looks at two strings and checks 390 00:13:07,460 --> 00:13:09,350 whether the first string contains the second one. 391 00:13:10,030 --> 00:13:10,820 So if we have a book 392 00:13:11,020 --> 00:13:12,430 where there's a remark which is 393 00:13:12,590 --> 00:13:14,270 a string that contains the 394 00:13:14,350 --> 00:13:15,780 word great, then the book 395 00:13:16,000 --> 00:13:18,570 matches the condition and will return the title of the book. 396 00:13:19,170 --> 00:13:20,130 We run the query and we 397 00:13:20,260 --> 00:13:21,640 see that we have one book that 398 00:13:21,760 --> 00:13:23,150 has the remark containing the word great. 399 00:13:24,980 --> 00:13:26,620 Our next query does something kind of new. 400 00:13:27,330 --> 00:13:28,240 I like to call this query 401 00:13:28,600 --> 00:13:30,230 a self join but that's 402 00:13:30,430 --> 00:13:32,250 probably only because I'm a relationally biased person. 403 00:13:32,650 --> 00:13:33,710 But what it's actually doing 404 00:13:34,520 --> 00:13:35,840 is querying sort of two 405 00:13:36,210 --> 00:13:37,610 instances of our bookstore data 406 00:13:38,440 --> 00:13:40,220 at once and joining them together. 407 00:13:40,620 --> 00:13:41,980 So we'll see that our Doc 408 00:13:42,320 --> 00:13:44,200 Bookstore appears twice in this expression. 409 00:13:44,730 --> 00:13:46,330 Let me explain what this expression is doing. 410 00:13:47,110 --> 00:13:49,290 It's finding all magazines where 411 00:13:49,500 --> 00:13:50,510 there's a book that has 412 00:13:50,720 --> 00:13:51,750 the same title as the 413 00:13:51,810 --> 00:13:53,480 magazine and here's how it does it. 414 00:13:54,010 --> 00:13:55,460 So our first path expression navigates 415 00:13:56,290 --> 00:13:58,080 two magazines and then 416 00:13:58,260 --> 00:14:00,460 it extracts in the condition the title of the magazines. 417 00:14:01,830 --> 00:14:03,360 The magazine will match if 418 00:14:03,600 --> 00:14:05,970 the title equals some book 419 00:14:06,180 --> 00:14:07,240 title and so to 420 00:14:07,470 --> 00:14:08,560 find the book titles, we need 421 00:14:08,830 --> 00:14:10,050 to go back to the top 422 00:14:10,100 --> 00:14:11,090 of the document so we get 423 00:14:11,230 --> 00:14:12,120 a second incidence of the 424 00:14:12,260 --> 00:14:13,620 document and we find book titles. 425 00:14:14,480 --> 00:14:15,450 Now when we have the equals 426 00:14:15,820 --> 00:14:18,760 here, this equals is implicitly be existentially quantified. 427 00:14:19,610 --> 00:14:20,170 Did you follow that? 428 00:14:20,440 --> 00:14:20,990 Implicitly existentially quantified. 429 00:14:22,330 --> 00:14:23,640 That means that even though 430 00:14:23,750 --> 00:14:24,770 we're doing equals on what's 431 00:14:24,970 --> 00:14:26,340 effectively a set, the condition 432 00:14:26,900 --> 00:14:28,200 is satisfied if some element 433 00:14:29,270 --> 00:14:31,810 of the set is equal to the first title. 434 00:14:32,980 --> 00:14:32,980 Okay. 435 00:14:33,190 --> 00:14:34,850 There's a lot of implicit existential 436 00:14:35,420 --> 00:14:36,720 quantification going on in 437 00:14:36,840 --> 00:14:38,010 equality in XPath and 438 00:14:38,310 --> 00:14:40,200 in XQuery, as well, as we'll see later on. 439 00:14:40,920 --> 00:14:41,910 In any case, let's run the 440 00:14:42,000 --> 00:14:43,130 query and we will get 441 00:14:43,350 --> 00:14:44,460 back the fact that the 442 00:14:44,640 --> 00:14:45,910 magazine called "Hector and Jeff's 443 00:14:46,200 --> 00:14:47,270 Database Hints" has the same 444 00:14:47,520 --> 00:14:48,560 title as a book, and 445 00:14:48,630 --> 00:14:49,300 if you look back in the data, 446 00:14:49,540 --> 00:14:51,030 you'll see we do have a book of the same name. 447 00:14:53,070 --> 00:14:55,230 We saw one example of a built in predicate contains. 448 00:14:56,070 --> 00:14:57,440 This example shows another built 449 00:14:57,720 --> 00:14:58,740 in function, in this case 450 00:14:59,120 --> 00:15:00,630 the name function, and it 451 00:15:00,830 --> 00:15:02,990 also shows our first example of a navigation axis. 452 00:15:03,560 --> 00:15:04,770 We're going to use the parent axis. 453 00:15:05,760 --> 00:15:06,690 What this query is going 454 00:15:06,930 --> 00:15:08,190 to find is all elements 455 00:15:09,080 --> 00:15:10,580 whose parent element tag 456 00:15:11,120 --> 00:15:12,770 is not bookstore or book. 457 00:15:13,330 --> 00:15:14,780 Of course, this is just for demonstration purposes. 458 00:15:15,170 --> 00:15:16,840 It's not really that useful of a query. 459 00:15:17,430 --> 00:15:19,720 But let me just walk through the construction of the query. 460 00:15:20,580 --> 00:15:21,960 So we're starting with our 461 00:15:22,260 --> 00:15:23,350 bookstore and then we're using 462 00:15:23,670 --> 00:15:26,820 // which finds all elements. 463 00:15:27,460 --> 00:15:29,180 We saw // earlier when 464 00:15:29,320 --> 00:15:30,130 we ran the query we saw 465 00:15:30,340 --> 00:15:33,180 that it matched every element in the book, in the database. 466 00:15:33,990 --> 00:15:35,340 Now, since we've already put in bookstore. 467 00:15:35,740 --> 00:15:36,470 We're not going to match the 468 00:15:36,530 --> 00:15:37,920 bookstore element itself but we'll 469 00:15:38,070 --> 00:15:40,030 match every child of the bookstore element. 470 00:15:41,030 --> 00:15:42,120 So what the condition looks for 471 00:15:42,590 --> 00:15:43,810 is the tag of the 472 00:15:44,030 --> 00:15:45,140 parent of the current element 473 00:15:45,540 --> 00:15:46,670 and it sees if it's book 474 00:15:46,830 --> 00:15:47,730 store or book and we 475 00:15:47,940 --> 00:15:49,170 return the element if it's 476 00:15:49,400 --> 00:15:51,380 neither book store or book at the parent tag. 477 00:15:52,220 --> 00:15:53,590 Here's how we find the parent tag. 478 00:15:54,160 --> 00:15:55,080 So name is a built 479 00:15:55,220 --> 00:15:56,610 in function, name operates on 480 00:15:56,760 --> 00:15:58,920 an element and it returns the tag of that element. 481 00:15:59,250 --> 00:16:00,630 The element we want to 482 00:16:00,770 --> 00:16:01,800 look at is the parent of 483 00:16:01,910 --> 00:16:03,260 the current element and the 484 00:16:03,310 --> 00:16:04,310 way we do that is with the 485 00:16:04,380 --> 00:16:07,120 parent navigation axis which is parent colon, colon. 486 00:16:08,280 --> 00:16:10,940 Finally, the star is matching the tags of the parents. 487 00:16:11,480 --> 00:16:12,590 Well, here we say match any tag 488 00:16:12,870 --> 00:16:14,240 of the parent, extract the tag 489 00:16:14,920 --> 00:16:16,190 and check if it's book store or book. 490 00:16:16,790 --> 00:16:17,730 So when we run the query, 491 00:16:18,230 --> 00:16:19,140 we'll see that we get that 492 00:16:19,320 --> 00:16:20,720 pack a lot of data but 493 00:16:20,990 --> 00:16:22,410 all of them are elements 494 00:16:22,910 --> 00:16:25,550 in the database whose parent is not the book store or book. 495 00:16:27,640 --> 00:16:29,120 Here's another example of a navigation axis. 496 00:16:29,810 --> 00:16:31,370 In this case, we're using following sibling. 497 00:16:32,180 --> 00:16:33,720 Following sibling says if we 498 00:16:33,960 --> 00:16:34,970 are at a specific point in the 499 00:16:35,040 --> 00:16:35,980 tree you should match 500 00:16:36,460 --> 00:16:37,970 every sibling so every 501 00:16:38,300 --> 00:16:39,300 other element at the same 502 00:16:39,660 --> 00:16:40,940 level that's later in 503 00:16:41,030 --> 00:16:42,610 the document, that follows the current sibling. 504 00:16:43,640 --> 00:16:45,910 So let's walk through this expression and see what we're doing. 505 00:16:46,620 --> 00:16:47,850 What this expression is looking 506 00:16:48,120 --> 00:16:49,610 for is all books and 507 00:16:49,750 --> 00:16:51,770 magazines that have a non-unique title. 508 00:16:52,340 --> 00:16:53,270 In other words, all books or magazines 509 00:16:53,790 --> 00:16:54,900 where some other book or magazine 510 00:16:55,860 --> 00:16:56,560 has this same title. 511 00:16:57,320 --> 00:16:58,750 So we navigate down to 512 00:16:59,310 --> 00:17:00,980 books or magazine elements, this 513 00:17:01,050 --> 00:17:01,840 is what we saw in one of 514 00:17:01,910 --> 00:17:03,220 our earlier path expressions, we'll 515 00:17:03,340 --> 00:17:04,130 match any book or magazine 516 00:17:04,680 --> 00:17:06,040 element and then we 517 00:17:06,140 --> 00:17:07,140 want to find one where 518 00:17:07,350 --> 00:17:09,240 the title is equal to 519 00:17:09,490 --> 00:17:11,170 some title of a later sibling. 520 00:17:11,640 --> 00:17:13,000 Now our books and magazines are 521 00:17:13,190 --> 00:17:14,600 all at the same level in 522 00:17:14,720 --> 00:17:15,830 our data so when we 523 00:17:15,940 --> 00:17:17,610 do following sibling, we're going 524 00:17:17,930 --> 00:17:19,950 to be matching all other 525 00:17:20,270 --> 00:17:22,770 books and magazines that appear after the current one. 526 00:17:23,390 --> 00:17:24,700 And again, this star says that 527 00:17:24,820 --> 00:17:26,630 we can match on element of any type. 528 00:17:27,260 --> 00:17:28,650 We could equivalently put book or 529 00:17:28,740 --> 00:17:29,940 magazine in here because we 530 00:17:30,040 --> 00:17:30,910 know they're all books or magazines, 531 00:17:31,400 --> 00:17:32,230 and we'll do that in a moment, 532 00:17:32,430 --> 00:17:34,590 but for now let's just focus on running the query. 533 00:17:35,680 --> 00:17:37,560 So we execute the query and we find two answers. 534 00:17:37,970 --> 00:17:39,150 We find "Hector And Jeff's Database 535 00:17:39,640 --> 00:17:40,570 Hints," which is a book because 536 00:17:40,810 --> 00:17:41,820 we had a magazine of the 537 00:17:41,970 --> 00:17:43,430 same title and we find 538 00:17:43,770 --> 00:17:44,980 "National Geographic," which is 539 00:17:45,070 --> 00:17:47,400 a magazine because there's another magazine of the same title. 540 00:17:48,700 --> 00:17:49,850 So actually this query was somewhat incomplete. 541 00:17:50,750 --> 00:17:51,220 And that was our fault. 542 00:17:51,850 --> 00:17:53,250 The way we wrote the query we 543 00:17:53,370 --> 00:17:54,470 said that we want to return 544 00:17:54,960 --> 00:17:57,330 book or magazine elements when a later one has the same title. 545 00:17:57,740 --> 00:17:59,020 So that doesn't actually return 546 00:17:59,520 --> 00:18:00,420 all of the ones with non-unique 547 00:18:00,980 --> 00:18:02,270 titles, it only returns the 548 00:18:02,660 --> 00:18:04,850 first instance of each one with a non unique title. 549 00:18:06,170 --> 00:18:07,950 Let's modify the query to do the right thing. 550 00:18:08,860 --> 00:18:09,630 What we need to do is not 551 00:18:09,860 --> 00:18:10,660 only check whether the title 552 00:18:11,030 --> 00:18:12,520 equals the following sibling title 553 00:18:12,900 --> 00:18:14,240 of some book or magazine, but 554 00:18:14,340 --> 00:18:16,190 whether it might also equal a proceeding one. 555 00:18:16,690 --> 00:18:18,070 So we add title equals the 556 00:18:18,150 --> 00:18:19,540 same construct using the proceeding 557 00:18:20,150 --> 00:18:22,690 sibling axis slash, slash, 558 00:18:23,040 --> 00:18:25,210 title... Here we 559 00:18:25,340 --> 00:18:26,390 go, and now when we 560 00:18:26,540 --> 00:18:27,810 run the query, we see 561 00:18:27,980 --> 00:18:29,080 that we get Hector and Jeff's Database 562 00:18:29,580 --> 00:18:31,140 Hints and National Geographic, but 563 00:18:31,380 --> 00:18:32,600 we also get another instance 564 00:18:32,960 --> 00:18:34,330 of National Geographic and another 565 00:18:34,690 --> 00:18:36,710 instance of Hector and Jeff's Database Hints. 566 00:18:36,960 --> 00:18:38,580 So now we have the correct answer. 567 00:18:38,890 --> 00:18:39,920 We don't only get the first 568 00:18:40,210 --> 00:18:42,560 instance of duplicated titles, but we get both of them. 569 00:18:43,610 --> 00:18:45,190 Now to show the use of 570 00:18:45,310 --> 00:18:46,560 the star, we were matching 571 00:18:47,100 --> 00:18:48,970 any book or magazine as the following sibling. 572 00:18:49,730 --> 00:18:50,590 What if all we were interested 573 00:18:51,060 --> 00:18:53,030 in is cases where there's 574 00:18:53,400 --> 00:18:54,810 a book that has the 575 00:18:54,880 --> 00:18:56,140 same title, but not a 576 00:18:56,450 --> 00:18:57,950 magazine, and we can do the same thing here. 577 00:18:58,850 --> 00:19:01,240 In that case, we shouldn't get "National Geographic" anymore. 578 00:19:01,940 --> 00:19:03,220 Let's run the query and indeed 579 00:19:03,710 --> 00:19:04,860 all we get in fact, is 580 00:19:05,010 --> 00:19:06,370 "Hector and Jeff's Database Hints," as 581 00:19:06,510 --> 00:19:08,140 a magazine because that was 582 00:19:08,770 --> 00:19:10,350 the only instance where there 583 00:19:10,470 --> 00:19:12,000 was an actual book that had 584 00:19:12,170 --> 00:19:13,590 the same title as opposed to 585 00:19:13,760 --> 00:19:15,390 matching books or magazines with the star. 586 00:19:16,930 --> 00:19:18,080 Don't take a look at this query yet. 587 00:19:18,370 --> 00:19:19,340 Let me explain what I'm doing 588 00:19:19,610 --> 00:19:21,830 before you try to untangle the syntax to do it. 589 00:19:21,940 --> 00:19:24,380 As I mentioned earlier, Xpath 590 00:19:24,690 --> 00:19:26,890 revolves around implicit existential quantification. 591 00:19:28,060 --> 00:19:29,120 So when we are looking for 592 00:19:29,270 --> 00:19:30,470 example, for an author whose 593 00:19:30,670 --> 00:19:32,750 name is Ullman, implicitly we will 594 00:19:33,030 --> 00:19:34,310 match the path if any 595 00:19:34,640 --> 00:19:36,730 author has the last name Ullman. 596 00:19:37,050 --> 00:19:39,080 And in general, most of 597 00:19:39,500 --> 00:19:41,290 XPath revolves around matching 598 00:19:41,700 --> 00:19:43,000 sets of values and then 599 00:19:43,550 --> 00:19:46,050 returning things if any element of that set matches the condition. 600 00:19:46,460 --> 00:19:47,840 What if we want to 601 00:19:47,930 --> 00:19:50,400 do universal quantification, in other words, for all. 602 00:19:51,170 --> 00:19:54,020 That turns out to be more complicated, but we can do it in a tricky fashion. 603 00:19:55,060 --> 00:19:56,320 So, what I'd like 604 00:19:56,350 --> 00:19:57,500 to do with this query is we're 605 00:19:57,700 --> 00:19:59,310 going to find books where every 606 00:19:59,850 --> 00:20:01,000 author's first name includes 607 00:20:01,600 --> 00:20:02,740 J. If we wrote 608 00:20:03,050 --> 00:20:04,260 it in the fashion that we 609 00:20:04,350 --> 00:20:05,410 might be tempted to, or we 610 00:20:05,550 --> 00:20:08,060 just say book author/author first 611 00:20:08,280 --> 00:20:10,090 name includes J, then we'll 612 00:20:10,220 --> 00:20:11,790 get books where some authors 613 00:20:12,170 --> 00:20:13,780 first name contains J. To 614 00:20:13,910 --> 00:20:15,180 get books where all author's 615 00:20:15,610 --> 00:20:16,920 first names contains J is 616 00:20:17,110 --> 00:20:18,420 more difficult and the way 617 00:20:18,690 --> 00:20:20,080 we're going to do it is, it's 618 00:20:20,230 --> 00:20:21,430 kind of a kluge, we're 619 00:20:21,740 --> 00:20:23,280 going to use the built in function count. 620 00:20:24,310 --> 00:20:25,590 So here's what we're doing in this query. 621 00:20:26,320 --> 00:20:27,820 We're finding all books where 622 00:20:27,960 --> 00:20:29,960 the number of authors whose 623 00:20:30,220 --> 00:20:32,240 first name includes J is 624 00:20:32,480 --> 00:20:33,300 the same as the number 625 00:20:33,630 --> 00:20:34,560 of authors of the book without 626 00:20:34,920 --> 00:20:36,360 a condition, okay? 627 00:20:36,790 --> 00:20:38,060 So, specifically under "book" we 628 00:20:38,720 --> 00:20:40,920 count the number of 629 00:20:41,120 --> 00:20:42,550 matches of an author's author 630 00:20:43,010 --> 00:20:44,230 sub-element, where the built-in function, 631 00:20:44,980 --> 00:20:46,860 the built-in predicate contains, is true, 632 00:20:47,520 --> 00:20:48,880 where the first name contains J. 633 00:20:49,880 --> 00:20:50,980 And so we are counting the number 634 00:20:51,130 --> 00:20:52,110 of authors whose first name contains 635 00:20:52,550 --> 00:20:53,630 J and we're setting that 636 00:20:53,880 --> 00:20:56,080 equal to the count of the first name sub-elements. 637 00:20:57,240 --> 00:20:58,170 We'll run the query and we 638 00:20:58,570 --> 00:21:00,040 will find, indeed, that there 639 00:21:00,340 --> 00:21:01,870 are two books where all of 640 00:21:01,950 --> 00:21:03,530 the authors' first name includes J. 641 00:21:05,180 --> 00:21:06,190 We can use a related trick 642 00:21:06,780 --> 00:21:07,670 to write the query we tried 643 00:21:08,010 --> 00:21:09,700 to write earlier but failed to 644 00:21:09,800 --> 00:21:12,340 find books where Ullman is an author and Widom is not an author. 645 00:21:13,090 --> 00:21:14,580 So with the implicit existential, 646 00:21:15,430 --> 00:21:16,320 what happened before is that 647 00:21:16,450 --> 00:21:17,690 we found books where there was 648 00:21:17,900 --> 00:21:18,960 an author whose name was 649 00:21:19,060 --> 00:21:20,450 Ullman and then there was 650 00:21:20,730 --> 00:21:22,620 an author whose last name was not Widom. 651 00:21:23,470 --> 00:21:24,730 And of course, we still got everything back. 652 00:21:25,050 --> 00:21:26,440 What we want to find is 653 00:21:26,680 --> 00:21:27,570 books where there's a last 654 00:21:27,910 --> 00:21:29,210 name that's Ullman and where none 655 00:21:29,580 --> 00:21:31,470 of the authors have the last name of Widom. 656 00:21:31,990 --> 00:21:33,330 That's effectively, again, a universal 657 00:21:33,940 --> 00:21:35,090 quantification for all. 658 00:21:35,590 --> 00:21:37,840 For all of the authors, their last name is not Widom. 659 00:21:38,520 --> 00:21:39,500 Since we don't have a for 660 00:21:39,600 --> 00:21:42,510 all construct in XPath, we're again going to use the count trick. 661 00:21:43,120 --> 00:21:44,070 So in this query, we're looking 662 00:21:44,360 --> 00:21:45,710 for books where one of 663 00:21:45,800 --> 00:21:46,950 the authors' last name is Ullman 664 00:21:47,520 --> 00:21:49,150 and the number of authors using, 665 00:21:49,540 --> 00:21:50,670 count again, the number of 666 00:21:50,740 --> 00:21:52,500 authors whose last name is Widom is zero. 667 00:21:53,130 --> 00:21:54,260 So now we've expressed that 668 00:21:54,450 --> 00:21:56,570 query, we run it, and we get the correct answer. 669 00:21:58,020 --> 00:21:59,710 That concludes our demonstration of XPath. 670 00:22:00,640 --> 00:22:01,970 We've shown a large number of 671 00:22:02,310 --> 00:22:04,380 constructs and we've written some fairly complicated queries. 672 00:22:05,150 --> 00:22:08,070 On the other hand, we certainly have not covered the entire XPath language. 673 00:22:08,790 --> 00:22:09,640 If you're interested in our many 674 00:22:09,940 --> 00:22:11,590 online materials, we'll also provide 675 00:22:11,960 --> 00:22:14,640 a data and we encourage you to experiment on your own.