MongoDB with Async Python Transcripts
Chapter: PyPI Beanie
Lecture: Finding Packages and Releases

Login or purchase this course to watch this video and the rest of the course contents.
0:00 Next up is searching for a package. We want to search for them in two ways. We want to be able to find them by giving a name
0:08 of a package, like show me stuff about FastAPI, as well as let's do a more unexpected type of query or search where we say, I want all the packages
0:18 that have a 7.0 release in one of their releases, just to explore digging into that nested embedded array,
0:27 that array of embedded objects that are the releases. So I'm going to put a little skeleton bit in here
0:33 just so that we don't have to type out all the boilerplate. So here's what we're going to do. As I said, two things,
0:38 give us the name of the package you want. So we can go get a package by name. I'm going to loop over those. If we have information about it,
0:46 what is its ID, when was it updated? How many releases does it have? Otherwise we'll say, there's nothing like that in our database.
0:55 And then I'll let you enter the string, something like 123 or 1.7.4 or whatever, and then we're going to figure out the major, minor,
1:03 and build parts because recall, in package, that's how we're storing those, right? In the embedded releases,
1:13 they each have a major, minor version and build, so we're going to need to have those pieces separately
1:18 so we can query it, and then we'll just have a function, you know, give us the package with all of these versions,
1:24 and we'll just print that out, right? I'm not sure how practical that is in terms of what you would really do with it, but it
1:30 is relevant for letting us dig into these nested objects. Well, let's start at the top.
1:37 We're going to have this function here, package by name, specify the name. And again, PyCharm takes a guess, but it's not very async friendly.
1:49 It's going to be a string and it's going to return either a package or nothing depending if it exists.
1:54 an optional package. Again, I know it could be package pipe or if you prefer that style,
2:04 not my thing, but certainly would be valid and mean the same thing. So then what do we
2:10 want to do? We need to do a query. We'll say package equals, we go to our package, we say
2:14 find one in this case, and we just specify via the type package dot what we're using
2:22 for our package, if we go back to the class, all is the name is really the true ID of the object so it's just going to be .id.
2:31 Then we want a quality, an exact match is name, and that's going to return our one object or none, and then we just return the package.
2:43 Sure you can inline the return, but I kind of like to have this in two lines sometimes
2:46 so I can set a breakpoint here if I really need to go have a look at it. So let's go and run this and see how this is working.
2:53 So I want to search F, not there, down here. F for find. Let's have FastAPI. Look at that. Found it.
3:04 FastAPI, last updated when I imported this with that many releases. And it doesn't matter what we put for this. I guess it may crash. 1.2.3.
3:13 Here we go. Zero with this because we're not running that query yet. Let's do another one. Let's do find, findantic, 1.2.3 just to make it happy.
3:25 Excellent, we found it with 95 releases. Let's do a final one where it doesn't exist.
3:31 Switchlang, that one I was telling you about that I wrote for adding switch to older Python, not going to be in the top 5,000 for sure.
3:39 So it won't be in the database, therefore, no package with ID switchlang found. Perfect. looks like that first part is working.
3:49 So the next one is going to be right, this one packages with version. Again, I'll let PyCharm add it over here, but we gotta upgrade it.
4:00 So we're gonna get a list of packages and we're gonna do our search on those three things there. Now, there's something that is not,
4:11 well, it's probably not obvious to most of you and it's really easy for this to catch you out when you're talking about a list of embedded objects.
4:19 If we had, like consider the user, they have a location. There's, it's super easy to just say, I wanna go find where the location is,
4:27 Portland, United States, that would be easy. When there's a collection of embedded objects, the way you write the query is the same,
4:35 but the meaning is not quite the same. So we'll do it like this. I'll write it in the wrong way first,
4:42 and then we'll do the correction using a document database style concept. So we go to the package and we could say find
4:55 and then our test that we want to put in here would be package.releases major ver = major
5:05 And then we'll just, let's just print out the ones that have this real quick, like this. All right, let's do our find again.
5:15 This time we'll just do the letter A because we don't care, but 1.0.0. Oh, whoops, it looks like I made an error in our function definition here.
5:25 What we're expecting back is just the number, not the packages themselves. So I'll just put this as an int.
5:33 And we'll return, instead of toList, we can just do a count. Rename that package count. Try one more time. Letter A doesn't exist, 1.0.0.
5:49 There are, well, a lot of them with 1.0. So never mind that there's this, what looks like an error.
5:55 Again, it's just kind of the typing trying to be too strong. So what you would expect here is that the way
6:01 you run this test is this going to be minor, this will be minor-ver, and this will be build,
6:12 and this will be build. So this seems completely reasonable in expectation. I want to go find
6:19 all the packages that have the major version set to major, the minor set to minor, and
6:23 the build version set to build. That's not what we're actually doing. And let me add
6:27 an S here so it doesn't conflict with the function name. So what we're really asking
6:32 MongoDB to say is I want to find a package that has a release with the major version.
6:39 It also has one of its packages, not necessarily the same package with a minor version. So
6:45 let's suppose there's one that has a release 111, and it has also a release 222. If we
6:53 asked for this to be 1 and that to be 2, MongoDB will look and say, ""Well, in this package, is there a release where the major is 1? Yes.
7:04 Is there a release where the minor is 2? Yes. But there's no release where there's a 1.2. That's not what we're asking here.
7:11 Okay, so this seems like what we want. It's not what we want. What we want is something similar to this, but what we need to do in terms of MongoDB
7:26 is something called an element query. An element query asks the question in the way you probably have been conceptualizing
7:33 it the whole time we've been working on this function. I want something where a single element, a single embedded element of the list has all
7:41 of these properties and applied to the embedded thing, not the overall document perspective.
7:48 So if we look over here on the MongoDB documentation, what we, it's not great that this is not wrapping, is it?
7:56 What we do is we say the field is actually equal to this operator where thing one, thing two, and so on, all the properties are set into a query.
8:08 So down here, we're going to use this operator that allows you to, from the ODM, to do finds on the array called element match. Okay?
8:17 going to have it import that directly from beanie.odm.operators.find.array import element match. So no, this is not going to work.
8:32 And then down here, the way we use it, instead of saying I want all of these, or I want kind
8:37 of an or applied to all the sub elements, I want to have the exact element where one of the items in that array matches.
8:44 So the way we do that in Beanie is we say element match, and we pass in the array here.
8:51 So package dot releases, and apply to that, we have the major version, minor version and
8:56 build version all have to match all three of those things, right? So we're trying to match an element of that list with this query.
9:05 Oh, a little bit non obvious. It's not very often you have to use element match, but it's so easy to think, ""Oh, this
9:14 is doing the query I want,"" when it's not, if that is an embedded list versus an embedded single object. I wanted to make sure we cover that in here.
9:23 Now let's see what we get this time around. Find letter A to skip. Okay, 1.0.0. I think last time we had 2,000.
9:31 About half of the packages matched that query. And now a little bit fewer. Let's go and see if we find the one that has 1.7.0. There we go.
9:48 Much much better. Let's see if we got any here that have a 7.0.0 release. 163.
9:57 So this is doing that query using element match to find the particular packages.
10:03 So don't let this really cool ability to say multiple constraints mean and fool you when you're talking about an embedded collection of objects.


Talk Python's Mastodon Michael Kennedy's Mastodon