Google I/O 2012 - Storing Data in Google Apps Script

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
DREW CSILLAG: Hi. Wow, that's loud. I'm Drew Csillag, and welcome to storing data in Google Apps Script. If everything goes to plan and everything works, we're going to be going on a tour of the four main storage systems available within Apps Script. Three of them you're probably already aware of if you've used Apps Script at all before. The other one, if you've been to any of the other Apps Script talks so far, they've totally leaked it. And so it won't be a big surprise. However, they were at least nice enough to plug this talk. A lot of useful scripts need storage in some form or other. And in most usages of script today, the storage is often related to the spreadsheet that you embedded the script in. Since we now have scripts in Google Drive and at script.google.com, it's important to know what storage systems you have available to you so you know which one is most appropriate for your use case. But before we start, it always helps to have a use case to help illustrate the concepts that I'm going to show you. Google has loved LEGOs since way back in the beginning, when Larry and Sergey decided that they needed to build a hard drive enclosure for the servers they were building. One of these originals is actually on display at Stanford. Since then, some offices, notably the New York City office, have LEGO areas, where we can experiment or play or blow off work or that kind of thing, to help us with our creativity. Or at least, that's what we're told. I myself have a number of bins of LEGOs at home. Actually, that's not entirely true. My daughter has a number of bins of LEGOs at home. She just lets me play with them, because she's cool. Anyway, there have been a number of times where we've been building something, and you run short of the number of LEGOs. You're, like, 12 bricks shy of being able to finish the thing that you wanted to build. And I thought that having an inventory of the bricks that I had would be really handy, so I can either design around what I have or at least know that I'm going to run into trouble before I'm done. So I decided to start cataloging the bricks. And I figured, hey, I work on the Apps Script team. It might not be a bad tool to use. So my plan was to start and keep things really simple because I don't want to have to do any more work than I have to. And so I decided to start working with the simplest of storage systems we have in Apps Script. And that's ScriptProperties. ScriptProperties is a simple, string-based key value store that's tied to your Apps Script project. Now, what exactly does that mean? This means that a given script project can only see the properties that belong to it. It cannot directly see any of the script properties that belong to any scripts that you might reference through the Script Libraries feature that we have. Because you might store things like JDBC connect strings or URLs or things that have authorization credentials, and you don't want those going all over the place. If you want to share them with the other ones, you can do that. It's just a manual affair. It's not going to be automatic. ScriptProperties works best when you're dealing with smallish amounts of data that the script needs that don't change terribly often. When I said that ScriptProperties is the simplest of all storage systems in Apps Script, I wasn't kidding. It's got exactly two methods, one to get the string value by key, and the other one to set it. One thing that's really nice about ScriptProperties is that if you don't want to write code-- because I prefer to write as little code as possible-- that if you don't want to write code and have to fill the store up with the values or build a UI to do it, you don't have to. In fact, it's completely reasonable to never call that second method, because in the File, Project Properties menu, there's a Project Properties tab, where you can edit, list, add key value pairs. And they're immediately accessible through the UI. You may notice that the API is called ScriptProperties, but the dialogue says Project Properties. This is mostly a historical artifact. Originally, because Apps Script didn't originally have the notion of a project, there was the spreadsheet and the script that you embedded in it. And that's all there was. Fast forward a couple of years now, and between being able to have multiple scripts, and now we've got HTML resources and other things, it evolved into the project as it has now. But we didn't change the name of the API, because we didn't want to break people's scripts unnecessarily for something that was basically a cosmetic change. For my LEGO brick inventory, I made my keys to be very simple, the shorter side, x, and the longer side, and the value was the number of bricks that I had. And it was really simple to find out for a given brick what I had. Basically, you get the property with two-by-four if I want two-by-four bricks, convert it to a number, and I'm done. Easy-peasy, lemon-squeezy. Well, ScriptProperties has some really nice advantages. Again, the API couldn't be simpler. There's two methods, one of which you may never have to use because of the built-in UI. Another great advantage is that, in order to use ScriptProperties, you need to do exactly bupkis to set this thing up. You just start using it. Lastly, and it's best when it's used for its intended purpose, for settings related to a script. That is, in other development environments, you might have a ,ini file or an XML configuration file or something like that. For Apps Script, ScriptProperties is how you do that. There are some things that ScriptProperties is not a particularly good choice for. If your data is going to change frequently, like more than once per second or so, then ScriptProperties is not your best bet. Also, as I said earlier, ScriptProperties is for smallish amounts of data. And this is more specifically what I meant by here. And so if your data's expected to grow to a decent size, you're going to run into that ceiling before very long. And now that we have Script as a web app, where we can use the user's credentials, if you're going to store user preferences in there, and your web app becomes extremely popular, you're more than likely to run into that size limitation, as well. And while in this LEGO example I'm kind of doing this, and we've seen a number of cases where you guys and even my boss has done this, ScriptProperties is not a generalized database. It doesn't provide any built-in search facilities. And from what we've seen when we've seen people do this, is that they have to write and roll up their own search and query facilities. And lastly, structured values don't do particularly well when you're using ScriptProperties. You can use JSON and CSV in the cases where you need it. And for one-off here and there, that works OK. But if you're going to be storing lots of data that way, it doesn't work especially well. So now that I had my bricks cataloged by size, I decided that I also wanted to track how many of each color I had. So I had a couple of choices of how I wanted to do this. First, I could try putting the color into the key, like this. But then it becomes extremely difficult to find out how many two-by-four blocks overall, because I have to know every color that I might possibly have. Alternatively, I could keep the color out of the key and make the value a CSV of the color in the count. But if I want to find out how many blue bricks I have, obviously I have to know all the brick sizes I ever have. Ultimately, what I wanted to do has now outgrown what ScriptProperties is really good for. And the next obvious one is spreadsheets. Spreadsheets can be used for all the kinds of things that ScriptProperties can, but handles things like structured data with ease. Like ScriptProperties, it has a high-functioning user interface, the spreadsheet editor. For doing data entry, you've got the spreadsheet editor, which allows you to do all sorts of things, like including formulas and such. But we also have Google Forms to do data entry, which is really, really handy. Also, if you need to copy or export and convert your data to different formats, with spreadsheets you can do that very, very easily. You can copy them, as well as import and export the data. For the LEGO brick inventory sheet, you can set it up something like this. And as you can see, we get the nice colors, we get fonts. I used formulas to compute the totals. And it was actually pretty simple. And if I get another color brick, it's just adding another column. If I get another brick size, it's just adding another row. It's really simple. However, this flexibility comes at a cost. The API for spreadsheets is necessarily more complicated. If we look at the sample, the function getBrickRow takes in a description of the brick, like two-by-four or one-by-two, and returns the row in the sheet for that brick type. So first, we get the active spreadsheet. And then I always get the sheet by name that I want, because who knows what people do with these things, because I've messed up my spreadsheet before. So if I get the one by name, I make sure I always get the right sheet in the spreadsheet. From there, I get a range of the second row, one column wide, down to the bottom of the sheet. And then we get the values. The data array that we get back is a list of rows within the range that we chose. The elements in that array are going to be single-element arrays, each containing one-by-two, two-by-three, that kind of stuff. Well, here, then we go and iterate over that array, looking for the brick that they asked for. And then when we find it, we return the index in the array that we found it at. You'll notice it says I+2. One of those two comes from the fact that we started from the second row in the spreadsheet, so we have to have one to compensate for that. The other thing is also arrays in JavaScript are zero-based. Spreadsheets, the rows are one-based. So we had to add an extra one to compensate for that. And if all else fails, we just say, sorry, too bad, so sad. We couldn't find what you're looking for. Now, for a moment, pretend that we have a getColorColumn that does basically the same thing, but returns the column for the color. With this, we can find out the number of bricks of the given type and color with the getBricksByColor function that we have here. Here, we get the sheet the same way we did before. And then we get a range of the row and the column from those two functions that we called with the color and the description. And we get the value from it. This allows us to answer questions very simply to say, how many blue bricks do I have total? Since we've got the total columns, we can use those column headings to find those rows and columns. So we can get all two-by-two bricks, all blue bricks, or all two-by-two blue bricks. So as you can see, spreadsheets have some of their own advantages compared to ScriptProperties. As I said, it has a built-in, high-functioning UI and handles structured data much more easily than you can in ScriptProperties. However, again, this comes at a cost. Specifically, if you're fetching lots of cell values from spreadsheets, you'll notice it's a whole lot slower than ScriptProperties is. When you're dealing with fetching data from a spreadsheet, you want to try to fetch a rectangle of it at a time. That'll be much faster. If you want to illustrate this to yourself, what you can do is you can write a script that fills in 10 cells one by one, and then one that writes 10 cells all at once. And you'll notice how-- you can watch it happen. And it's very much-- so the upshot of this is that you want to try to get and manipulate data in large chunks. There's also some limits that you need to be aware of. There's at most 50,000 rows in a sheet and at most 200 sheets in a spreadsheet. If it gets anywhere near this size, you'll probably experience lots of pain anyway. They do get slower to load as the more data you put in them. So in any case, for what we were doing so far, the spreadsheet handles what we're doing OK. But how do we expand it when we start adding things like LEGO plates? LEGO plates, they look like LEGO bricks, but they're about a third as thick. Well, in this case, we've literally added a third dimension to the data. And while we could put it in a spreadsheet, we've already had to write a bunch of search code to be able to do what we're doing so far. And I didn't want to have to write any more search code, because I've done enough. Well, what else could we do? Next one was JDBC. Using JDBC allows us to do all this quite nicely. And our search code just becomes comparatively brief SQL statements. We can now add the height attribute. And the table would look something like this. Since the plates are one third as thick, I figure I'll keep the standard height brick as now three. Well, how do we use this from Apps Script? Well, here's an example. To get anywhere, you need to get a database connection. And here, we're using a standard JDBC connect string to connect to a MySQL database. Apps Script supports MySQL, SQL Server, and Oracle. And since database connection strings contain authentication credentials, you might not want this in your code, plus the fact that database servers do move from time to time as things happen. So here, I used ScriptProperties exactly for its intended purpose. And so we keep the JDBC URL in a ScriptProperties and just reference it here. Once we've got the connection, we create a statement, give our SQL statement over to it, and get the result set out. And then we iterate over the results set. Results, they have the getObject method here. But they also have a bunch of get and then type. They have getFloat, getDouble, those kinds of things. So if you want to try to do type coercion on the fly as you're extracting these from the result cursor, you can do that there. If you notice, there's numbers and comments after these lines. You can call them either with the column number of the result or with the name of the column in the result. Generally speaking, I've found it's a good best practice to always use the name, for a couple of reasons. First, the name makes it much more obvious to the reader of this thing what column you're referring to, because that's a whole lot easier to read than this. Second, if you're doing things like SELECT *, and somebody decides to add another column to your table and then drop another one, then all your numbers change. And then this loop stops working. Now, once we've extracted all these things, we build the result object, throw it in an array, and return it. But getting all the bricks is not a terribly useful thing all by itself. Let's say we want to find all the blue bricks. Something that I've seen over the course of my career is code that looks something like this. All I will tell you right now, and I will explain why, is never do this. The potential problem is actually with every SQL database API in every platform everywhere. This is not at all unique to Google Apps Script. What's the big deal? It works, right? If you substitute in blue, you do the query, you get all the blue bricks. And it will return exactly what you want. And that's good. But if some user of the function can pass in some nefarious value for color, bad things can happen. There's actually a cartoon that covers this. If you plunk in the term "Bobby Tables" into your favorite search engine, you will find it. It's very funny. But I'll illustrate the problem to explain it. So if you call the function this way, yes, you will get no rows from this when you do execute this query. However, you will no longer have a table with which to query after you're done, because when you take that statement, and you add that argument to it, the SQL you get there is what you wind up with. So now that I've told you exactly how not to do this-- actually, let me back up a second. If you learn nothing else from this whole session-- and as much as I want you to remember the rest of it, if you remember nothing else, remember this, because you never, ever, ever want to do this-- so now that I've told you how to do it wrong, here's how you do it right. All the databases that Apps Script supports support the notion of something called bind variables. And here, we're using question mark. And this is basically a placeholder for the value you actually want to pass in. And much like the get methods that were on the result object, on the statement object, there are a bunch of set methods, which allow you to set the value of what those placeholders are. And they are numbered from one. In this case, we've got just the one, so we're good. And then the loop follows as it was before. JDBC has some nice advantages. First, it's SQL. And pretty much, these days, a lot of people know SQL. So it's not that hard. Second, if you use reporting software in an existing database you have, you can continue to use that. Apps Script is just another client. And third, you've got the transaction and consistency guarantees and all those kinds of things that you're used to. However, it's not all rainbows and unicorns. Unlike ScriptProperties and spreadsheets, you have to have this database hosted someplace, with all the provisioning and setup headaches that that implies. It can also be slow. And actually, this delay, depending upon where things are, can actually be built in. The problem here is that, say, if the Apps Script server that happens to go to try to talk to your database lives in New York, and your database is in LA, even if everything was traveling at the speed of light, it would take 24 milliseconds to make the round-trip. And that's before you've routed any packets and oh, yeah, even executed your query. So talk about the importance of data locality. And also, as I said before, if you remember nothing else, be careful about SQL injection, or you will have a really bad day. Another thing also that we noticed, if you looked at the code sample, is that you spend a lot of time shoving things in and out of SQL. And it's kind of a pain in the neck. A lot of other languages, Java and C# and those, they have all these frameworks that basically handle the marshaling aspect, using either data access objects or object relational mappings. A lot of what they're doing is really just handling the data conversion between them. And again, you have to plan a bit up front. You can't just kind of do it ad hoc. You can't really kind of wing it, because the expression I've heard before is that data comes and goes, but schemas live forever. So to illustrate, as we move to cataloging more of these LEGO parts that I have, I had a bit of a dilemma, because not all LEGO parts are easily describable the way we did bricks. Not all of them are rectangular. They have the ones where the number of studs on the top is different than the number of little holes on the bottom. We have LEGO minifigs, which are the little people-- and they look nothing at all like a brick-- and satellite dishes and everything else. So how do we represent these sanely in the SQL database? You've got a few choices, none of them I particularly like. But you can have one table that's got, like, 5 million columns. And each item has some subset of those filled in. And I hope you know what-- you'd better be careful which ones you fill in. You can try to partition the parts into things that sort of look the same and then try to basically do the same thing there. But you have a granularity challenge, like how narrowly do you do these. And then another option that I've seen before is you compose an attribute value table. But those tend to be a bit of a bear to query. Actually, in some cases-- I think I was talking to somebody who said they wound up basically implementing their own query optimizer to be able to do it. So for this LEGO example, it turned out that the SQL database is probably not our best choice. Well, today, I'm not really announcing it because it's already been announced, but-- ta-da! We're launching something that's even easier. And it simplifies the way that you can manage data within Apps Script. It's sort of like if I do everything right, it'll kind of change the way that you think about ways that data is organized. We're calling it ScriptDb. And this is kind of a big deal, because it's a JavaScript object database. You're working in Apps Script. You're working with JavaScript objects and data all the time. Why not just deal with all that marshaling stuff and make it so that you don't have to deal with it, you just save what you want? And the other nice thing is that the query syntax is very, very simple. The basic premise is what we call a query by example. Sometimes, we refer to it as QBE. The idea is that when you call a query, you give it an object that looks sort of like what you want to come back. And we go ahead and figure out what that means and return you the objects that make sense. ScriptDb is really convenient because it comes ready to use, which means that there's nothing to host, there's nothing to set up, there's nothing to configure, there's no installation. All you've got to do is use the API, and you're good to go. All right. So how do we do that? Well, let's start simple, and we'll go from there. All access to the database is via a database object. And in ScriptDb, you get it by calling ScriptDb.getMyDb. Like the way ScriptProperties works, this database object is tied to your script project. That means that other projects that may use yours as a library don't have direct access to it. However, you can pass this database object to other libraries that you may be using, and they can access it the same way. But a good best practice is to locate all of your database code in one library and then export functions that manipulate it through the libraries feature. Saving objects in ScriptDb is actually kind of simple. Here, we make a part that's just a two-by-three standard height brick. We've got 52 of them. And they're blue. And we say save. The object that's returned by save is almost exactly identical to the argument to save, except that it's got a getId method on it. It's the internal ID that allows us to be able to access this thing later. And one of the things that's not illustrated on the slide, but when you save things in ScriptDb, like we have, it's a fairly flat object. It's just got attribute at value, attribute, value, attribute, value. But the values themselves can be objects themselves, whether it be arrays or nulls or Booleans, numbers, strings. The only main JavaScript type we don't currently support directly is Date objects. But what you can do is you can call getTime on it, get the milliseconds as epoch value, and then save that instead. So the other thing, also, that the saved object allows us to know is whether or not this is an existing object in ScriptDb or not. And this allows us to do updates. And here, we're taking the object we just saved. We say we're setting a Boolean flag on it. We're updating the count. And we just save it again. The idea that we had is actually useful because you can just say, I want to load it. Or if you have an array of IDs, you could just load the whole array. Now, if the object with that ID doesn't exist, you get nulls back. The array of parts that you get back is going to be the same length as the array of IDs. For any one of those IDs that doesn't exist, there's a null in that spot instead. So this way, you can identify which ones are which. We can also delete things by ID. Or if we already have the object in hand, we can just say, delete the part. But the interesting thing is when we get to queries. Now that the data is there, how do we find what we want? Well, we'll start with a basic query. First, we get the database. And this is how we do queries. In this case, we're saying, give me all the objects in the database where the short side attribute is equal to two. That's pretty simple. And then the result object looks a lot like a Java iterator. If you've seen Java iterators before, it's the same pattern. Basically, while the result still has a next item, we get the next item out of it, we process it, do whatever we like. Now, if we want to find all two-by-four bricks, we just add another attribute into the query object to do it. It's pretty simple. We can also query sub-objects the same way that we query the top-level items. So this case, say I've got-- my LEGOs, actually, I keep track as to which ones are in which bins in a bag. You can query these sub-attributes of these objects, as well. We also support doing alternation or OR. In this case, we're saying, give me all the LEGOs that I've got where the short side is two and the color is either green or blue. So far, so good. But what if I want the ones where the long side isn't three? We also have NOT. Now we get a little trickier. I've got more than just LEGO bricks. I've got those little LEGO men, the minifigs. And they have various parts and colors and decorations of their own, like heads and torsos and legs and accessories and equipment. So what I want to do is, how do I format these things so I can find all black minifig parts? So if we have minifig parts laid out, like up there on the top, you can use any value just to say that this attribute exists at all in the object. We also support things like, as your database gets bigger, you might not want to deal with them all at once. We allow you to say, like a limit query, this is just like the way it would work in SQL . You just say, I want 20. Limits on their own are not terribly useful until you can do ordering. By default, when you sort, it'll sort them lexically, like it does on the left. However, when you're sorting numbers, you'd probably prefer that 2 sorts before 10 instead of afterwards, so you can pass the DB numeric argument as the second argument. And that'll take care of sorting it the way that you want. Now, in the course of my career, when I'm dealing with databases and building a UI, oftentimes, you're building some kind of paginated display. And so here, you've got your ordering criteria. You're saying, start at page number times page size, limit page size. So like in our case, our page size is 20. So give me the second page of 20 results. And every time I've done this ever in my life, I always seem to manage to screw it up. And so I decided when I was doing this that I was finally going to capture the pattern so I never have to get it wrong ever again. And so I added the paginate option. You still can you "start at" and "limit" if you choose. But you don't have to. Like JavaScript double equals, we also support the same kinds of type equivalences in our queries. So if you have saved something as numeric 3, you can query it with string 3 or Boolean true. I won't read the slide. I assume you all can read because you're here. But as another example, if you save an item with a string value of 2, you can find with querying for the numeric value of 2 or true. If you query it with the Boolean true, that'll match any true value. So you'll obviously get things other than 2 as a value. Now, this is the question that almost always gets asked when I get this far. When you're using data stores, you need to partition your data into logical subsets of related data. For example, if you're in a file system, they have, then, this notion of directories and sub-directories. So you don't have to deal with all your data at once. You can just deal with, say, the photos from the last time you went to Chicago or a bunch of salary letters or anything like that. In database land, you have silos like all the HR data or all the data that corresponds to some subsidiary corporation that you have. In a finer grained data silo, it might be the table of current employee compensation or the data for SKUs in department 29. The mechanism for siloing data in database land is either by the server, the cluster it's in, the database instance, the database within the instance, the tables, and even to a certain extent, the columns. Data silos come in a couple different flavors-- actually, exactly two-- disjoint and conjoined. Disjoint silos are silos where a record can be in, at most, one silo of the given type. An example of disjoint silos are things that are cars and things that are people. There's obviously no overlap between those two. Or people that are 20 years of age or people that are 21 years of age. In traditional databases, these are often implemented with tables, since while a given row, you may have copies of it in other tables, a given row is actually only in one table at a time. Conjoined silos, on the other hand, are silos where things can be in more than one silo at a time. An example is things that have email addresses and things that have postal addresses. You can have any of the four different combinations of having these or not, and they're all valid. And in traditional databases, you can represent these using views. So let's apply these to ScriptDb. With ScriptDb, you could silo by the script using the Script Libraries feature, and then by different combinations of attribute presence or value. A simple way to do disjoints sets is like we do up top in what I call a table attribute. Since an attribute can have, at most, one value at any given time, that kind of fits the definition of a disjoint silo quite nicely. One point to make here is that there's nothing magical about the name "table." We could have called it "tableau" or "mesa" or "Fred." ScriptDb doesn't know what "table" means. So you can use whatever you want. In a way, the silo and conjoined sets is what I call an implicit or an inferred one, in this case, something like minifig type, just checking for the presence of an attribute. But sometimes, there's no way to after the fact infer a particular conjoined data silo that you're looking for. In such cases, you can add explicit flags to the data, like we do on the third case. If you have the mental model of how Gmail labels work, this is very, very similar to that. When you're choosing how to lay out your objects in ScriptDb, something that's a really good idea is to name things consistently within the objects. Within a silo that you've preplanned ahead of time, it's kind of obvious. If we're storing LEGOs, and we want to know colors, we don't want to store it sometimes as color, sometimes as brick color, and sometimes as color brick, because it makes the queries kind of ugly. However, even across your data, it's a good idea to always call the same thing by the same attribute. For example, in the examples in this session, the color is always the attribute simply named color. If we stick to that, when we add things like axles or moon plates or roofing bricks or whatever other LEGO parts we can come up with, we can always pull all things of a particular color just by querying a color attribute. How about another example? Say you have a database containing your organizational information, like the org chart Expense Reporting. If you always call the department ID department_ID in your data, you can, in one query, pull all of the stuff relating to department ID, whichever one you choose, all in one pull. Now, eventually, it's going to happen where you don't actually follow through with this, and things don't go right, and things weren't named consistently. After all, to err is human. Are you doomed? Fortunately not. One query feature that I haven't mentioned yet is what we call attribute OR. Much like we have db.anyOf for values, we also have a syntax to represent the query where this or that attribute is equal to some value. Instead of having an unquoted attribute, like I've had in all the examples so far, you have the attribute be a quoted, comma delimited list of attributes. So in this case, it would return all the ones where the color was black, irrespective of whether or not you spelled "color" with a U or not, or which side of the Atlantic you live on. If you noticed earlier, I used the term "preplanned silos." Some silos are very intentional, like the table attributes or the flag-based silos I mentioned earlier. But if you name things consistently, often useful silos can be discovered in your data after the fact. I've already been using a few of them already in the talk. The one about color I've already mentioned. But there are a few others, as well. One silo is the bricks siloed by one of their sides, whether the long side or the short side. And if you look across your data, you'll find that useful silos turn up in a bunch of different places. Even if you're using SQL databases, it's actually a good idea to do this, too, for a lot of the same reasons. Well, so far, so good. The only thing left over that I can think of now is joins. Well, what about joins? Well, first, joins are needed less often, because if you name things consistently, you can avoid a lot of them. In a traditional database, you can't really cut across your data in quite the same way that you can here because if you're crossing multiple tables, you either need to do a union query or a join for that. Second, multiple queries in ScriptDb are not that big a deal. ScriptDb queries are comparatively cheap, often in the neighborhood of 30 to 50 milliseconds, a little bit more expensive than the speed of light round-trip from New York to LA and back. Your first database operation in the script will take a little bit longer, because we do have to load some information about your database before we can proceed. Traditionally, when working with SQL databases, I've seen people go to somewhat extreme lengths to formulate the query so that they can get the data in one pull. But in ScriptDb, multiple queries are actually just fine because they are, again, fairly inexpensive. So say we want to find what color of standard height brick we have, that we have at least 50 of them, and they're two-by-two bricks, and then to make sure that we have a torso that's the same color. So first, what we do is we query our two-by-two bricks, standard height. And we say, give me the ones where we have at least 50 or more of them. We'd loop through the results here, storing the colors in the color variable. And then, secondly, we just query for minifig torsos, where the color is any of the colors that we found. So it's actually not that difficult. We are looking for common patterns that we can make these kinds of things simpler for. There are some limits that you need to be aware of in ScriptDb. Query results can be at most 50,000 rows. Though the size of your actual data set can be much larger than that. It's only governed by the quota. And the quota limits are given here. So hopefully, you've got now the information that you need when developing your next Apps Script program as to choose which storage system is best for you. To summarize, ScriptProperties is a simple key value based store with a built-in UI, spreadsheets when you need more structure and a more advanced built-in UI, JDBC for interacting with existing legacy databases you have or when SQL is important to you, and ScriptDb for a zero setup JavaScript object store. If you want for more information about Google Apps Script, ScriptDb, you can go to the respective URLs in this slide. Yes, we've now got some time for questions if you want. Please go up to either one of the microphones so that the people on YouTube can hear you. Great, thanks. AUDIENCE: Hi. Is the ScriptDb one instance per script, or we can have different instances for each user that's running the script? DREW CSILLAG: It's tied to the script project itself. So there is one per script. Did I answer your question right? AUDIENCE: OK, but the quota then stays script owner then? DREW CSILLAG: Yes, the quota's charged to the script owner. AUDIENCE: All right, thanks. AUDIENCE: You mentioned a best practice around making a store available to multiple scripts. Would you mind just reviewing that? DREW CSILLAG: Yes, sure. Actually, if you watched Saurabh's talk the other day, you can kind of get an idea of what he did. But basically, the idea is you put all the database access that you need for your ScriptDb database into one script project. And then the projects that need it can then reference it as a library. And you could do it that way. Or again, you can pass the database object between the libraries as well, if you want to do it that way. But the best practice is to co-locate it all in one spot. Hi, go ahead. AUDIENCE: So you talked about doing a query with a comma separator for two different words, where effectively, they mean the same thing. So you have some limitations, it seems, on what the-- DREW CSILLAG: What the keys can be. AUDIENCE: --what the keys can be. DREW CSILLAG: Yes. Currently, they are limited to what you can put into a normal JavaScript identifier. AUDIENCE: Identifier. OK. Not an object key. Because you could have the comma in an object key. DREW CSILLAG: Right, yeah. But these are-- I'm drawing a blank on what the thing-- it's a JavaScript identifier is what the allowed characters set for a key. But the values can contain whatever. AUDIENCE: OK. And the other question is, when you're returning multiple values, you return null to indicate that the value wasn't there. DREW CSILLAG: Yes, when you're loading with an array. Yes. AUDIENCE: But that means that you can't-- is there a way to distinguish whether the value was there, but its value was null? DREW CSILLAG: OK. Yeah, something I didn't mention is that when you're storing objects in Apps Script, they have to be regular, full-on-- like an object with attributes and values. I can't just store the number 3 directly. So all the things that come back from a load will be JavaScript objects with key value pairs in them. If I'm explaining that-- AUDIENCE: OK. DREW CSILLAG: That make sense? AUDIENCE: Thank you. DREW CSILLAG: OK. AUDIENCE: What is the limit, how many records or-- what can I store? Can I store millions of records? Or is there a limit? DREW CSILLAG: It's limited by the size of the stuff you put in there. It's not based upon a row limit or a row count. AUDIENCE: You mean limited by the total size of what I store? DREW CSILLAG: Yeah, by the total size of the store. It's a size limitation. It's not the number of rows. AUDIENCE: And what's the size limit for the store? For a paid customer? DREW CSILLAG: Consumers, 50 meg. Google Apps customers are 100 megs. And-- AUDIENCE: Apps for business, 200. OK. And is this still in beta, or is it production? DREW CSILLAG: I think it's still tagged as experimental. AUDIENCE: Experimental. OK. DREW CSILLAG: Yeah. AUDIENCE: This was asked earlier. But yesterday, you talked about standalone scripts that run with user quota. And somebody asked a few minutes ago about the storage. You said it's per-- can you review again? Are all the database structures per script, or are there any that are per user? DREW CSILLAG: Things like ScriptProperties and ScriptDb, those are charged to the script owner. But all the other quotas I can think of are all charged to the user accessing the thing if you publish it as a web app. AUDIENCE: I have another question about the quota. Is the quota tied to the script or to the owner? DREW CSILLAG: To the owner. AUDIENCE: OK. AUDIENCE: Hi. Is there a way that two users with two different scripts can use the same database? DREW CSILLAG: Yes. Using the script libraries feature that we talked about earlier, what you can do is you can make it so that, say, the included library, its sole function may be returning the database object that's associated with that common script project that they're both using. AUDIENCE: So I'm still grappling with the quota issue. Is there a way to-- if I create a library that accesses ScriptDb, can I now have individual users who want to have their own separate databases use that library and have three users, three separate databases, three separate quotas? DREW CSILLAG: Yes. Basically, what you would do is, in the three separate scripts, what they would do is they would pass their database instance into the common library that manipulates them. That make sense? AUDIENCE: Yep. DREW CSILLAG: OK. AUDIENCE: I've just got a quick question. You said there's a 50k [INAUDIBLE] limit. I think it's returned. DREW CSILLAG: That's just on the query size, yeah. AUDIENCE: So how did-- DREW CSILLAG: If you need more than 50,000 rows, you'd have to find some way of breaking it into chunks. AUDIENCE: OK. Is there a way to see how many you would get? Because what if I want all bricks, and there's 70,000 blue? I'd have to know what IDs are in there. Like I didn't know if there was a way to get first 2,000 on the query or whatever. DREW CSILLAG: I'm going to say talk to me afterwards. Off the top of my head, I don't remember. I'd have to look it up. Pagination might do it. Like I said, I'll have to double-check. AUDIENCE: Is there a count feature? In other words, can I count how many bricks I have? DREW CSILLAG: Yes. Actually, there is a method in the DB object that I didn't mention. It's db.Count. And it takes the same kind of query object that the query does. Anything else? Well, thanks for coming. Great. [APPLAUSE]
Info
Channel: Google Developers
Views: 8,991
Rating: undefined out of 5
Keywords: gdl, drive
Id: 8lJewIWVI5s
Channel Id: undefined
Length: 41min 47sec (2507 seconds)
Published: Mon Jul 02 2012
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.