 So thank you for coming. This is leveling up with Ecto. When I first started thinking about this talk a long time ago, I sort of imagined it being a lot more workshoppy and live codey, and as I started preparing it, I started to feel like the stuff that was going to be the most useful was a little more conceptual. So it's going to be a little less workshoppy than I imagined, but I didn't want to drop that component completely. So up on the screen there is a link to a GitHub repo that I set up, and it's just a simple Elixir app that sets up Ecto and sets up some sample data that I'll be working with in the talk. So if you do want to try some stuff out as we go, go ahead and give it a shot. The slides might be going by kind of fast, so there's a link on the readme to the slides so you could pull that up too and copy and paste if you need to, or refer to it later or whatever you like. So what do I mean by leveling up? Well, when we first start out with a new platform or a technology or a tool, we're a little bit awkward at first. We tend to stumble around a little bit. We lose our balance. We're not sure where we're going. Things don't often go very well, and in our desperation to get something, anything working, we become practitioners of SODD. That's Stack Overflow Driven Development. You probably know how this goes. You have a problem. You Google it. Something comes up on Stack Overflow, copy, paste, tweak a little bit. It works. Hopefully, you move on with your life. And that's kind of how it goes for a while, but over time, you start to get a sense of how things work. You get some experience trying the tool in different contexts. You learn from other people, and pretty soon you're having to Google a lot less, and it's not so much that you have the whole API memorized. It's just that you kind of understand the lay of the land, and you know where you're going, and you get a lot less of this and a lot more of this. So my goal for this talk is try to accelerate that process for you with Ecto. I'm relatively new. I started working with Elixir and Ecto around the beginning of the year, and I was stumbling around quite a bit, but I really wanted to get my head around it. So I took more of a deep dive, and what I'm hoping to do is share some of the things that I learned, and hoping that it'll make things go a little bit faster for you. So if you're relatively new to Ecto, if you've experimented with it a little bit and just kind of want to get your head around a little bit more, this is the place to be. If you are a complete newcomer to Ecto, you've never seen a line of Ecto in your life, I'll be honest with you, you probably won't understand every single line of code you'll see in the presentation, but hopefully you'll get the gist, and certainly the foundational stuff that we're going to talk about will serve you as you keep going. Now if you're an Ecto expert, if you're the sort of person that provides the examples that practitioners of SODD copy and paste into their code base, there may not be too much in here that will surprise you, I'll be honest again, but if you're not familiar with the changes in Ecto 2, if you're a diehard Ecto 1 person, then there probably will be a few surprises. So that's the context. So let's talk about what is Ecto. So this is my very biased and completely unofficial definition of Ecto, but I think of it as a tool set for interacting with external data sources. And I chose these words kind of carefully. It's a tool set because there isn't just one way to use Ecto. It's got lots of different pieces to it. They harmonize beautifully and work together beautifully, but you don't necessarily have to use all of them. You can use pieces depending on what you need to do. And I say external data sources because although Ecto is best known as a tool for interfacing with relational and in some cases no-SQL databases, it can do much more. And anytime you're interfacing with external data in any way, might be files on your local file system. It might be an external API or anything like that. There's things in Ecto that can help you. It's not just for databases anymore. Ecto's a pretty big library. There's lots of pieces to it. So if you've tried to wade into the code of the documentation, it can be a little disorienting at first. But to my mind, there are kind of four main pillars that are the foundation of the whole thing. And these are good entry points. And these are repo, query, schema, and change set. Technically, there's a fifth, which is migrations. But migrations are covered pretty well in the docs, and there's not too much more subtlety around them. So I'm going to just pretend they don't exist for today. But migrations are the means of getting data into your database, and they're used in other systems as well. So let's start with repo. So repo is the part of Ecto that actually talks to the data source. And in fact, it's the only part that talks to the data source. And this is good to know, because if you're trying to manage where in your code side effects happen, it's pretty clear. You can mess around with schemas, change sets, and queries all you want. Nothing is going over the wire until you actually use one of the functions in repo. And so repo has functions that you'd expect to see. Get, insert, update, delete, transaction, the usual suspects. This isn't an exhaustive list, of course, but this is the kind of thing you find. Typically, you don't call the functions in Ecto repo directly. Instead, you create your own module in your app. That's namespace to your app. Typically, by convention, we call it repo. You could call it something else if you really wanted to, but I don't recommend it. Repo's going to be easier. And then there's a call to use Ecto repo, which pulls in the functions, but it is your own local module. Then somewhere in your configuration, you tell Ecto how to take this module and connect it to some data source. In this case, it's Postgres, and we provide all the information that needs to connect. And then from there in your code, you usually alias it. And then at that point, you just start calling repo. And as though you're talking to Ecto repo directly, but it's your module. Now, this is kind of important to keep in mind, because there are two nice consequences with this arrangement. One is that it's super easy to connect to another data source if you need to in your app. For example, if you had some legacy database that you needed to talk to, you'd simply create another module, give it a different name, legacy repo in this case, and then configure it with whatever information you needed to hook up to it. And at any point in your code, if you need to talk to the new database, you just talk to repo. If you need to talk to the old database, you just talk to legacy repo. Easy to do. Another consequence is that it's super easy to add your own custom repo functions. So let's say we were making an application that helped us manage a music collection. And we wanted to do a lot of counts. So we'd want to find out how many artists we have, how many albums we have, how many tracks. If we're doing this sort of thing a lot, we can create a little shortcut for ourselves. We can just add a function into our repo called count that takes a table name. And then we forward that on to repo's aggregate function. And then in our code anywhere we need to count, we just have to do that nice and easy. So if you find things you're doing things sort of repetitiously that interface with repo, you might think about putting your own function. Now there's some functions in repo that allow you to work pretty directly with the data in a low level way without even having to get into schemas. And they're easy to spot because they all end with all. Insert all, update all, delete all, and just plain all for queries. So for example, if you just want to throw some data into your database, you can just do a simple insert all. This call here will talk to the artist table. It will insert one record, and the record just has one field called name, and it'll set it to John Coltrane. And the thing you get back, we didn't ask for it to send us anything back, so we get a tuple with the number of records affected, and then nil, because we didn't ask for anything. If we wanted to update all those records, we could do it that way. Again, we're just using the table name as a string, and then we have a set keyword. We tell it which column we want to update, and if we want to delete them all, we can do it just like that. But what about queries, you might ask? Well, if you want to go super low level and just use SQL, you can. There's ectoadaptors.sql query, and you can throw a SQL query directly into it. I don't recommend it. The response is not super friendly, and if you don't enjoy filtering through all those tuples to create timestamps, this might not be the way for you to go. The recommended way is to use the query module. So, ecto implements a DSL for generating queries, relies on macros pretty heavily, and this DSL feels a lot like SQL, and I do mean that in a good way. What I mean is that if you're familiar with SQL, you're gonna find ecto's query syntax pretty easy to pick up, and if you don't know SQL, you're gonna learn it without knowing it, and pretty soon when you have to go spelunking through your app logs to figure what went wrong, those SQL statements that are flying by aren't gonna look like gibberish to you. You're gonna have a good sense of what they mean. So here's an example SQL query. This goes to our tracks table, and it does a join on the albums table, and it pulls back the track ID, the track title, and the album title, and we're filtering just on those tracks that have a duration longer than 600 seconds. So we're looking for the long self-indulgent longer than 10 minute tracks in our collection. This is what the query looks like in SQL. This is what the query looks like in ecto. It's pretty close. All of the SQL keywords kind of become keywords in the ecto syntax. The only thing that's unusual is the query start with from, that's almost always how the queries begin. Once you create that query, you can just pass it off to repo all, and you get your results back. Now you may at some point have to deal with pesky user data, data coming from somewhere else. So if you have to interpolate into the query, you need to use that little carrot symbol. So if you see our where clause now, that min duration has a little carrot in front of it, which is us telling ecto, please evaluate this variable and stuff it in there for us. If you didn't have that carrot, ecto would get confused and think min duration was part of the keyword syntax. Now if you do that, and we're not at schemas yet, you have to think a little bit about types. So if that value might be coming in as a string, rather than an int, you're gonna need to let ecto know. So here we've modified the where clause to put a type macro around it to make sure it's forced down to integer. Now that's not very much fun, but there are ways around that and we'll get to that later. Now a really useful thing about queries is that they are composable, which means you can create fragments of queries that can be chained one onto the other so you can further refine the query down the line. So if you have some particular part of a query that's tricky, if you're drawing a lot of tables and you need to reuse it, you can. You just have to store it and then work with it again later. This allows you to do what some people in other ORMs would call scopes. You know who you are. So here's an example query. We'll talk to the Albums table and we're gonna do a join on artists, mapping the artist ID back to, sorry, the album ID back to the artist ID. And we're specifically looking for Miles Davis. So effectively this query gives us all the albums by Miles Davis. So let's call it that, albums by Miles. Now this is actually not a valid query at this point. If you tried to pass this off to ecto, you'd get a big error. And the reason is, because we haven't told it what to select. And if you don't have schemas, ecto won't know. There's no such thing as select star. And certainly if there were, you'd have to be explicit about it because that's elixir and that's what we do. But you can build on this query somewhere else. So now we're creating a new query and we're saying from A in, instead of giving a table name, we give the query that we created above. And now we can kind of interact with it in exactly the same way. Here we're just gonna get the title. So if we ran it, we would get the album titles. Now we could do something fancier. Take the same query and now we're gonna add a join onto it, which takes us into our tracks table and now we're gonna pull back the album tracks. And this would just give us all the tracks that we have in our dataset. So this ability to put queries together and slice and dice them and refine them is pretty handy. If you find yourself repeating parts of queries, it's a good idea just to stuff them all in a function and then you can call it back later when you need to somewhere else. So now that we have this query syntax, we can go back to these all functions that we were looking at earlier and do some fancier stuff. So here's a query that pulls back all the artists whose name is Art Tatum, but it looks like it's misspelled. So we're gonna do an update and correct the spelling. Here's another one that goes back to get our self-indulgent tracks and we're going to delete them because who has time for that? So the story so far, we're only a few minutes in and we can already do a lot. With repo, we can talk to the database. With query, we can construct just about any query we want to and we can insert, we can update, we can delete and we can read. So we've got the full crud spectrum. It's all good. There are a couple little hitches. First of all is that we always have to be explicit about our selects. And if you were doing something with very large tables where you're getting back lots and lots of columns at a time, this would get tedious in our hurry. The other thing is that we have to be careful about making sure we're correctly casting the types of data that we're forcing down. Like when we saw that min duration coming in as a string. It'd be nice if we had a shortcut to help us deal with these things we might have to do over and over again. And that's where schemas come into play. So again, my highly biased unofficial definition, schemas define a reusable shape that we can use to move data in and out of our data source. So if you find you're selecting the same kinds of thing over and over again, it's a good chance that you're gonna want a schema. So schemas again use a DSL. This is a typical setup. We're gonna create a track module and we're gonna pull in schema by using the use statement. And this gives us the macros that you see below. We call schema, give the name of the database table that we're mapping to and then define the fields one by one and their types. And timestamps gives us this nice little shortcut of two columns called inserted at and updated at which depending on which repo call you go through will update automatically for you. Now this saves us some typing. So here's, we go back to that query we did earlier where we're looking for the long tracks. Here's what it'll look like without schemas. You see we have to do that nasty cast on min duration and we've gotta manually select out all the fields that we want. With schemas, it looks like this. We simply have to say from T and track and because we're referring to a module that has a schema, we don't have to provide the select statement. Ecto's just gonna grab all of those fields that we've defined. And we don't have to manually cast it because Ecto's gonna do it for us. It knows that duration is an integer so it's gonna do its best with whatever comes in as min duration. String, atom number will probably work fine. If it's a list it'll blow up of course but it's gonna do its best. Now, here's the thing about schemas. Wonderful time saver here but you don't always have to use them. Just because they're defined in your project, in some situations it might be more work than you need. For example, let's say we just wanted to get all the names of the artists and the number of albums for each person. That type of query doesn't fit neatly on top of a schema. An album count is not something we've defined anywhere and it's not like we want the whole artist record or the whole album record. We really just want a simple map, name and account. And so in that case you can construct the query so we do a join, we do a group by but you can still manually supply the select if you want to. And then when you run this you'll just get the values that you want and not anything else. So schemas are great but you don't need to go overboard with them. Especially when you're in reporting types of situations where you're grabbing data from lots of different fields. Let them go and do your own selects and there'll be a lot less data to come through. Now schemas are a huge help when it comes to dealing with associations. So associations you may know are what allows us to create relationships between tables. So we've modified our artist schema to say that it has many albums. And we refer to the name of the association we're gonna call it albums and the struct that has the schema that we need which is musicdv.album. And this assumes of course that we've set up our migration so that there is an album or sorry an artist ID on the album table. There's four different kinds of associations you can have. Has many, has one, belongs to, belongs to is what sits on the other side of a has many or has one. And as a Vecto2 you can do many to many which is pretty nice. The album schema uses a bunch of these. It belongs to artist. It has many tracks. And it's got a many, many to relationship with genres. Genres being the type of music. Jazz, funk, rock, speed polka, whatever you want. Now if you wanna see the association records now that we've got our schema set in place for album we can use repo.get. We pass in the struct name which is album and an ID. This is gonna give us album number ID. And then we simply use dot notation to get album dot tracks. And when we do this we get the dreaded Ecto association not loaded. Who's seen this one? Yeah, kinda gives you this feeling. Okay, why did this happen? So here's the elixir thing at work. Ecto will not go on a fetch associated records unless you specifically ask it to. Now when you're working in IEX and you're just trying to look at your data and you click on an association and you get association not loaded this seems like a big pain in the butt but this is a good thing. You really don't want your database interface running around and grabbing whole bunches of records without your knowledge. And if you don't believe me after the talk go and Google N plus one queries. I won't take the time to get into it now but there's probably people in this room that can tell you some painful stories. There's guy nodding right there. Can tell you some painful stories about what can happen. So the takeaway is when you're dealing with associations and you're running your queries you gotta be super specific about which ones you want. So here's how you fix it. One way to do it is to add preload keyword to your query. So you just set up your query like normal but then you say preload and specify the association that you want. In this particular case it's albums. We're gonna load the artist named Bobby Hutcherson and we're gonna get the albums back as well. And you can nest these if you've got associations on top of associations. So in this case we're not only gonna get the albums by Bobby Hutcherson but we're gonna get the tracks. And these are groups together because tracks are not directly related to artist but albums have a has many relationship to tracks. So we do nesting that way. If tracks had a has many to something else we do another keyword list and you can just go turtles all the way down. Another way to fix it is you can use preload after the fact. This will save you when you're typing around in IEX and you hit that association not loaded. You can fetch the record and then once you have the record you can pass it to repo preload and then that'll get the associations for you. So if you're in IEX you don't need to redo the whole query over again. You can just slap yourself on the forehead like you normally do and pass the record into preload and you're good to go. Now another great thing about this scheme and the association support in Ecto is that inserting new records when they have complex nesting is now a total breeze. So imagine we wanted to insert an artist one of his albums and all of the tracks all in one shot. You can do it like this. So we call repo.insert and then we set up the artist struct we set the name and now we have the albums association. And so we set that up since it has money as a list and we create an album struct within that list create an title for that album then set up a tracks association within there and then four more structs underneath that and this will absolutely do the right thing in terms of figuring out which foreign keys go where. Now please just take a moment and bask in the beauty of that and think about how, yes it does deserve a pause. Jose and Eric and Mio, thank you and think about how easy it's gonna be to set up your development database next time. By contrast, here's what that data structure looks like in JSON. This is just pure JSON and that's executable elixir code. You wanna talk about clean syntax. It doesn't get any lighter than that. So this is a fantastic way to dump a whole bunch of data into your database at once. Provided that data is valid. Now it's okay to do that in development with data that you know about. You certainly don't wanna try that in production with data that comes from those pesky users out there because who knows what they're gonna put in there. If you're throwing user input into the database you wanna make sure that it's the data you expect. You wanna make sure it's in the format you expect and that it's all valid and that's where change sets can help you out. So change sets allow you to filter, cast and validate the values that you wanna send to your data source. Also helps you track errors. So here's a typical usage. Somewhere in your code base you import change set and then we've got these params that came from somewhere. We don't know where but we've got a map of title and index. And then we set up the change set by using an empty struct that's mapped to one of our models. Oh, I said model, I'm not supposed to say model. To one of our modules, it's so close to model that we set up as a schema. And then we call the cast function and cast is provided by change set. And this is gonna do two things. First of all, it's gonna filter the params. That second argument to cast defines, it lets us specify which fields we want. So we're saying we just want title and index. If those params had anything else in there they would just get thrown on the floor and ignored. The second thing that's gonna happen is that they're gonna be cast into the correct type. So if you see that index value up there as a string this cast call is gonna convert it into an integer. And what we get at the end of cast is another change set. And change set can be passed into the validate functions that the change set module provides. And so that first one we can make sure that title and index were provided. That's what validate required gives us. And we can make sure that we got a number greater than zero for index. Now once we have this change set we can just kinda see if it works. So we'll pass it into repo insert and see what we get back. If we get a tuple with okay and the track we know it worked. And if we get error and the change set given back to us then we know it didn't work. But at this point change set has been populated with an error struct that tells us exactly what the errors were. So if for example we had left off title that's what we would see in change set errors. It would tell us what the field is and what the problem is. Change set ships with a whole bunch of validations that are pretty useful. You can look at strings, verify format of strings, check on numbers, check booleans, all kinds of things. But as you're searching through the documentation looking for the validations that you need make sure you're also looking for the constraints. And these are functions that end in the word constraint. Constraints work exactly like validations in that you can pass them through just as we did with the validates and they will populate change set errors if there are problems. But constraints are special because they actually verify it at the database level, not necessarily on this side of the wire. So take an example, the unique constraint. So sometimes in your data you need to have columns that are guaranteed to have unique values. Like in a user table the username you wanna absolutely be unique. Now you can sort of try to enforce that on your side but really the only way to be sure is to set up a unique index on that column when you first set up the database. So that way the database will absolutely enforce it and make sure you don't get duplicates. So if you do something like this where you take the change set and you apply the unique constraint to it, provided everything else is okay with the change set Ecto will actually try to execute this query. But it will check the error that will come back from the database if the uniqueness was violated. Catch that and then put it into your errors as if it was a regular validation error. So validation errors and constraint errors are gonna look exactly the same in that error structs. It's just that anything that's constrained you know came from the database and you know is for real. This seems small but it's kind of big because the way you used to have to do this in other systems is you kind of felt like an idiot because you would get the username from the user and you'd run to the database real fast and say is this available? Yeah, it's available. Okay, let's give it a try. You shove it off to the database but you know the index is there so you might get an exception. So an exception comes back and that either means that the server blew up and the database is completely offline or the username is simply not available. So you had to figure out looking at the exception which was which. Ecto does all this for you behind the scenes. So you get the guarantees that the constraint is good without having to muck around through a bunch of exceptions. That's pretty nice. So validations on their own are pretty handy. You could imagine how they might be useful outside of a database. Let's say you were talking to some sort of third party API and you were collecting some data. You might want to make sure the data is good before you send it off. Validations are handy for that. Can we use them? Yes, you can. Because you can have schema-less change sets in Ecto. Yes, you heard me correct. You can define a change set without a schema it just kind of looks like this. Probably not correct to say schema-less. It's more like schema on the fly. You can create a map that defines the parameter names and the types that you want to enforce. So in this case, we're gonna say that our API is expecting a band name and an album name and they're both strings. Here's the input we're gonna use. The band name is modern jazz quartet and we're gonna drop off the album name because the user was feeling obtuse that day. And then you set up a change set just as you normally would. But instead of that empty module, I said it right this time, that we used before, we pass in a tuple. First is just an empty map and then the second are the types that we defined earlier. So we're telling Ecto, this is the structure we wanna enforce. From there, we can just treat it the same way. We call cast on the params and we can run our validations. And at the end of it, we can ask change set if it's valid. In this case, it's gonna say false. And if it's false, we can look at the errors and it looks exactly the same as if we were using a schema. So if you can think of a place where you'd like to use those validation functions and it's not in the context of database, you can. You can just use change set on its own. Let's talk for a little bit about Phoenix integration. So if you've done any work with Phoenix, you've probably discovered that Ecto and Phoenix play pretty well together. That's because if you look in your dependencies, you'll see that there's another package called Phoenix Ecto, which kind of provides the glue. Phoenix Ecto just adds some definitions for behaviors that Phoenix is expecting and puts those onto some of the modules from Ecto. And this is why, for example, when you're setting up a form in Phoenix and you have form for, you can pass in a change set and you can use these nice shortcuts for setting up the form fields. And you don't have to do all that mucking around with names and values yourself. It's pretty handy. So this works very, very nicely, but there's one drawback, which is that it implies that there's kind of a one-to-one correspondence between our schemas and therefore our database tables and our user interface. So this works great if our form looks exactly like our database, but, and that works some of the time, but that may not always be what you wanna do. Let's take the track example. Now let's say we wanted to create a form for user to input tracks. The duration is kind of tricky because in the database we're storing it as an integer of the number of seconds. That's not a very friendly interface for the user to do. Normally when a user sees duration it's expressed as a string, minutes and seconds. If we want to get that as seconds, we have to ask the user to do some math. Now we could create a separate form that would do this, but then we lose all that nice convenience of just passing a change set directly to form for and being able to construct all the fields. Well, change sets and schemas have a couple other tricks up their sleeve to get around this. One of them is virtual fields. So one way we could solve this is add another field to our schema called duration string. And this is gonna be the duration expressed as a string. And you'll notice we've added a keyword called virtual true on the end. So this is our little cue to Ecto saying we're not gonna try to persist this. This is just something we're using for our own purpose. Ignore this, move on. Having done that, over in Phoenix land when we pass the change set to form for we can use duration string as a first class citizen when we're setting up our forms. It'll work just the same as all the others. Now back in our controller or the module that the controller uses according to what's coming in 1.3. I'll be agnostic about that. We set up our change that is normal. We set up the track. We cast it. We do the validations. But now we're gonna do another step, which is we're gonna convert the duration string over to an integer. And we'll do a separate function for this. I didn't pass the change set there, that's all right. So we'll take the change set in the separate function and we'll call get field, which is a change set function to get the duration string out of the change set. This is the string that the user provided. And then we'll do some sort of magic conversion to take that string and turn it into an integer representing the number of seconds. So how you take something that says four colon 33 and turn it into a number of seconds, that's an exercise left to the reader. Now once we have that integer, we can put it back into the change set by calling put change. And now we're using our real duration field, the one the database actually knows about, and supplying the integer. And that's been back from there. We can go ahead and validate it to make sure that we got a number that's graded from zero. So this is a subtle shift, but it points to something kind of important, which is that we now have a way to decouple our UX from our database tables without sacrificing the convenience that comes from passing change sets around. If the form that you wanna present the user looks exactly like the database table, no problem, you're good to go. But if you don't want it to for whatever reason, in this particular case, there's a way around it. And you can go even further. And here's another big secret, schemas don't actually have to have a database table associated with them. There is a feature in schema where you can define what's called an embedded schema. And this is sort of like an entire schema full of virtual fields, that you could then pass into a form, collect the input, validate it, and then convert to the real schema that you wanna use. It sounds insane, but it's true. I don't have time to walk through an example right now. So I would refer you to this blog post that Jose wrote called Ectos Insert All in Schemeless Queries. He's got a really nice example in there where he demonstrates how a form you might use to sign up a user would be different than a form you'd use to edit the user content, but how you can map that back to the same schema in the back end. It's super handy and I strongly recommend reading that blog post because it'll just open up your mind a little bit about what is possible with keeping the convenience of the Phoenix Ecto integration, but providing a more flexible and more dynamic UI. Okay, that was a lot of stuff. We've gone through a lot of things. It's been a little random. You can do this, you can do this, you can do this, you can do this. What does it all mean? So if I could give you one big idea to take away with you as you go into lunch and contemplate, it's this flexibility. There is no one right way to use Ecto. It's got a lot of different pieces to it. It's a lot of different tools and you can use them in any combination that you want. You wanna do some validations without having to have a database behind you? Go ahead, you can use it. You wanna go super low level and not even bother with schemas? Fine, you can. It's kind of up to you and so you can think about what your use case is and then look around in the Ecto tool set to find the things that can help you. So to keep leveling up, I recommend you check out the GitHub repo. The source code is surprisingly readable and the documentation is very, very good. In particular, the top level modules have a very nice preamble to them, like the big four that I talked about that walk you through a lot of the nuances. So it's definitely worth checking out. So that's about all I have. My name is Darren Wilson. I work for a company called Infinite Red. We do web and mobile development using Elixir and React Native. So if you need help with either of those things, feel free to talk to me. I'm at Darren Wilson on Twitter and in most places on the internet. I also tend to lurk in the Ecto channel of Slack, of the Elixir Slack, I mean, and I troll the Elixir Forum too. So if you ever wanna talk about this stuff, if you have any questions, feel free to contact me anytime. Thank you for coming. I appreciate it.