Storing Non-Scalar Data
CC Attribution 4.0 International:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Rethans, Derick
In this presentation we will look at storing complex data in a single field. Many noSQL solutions are created around this (such as Redis' lists, sets and hashes; MongoDB's and CouchDB's records), and many relational database now also support storing complex data in a single field through specific data types (such as PostGreSQL's JSONB or hstore, MySQL's JSON). Each of the different database engines support different things, and handle these data types in different ways. In this session we compare the different approaches to storage, indexing and interactions with these data types in different databases.
 Welcome this morning. I'm Derek. I'm Dutch breakfast long story. And most people tend to know me because of PHP stuff. I wrote some things for that. I won't be talking about that whatsoever, but that might be an example that uses PHP code in one of these slides. I like maps. I like beer. I like whiskey. And you'll see those items coming back in the examples that I'm using. It's a bit, well, besides the maps, the beer and the whiskey, it's a bit too early for that if you ask me. All right. So what are we going to talk about this morning? I'm going to talk about non-scaled data. And first of all, I'd like to sort of define what I mean by this. Traditionally, what you would store in a database, you have like fields and you have values. And those values tend to be a single value, right? So there can be strings or numbers or booleans or whatever thing you come up with there. And then you have non-scaled things, which are things like arrays and nested objects, arrays of objects and so on and so on. I'm sure some of you have in the past stored a comma separated list of values in the database field at some point, right? Just raise a hand. Yep. And there's people not paying attention or lying. Always the case. So yeah, this is the thing, right? The non-scaled data aspect, you see a cliché up quite a bit more in many relational databases. You couldn't traditionally do anything with that. So a few examples. So a few examples here. If you have an article, you want to store some text with that, right? Well, you ought to say that there's articles about Java and PHP and databases. So initially, what you'd have to do in a relational database is you have to have this table that defines all your text. Then you have to define the table that stores all your articles. And then you have this link table that links the articles to the text, right? So you get three tables for doing that. But that's not particularly very efficient because you need to do like a two-dip table join any time you show an article. Now, other examples where this becomes sort of important if you have things like a set of properties that you need to store with something. A traditional example here is using like a big product catalog, right? So if you have a product catalog, most products will have a name in it. They will have a price in it, right? So those are the fields that are the same for almost all things. That wasn't me. No, that wasn't me. I don't know. We'll keep an eye on that. So yes, you have the set of properties that are always going to be the same for a product and you have a set of properties that are going to be different. Like for books, you have an author and a number of pages, right? Whereas for whiskey, you'll have how strong it is and where it is from and how heavy it is for shipping and things and so on and so on. So there's a very different set of properties. Traditionally, in a relational database, how you would store those is you would store them all in very complex pattern called EAV, which is an entity attribute value relationship. It works well, but it is not very easy to find back things in the database when you look at it yourself. And it's kind of a complicated thing where you need to multi-table joints to get all your properties out of it. Unless, of course, you normalize it, but that's something. In a relational database, you don't do it. So those use cases here are the sort of data things we're going to have a look at, but not with books. So what this presentation is not about, I'm not going to talk much about scalability or high availability or benchmarks or I don't have the time to go in that covering all the mentioned technologies here because I probably need a whole day for that. I only have 57 minutes left. All right. So the things we're going to look at is database type for non-scaled data. We're going to look at some query and manipulating data sections and we have a bit of a recap at the end. We got started with that. First of all, let's have a look at different database types. Now, you're probably familiar with relational databases, MySQL, PostgreSQL, Oracle, IBM, DB2, MSS, SQL, one of those, right? Probably should know. But in the last, say, what, 15, 20 years, a whole bunch of other different types of database that are also on the market often put under the no more no SQL, which I don't think is a particularly good name for them, but basically non-relational database is probably a better way of describing that. So there's three groups I want to look at. There's many more groups. If you look at Wikipedia, there's probably 80 different categories that people come up with. That's, I mean, overdoing it a little bit, I think. But to look at the most simple thing that you have here is usually a key value store. And a key value store is, it works really simple. They tend to be used for caches because they are so simple. Lookups and storage is only done on the one specific key and you can't do operations on anything else besides this key. A good example for this is RADDIS. And in addition, I forgot to mention point three here, is that the values that you associate with those keys, they don't have to be just single values, right? They can be lists or hashed as well. Go back into those in a moment. So the keys in RADDIS are binary safe strings. The values are safe strings. But the strings do a little bit more than just strings sometimes. But besides the single, the scalar value of strings, they also can store hashes, lists, sets, sorted sets and some others, which I'll show you in a moment. The interaction of it goes through RADDIS CLI or PRADDIS PRADDIS, which is a PHP extension to talk to RADDIS. The RADDIS protocol is particularly quite simple. It is still a binary protocol, so you do need a little bit of logic to talk to it. But the RADDIS CLI tool that comes with it is very simple, and I'll have an example of that in a moment. And the normal strings are sort of special as well, because you can treat them as numbers, and I will then use them as numbers instead of just pure strings. So let's have a look at one of those of data types in. And I said before, in key value storage, the only operation you can do on a key, right? So if you want to store, say, properties of a specific product, in this case, the whiskey Glenfiddler, and I want to do something with the property tags, what I actually construct myself is this key, convention dictates that you use the colon in between the different things in here. So we have the product category whiskey here, we have the name or the slug that we have here as Glenfiddler 12, and then we have the property tags. So you build this key up yourself because as being a key value store, there's no other way of doing that. There's no multi-column keys. And then with specific operators, you can add tags to this. So the first one we do as add stands for set add, we add the tag fruity. And then the next one, we add vanilla to it, and then we add fruity once more. But because there's a set of data, and the set only contains the same values and not duplicate values, we're not fruity again, I won't do anything with it because it's already part of the set. And then if you want to do things with spaces in there, you need to use double quotes. Then when you do a query, you can do many different queries on sets. You can do test whether the set contains a member. So when we set, well, if there's a tag pt set on this whiskey, return one or zero. And of course, because we hadn't set it, we get a zero back out of it. So it means false, not part of it. And of course, you can query all the members of the set. And that's probably wise if you sit on that side of the room. I'm just warning you. So those are the sets. Now, an additional thing to this is hashes. And they're working a little bit of a slightly different way because in a hash, you have key value pairs that you store with a property. So to go back to the example that I have about a product catalog, if I want to store multiple properties, what I'm doing here, I'm storing for the whiskey Ben-Nevis 19. I store in the props field, I will store the key value combination distillery Ben-Nevis. So it says basically, the distillery for this whiskey is Ben-Nevis. You can also do HM set, which is that's what hash multisets. And basically, I'm setting two properties here. The first key value pairs region, Scotland, Thailand. And then the second one is H19. And that's how you specify the non-command line. Of course, when you put things in the database, you can get things out of it again. So you get the hashes out of it again by using hash get all, which gives you all the key value pairs that are in there in A array, starting with one. I don't know particularly why they chose that, but that's how it works. And of course, you can get values for specific properties as well. And I have to mention here that although age, we stored like 19 as a number, right? It comes back as a string because the values are always going to be strings, but some exceptions. So that's what Veritas does as an example of a key value store. And there's all the examples, of course, as well. So slightly more complex data, data stores are the document data stores. They're often more richer data types. You can often do operations on both keys and values. In many cases, when you see documentation for these document stores, all the examples will show you the data objects in JSON. That does not mean that those things are actually stored as JSON in the database, but it's often a way for communicating it or at least visualizing the data that goes in and out of it. And examples of this group are the ones that Lisa will be showing here is MongoDB, couch will be in Elasticsearch, but of course, there doesn't more than these three here. All right, so let's have a quick look at MongoDB. It's an open source product, just like all of the others really, so it doesn't really particularly need mentioning. The documentation is often using JSON, but it stores the documents as something called BISN, which is binary JSON on disk. The interaction with those documents is often through language-specific data structures. So if you are using Java, you build like the documents of like a Java builder kind of pattern. If you're using PHP, you can use PHP arrays and objects to store the data. And the driver will automatically convert us to this BISN layer under the hood. It's not something you have to take care of yourself. The interaction, however, is a bit more complicated than with Redis because it is now a binary protocol, so you need an extension for languages to interact with that in PHP. It's called MongoDB, but there's drivers for any other language you can think of or so. So the interaction is a bit more tricky, but it makes it easier to deal with from your language because it's a natural way of dealing with data in there. The documents are a bit more complex now because we have key value combinations, right? So we have at least MongoDB, there's always this underscore default, which is your primary key. It's immutable, so you can't change that. And then you have a whole bunch of properties, and I just want to point out a few. Like the words one is an array of words, and that is the native data type in there. It's an array of words that you can do queries again, see them. And then batches is a bit more complicated as well because it's an array of like objects again. So not only can you store just an array of things, you can store an array of those nest documents. And this is basically the same about all the other document data source they store something very similar as you'll see in a moment. So this is, I think the only PHP example that I have, but it should illustrate enough how this sort of works. If you want to insert something, you create a connection to a database and a table name, which in MongoDB we call collections. We insert two documents. As you can see, they're just simple two PHP arrays that we insert. We treat them as key value pairs. And then with insert one, we insert one document, and then insert many, we do many. I mean, it's not particularly very difficult. And all the languages, it works very similar ways. Having a look at CouchDB, it's also an open source of patchy products. The documents are JSON objects. And that is also what you talk over the wire. So CouchDB exposes a REST API, which you can talk to with curl, or if you want to use an HTTP peak line, there's things like guzzle and all the things. So it's not a binary protocol, it's simple, very much a REST API, which is handy because you don't have to divide specific language drivers, but then it's also not a binary protocol, so you end up having more data over the wire. So that's a little different trade of that, mate. So to have a look at the documents, as you can see, this is exactly the same document with one exception, is that every document, so they have this unique ID, but they also have this underscore REST stands for revision, which is important. We'll see later when we update documents. And this is something that is generated by the database for you. The number one, it just increases every time you update a document, and then you have a hash describing the contents of the documents. So that's CouchDB. Inserting in it is not very hard either. I forgot to mention that both MongoDB and CouchDB, you don't necessarily have to create a database, they'll just get created for you without having to do anything, which is kind of handy. So that's what I'm doing here, right? I'm posting in this demo collection, I'm posting with a unique key, which is in this case, Derek at localhost, as my underscore ID value, basically, telling that it's a JSON, and then I give it a JSON string containing information. And you can see here that the underscore ID is not in here because you don't have to set that. And a return value that you got back includes this revision number, which is important to know. And I'll show you why later. All right, elastic search, again, is a very different thing. It is not so much a database, it is more like a full text search engine, but it is also sort of a database because you can store documents in it. They use fancy words like near real-time text search engine, basically what it means is that as a database, if you store new documents in it, it takes some time for the index to parse this document and add them to the full text search index. That is often really fast, but it isn't instantaneously, whereas in other databases, when you store something, you have it immediately available through the indexes, right? So that's slightly different here. It is based on Lucene, just like Solr is, and Lucene is more, I think, originally, a Java-based storage engine that knows how to store full text search information, basically. And as many other tools like elastic search have been built on top of that, and probably one of the selling points of elastic search is that it's very easy to cluster. Actually, it is so easy to cluster that you sometimes end up doing it without wanting to do it. About a year and a half ago, I was speaking in the user group in Amsterdam, and I was borrowing the afternoon an offer of somebody, and I was giving a slightly different talk, but it involves elastic search, and it connected my laptop to the network, and it started replicating somebody else's data because we're also playing with elastic search. I mean, it wasn't important data, but it started replicating it. Luckily, they have now changed their default configuration, so that doesn't do that out of the box anymore. Sorry? Yeah, it's changed now, yeah, absolutely. Yeah. Yeah, you now need to configure your cluster key, I think they call that. That's not a problem anymore, but at that time, I thought it was quite funny. But it still illustrates that it is very easy to cluster because you just start all the nodes up in the network with the same configuration name, and it'll just start replicating. So that's really handy. So they don't, as I said, because it's not a traditional database, they don't really call them database and tables. They tend to call them indexes and types, and the type is basically a collection where there's lots of common fields, although you don't necessarily need to have the common fields because they are being no scale solutions. Again, you interact with this with JSON objects. You can do it with the REST interface or with some helper extensions or libraries, in this case, that's elastic search PHP, which is something that the people from Elastic Search have written, and it's there for all the languages as well. Again, elastic search documents. Do you see any differences? Nope, I don't either, so that's good. And you can insert this in, yeah, again with curl or REST API back into the database. The curl URLs were a bit more complicated, but other than that, it's practically the same thing, right? There's not much difference. So one thing that elastic search doesn't really do is, again, that doesn't mean, what it doesn't really do is update documents. Anytime you do updates, it basically replaces the document and has three index the whole thing as well. So we spoke a bit about key value stores and we spoke a bit about document data storage, but we also want to have a quick look at relational databases because I thought, well, those document stores with all those properties that could get kind of handy, and we want to have this functionality in a relational database because lots of people already use them, right? That makes sort of sense to add some of the properties from the document stores and key value stores as part of, in this case, I'm showing you Is MySQL and Post-SQL. Of course, other database will have different things, but let me just talk about these two. So the first one to have a look at is MySQL, which has support for a JSON type. I call this very basic because the interaction with it are very different from what Post-SQL allows you to do. They're also working on something called MySQL document store, which in my opinion is not a great name because the only thing it really allows you to do is, it's like, crud operation, like create, remove, delete, and delete. No, read. That's what the Airstand, where it's not both, remove and delete because that would be a bit silly. So it's a very simple layer built on top of MySQL that allows you to interact like it as it was like a NoSQL-ish data store. I don't think it's been released yet, but this field goes really quickly, so the slides might be out of date a little bit, but I don't think it's been in a, this has been in a release yet. So this JSON type, the interaction with this is through strings containing JSON, which is a little bit different than what the rest APIs for Couch, TV and MLS exerted us because you still need to construct this as part of a SQL statement. The storage, again, is better than just storing JSON data because that's kind of pointless to search and things do with, right? And the manipulation is with specific SQL operators. They are usually, luckily, part of the standards, so at least between MySQL and PostgreSQL, they're practically the same. And one of the properties that my phone and website said that the field order in these, in the JSON type for the fields, isn't guaranteed, which is sort of the case with the other document stores as well, but they tend to not do anything bad about a meter. So in general, they should be alright. Just to have a quick look at how to store something like this in MySQL. So if you look at on the MySQL command line, you can do create table uses and you have the JDOC field and we call this the JSON type. And then in there, you can insert this JSON document. As you can see, I used a single quote around it, difficult to see, and then with all the fields in there. So you're inserting this string as it was a JSON document, which means you need to be careful about escaping at some point because the SQL itself needs something to make sure you're not escaping. For example, if you had a quote in one of the fields here, you need to make sure you escape it because it's part of a string. PostgreSQL has a few different data types in there. They have something called hstore, which is very similar to a key value kind of data type. They also have a JSON type, but I won't be talking about that because they basically don't want to recommend it anymore. The JSON data type is much better anyway. So there's no need to use the other one particularly anymore. And the query syntax is, and the index support is quite a bit better than my SQL, so postgreSQL. So again, let's have a look at hstore first. So hstore is again a column type, which is a list of key value pairs, and there are both a string. So it doesn't support richer data types here than just scalar values for the hstore field, I think. That's probably true, but I did not try that. Yes, I did not try that. Okay. I'll give that a shot later. Okay, good. Okay. I'll play with that. Let's pretend we have only the simple set of document, right? So yeah, what is important that I said is that even though you store numbers and booleans in it, there are still strings. So you can't do particularly queries against that. And what they style is that it is often a field that is often not queried, although you can index this, for properties that are not necessarily part of every document. So for example, as I showed you with the product catalog, it could be those extra properties. I do quite a lot of stuff with open stream map stuff, and open stream map users postgreSQL, and I do quite a lot for starting map data. So there's lots of descriptions of objects in there that create a style of the map, but you can't necessarily store all the tags that objects can have because it's an unbound set of tags, and the way how the rendering of those maps work is that for every tag that you put in their schema, in the rendering schema, you have a specific column, and of course you can't have an unbounded set of columns, right? So what they often do is they have this set of properties describing one document that are often there and necessary for styles, and if all those stuff is hardly ever used, they put it in an h-store, so you can still do something with it, but you lose performance, but it allows you to store all the things that you want in there. So that is sort of the use case for that. The JSON-B type is similar to MySQL's JSON type, but also quite a bit better because unlike MySQL, you can actually index it, and it supports better or richer data types in the h-store because it supports all the JSON data types, really. The column type is called JSON-B, and yeah, we have now this richer, well, you might remember this JSON document from before, because it's practically the same that I've inserted in both Elasticsearch and CouchB and MongoDB, right? So it looks like this. So after inserting data, we need to sort of query data, because database are sort of useful to get data back out of again, and yeah, I already sort of showed you how to do it with Redis, right? You can, in sets, you can check for whether a key value is part of the set. You can retrieve all the members of a key. The hashes, you have hget all to get all the information out of a key, or you can do hmget, which you get a set of keys out of the head, hash, and I didn't show you the lists, but there's also like lists that you can set ranges from that you want to retrieve and stuff, and there's all the data types too, but there's many more like this. So that's how you query there, or of course you can request the whole property by its key. So in CouchB, queries are primarily done through the underscore ID field, which is the default. Now, it is possible to set up secondary index and secondary keys as well, but you do that to something called MapReduce, so you need to define those views upfront. It's clever enough that it doesn't have to recalculate a view every time you insert a document, because it stores the keys any time you insert or update a document. No, it doesn't do that? It does an incremental update of the index once you query it. So the insert is cheap? Right. The first query is slow. It updates incrementally the view. I did not know that. Well, thanks for that. So yeah, if you do look up on that, you tend to do it only a primary key. Now, I have to say CouchB is not the database in our best in here, but that should be all right. MongoDB allows for doing queries without having setting up secondary keys upfront, so you can do those queries right away. This is a simple query that you look up, which is basically what we find out once again, that wasn't me. I find out all the check-ins where the, there we go, region slug matches Scotland Islay, and the rating is larger or greater than three, and we only want to store the whiskey rating in H fields. So this is the quality match. There's this match with a query operator, and there's many more than just GTE, that's for greater and equal. And there's a projection. Basically, what it says is the following as go. That's what it says. Select whiskey rating in H from check-ins, where region slug is Scotland Islay, and the rating is larger or equal than two three. So what like you mean this morning? The stream doesn't help the people here, does it? Okay. There's also another way of doing queries is something called an aggregation pipeline where you run different operations on a whole collection ago. So in this case, what we're doing is we want to find all the documents where the region slugs start with Scotland. It's a regular expression operator. And then we do a group by region. So basically this says, find me all the whiskeys in the region Scotland, and then group them by specific subregion. So something you get back out of this. Okay. I said those things already. And then you get something back like this. So you have the ID fields, which is the name of the region. And then it has an array of the whiskeys that come back out of that. So it's a simple group by that you do here. And of course, unlike your relational database, it actually just returns the array here instead of doing a single record per, single record per return, basically. All right. So elastic search has two different ways of doing queries. You can do it on its primary key, or you can do this on a field value combination. That's a simple way. So here we're searching in users count 16. So that's key value per count has to match 16. And you get a result back out of that. But very elastic search really shines is, of course, its full text search capabilities. So you can construct queries like the following. Like it's a Boolean query, and they have all the queries as well, like just Geo queries and a whole bunch of others. And it says the documents that I find should match, the set of equals pandering, and then a match risky should be sherry. And in this case, they both need to match. But you can construct queries where there's an or query or a Boolean not query. And it's so much richer than I can show you here, which is really, really good for building full text search index, like having a search box saying, well, give me all the documents or all the products that match these keywords and stuff and so on. It, yeah, that works really well for that. Quering JSON objects in myScrown, I found bit complicated because there's new SQL operators that I didn't really know in front. So if we have this table where we store the name of a whiskey and the properties, in this case we store the properties age, cost, strength, and ABV or colored. So the last one isn't shared among the documents. We can construct queries, say, select name and age for all the whiskeys where cost, strength equals true. And that's what you get out of it, right? So cost strength in this one is true. And what we want to get out of it is the name of the whiskey, which is there, and then the age as a value. I'm not sure why it showed only a single arrow here. I'm expected to show two, really. In any case, what I want to point out is that the way how you match against subfields is by using the dollar dot, that is the root of your document, then you have the field name. And if you have nested field names, you can use another dot and then another field name to match against these queries. The difference between arrow and double arrow is that this escapes the, unescapes the JSON value, whereas the single arrow does not. In this case, that doesn't show a difference because of the scale of value, but otherwise you would have seen that. All right. So the age story, you can also query a slightly small example here. So what you can do is get a value for a key. You can, again, use this arrow operator. So find me for every document in the table, find me whether the is admin field, the value of the is admin field. And then you get that back. You can find all the unique user count with larger than 10 by, again, using a finding from the age doc, the unescapes default, which is there could localhost where the count unique is larger than 10. So then you find the documents there. So you can do queries on this, on this fields. Quite well. You can also check whether keys exist, which of course you sort of need now. And you can match against key value pairs as well. So let's have a look at indexes. So Redis is primary, only looks at the key. The only key is your index. That isn't quite true because the data structures that you store for each keys, like sets and lists, they are also optimized in its in-memory base index. So that is sort of an additional index to it, but it isn't something that is exposed. So this data structure can provide you some with additional indexing. In CouchB, indexes as I said, there's by default you have the primary key being indexed and then you have the secondary keys that you can construct with MapReduce. MapReduce is basically a way for every document that you have. It will emit something, key value pairs, which you then can use to do queries against it. It's kind of tricky to explain, but it is sort of how it works. The output you get back from that is also sort of defined by the view because you define the sort order in there as well. You construct a new document. Yes. So what I've done here is, so those secondary indexes you store in something, I believe it's called a design document as part of the database and then you can do queries against those views. So that's why the word design pops up when you use the query. So I've only showed you really the map function. Basically what I'm doing here for every risky document that I have, I emit something based on a region slug and then a slug of the document. When I do a query against this then, against this design document in docs, I do the view by region because that is what I named this design document and then you get a result out of it. So you get all the rows. So you have the key scull and the full ID field because I emitted that and then the value, because that's the one that comes out of the document. And you do queries against that. It works quite well, but you need to think upfront very clearly on which bits of data you don't do queries on, which is something you don't really need with a relational database with the cave yet, of course, that if you do a query on something that you have an index, it's going to be slow. And that is basically the case for everything that you have an index, right, in any database. Think of it as a post-gres computational index. I'll get back to the MySQL version of those in a moment. So in MySQL, you can sort of, different words starting with an M. In MongoDB, you can do indexes, as I said already there, on the underscore ID field because that's unique primary key. So that's on there. You can set up, of course, indexes on keys as well as you can set indexes on nested fields. You can do that in CouchDB in a few years as well. Of course, post-gres call, you can do this as well. And what is kind of useful is that if you set an index on a key that has an array of values, you can actually do an equality match against words. So you can say, find me all the documents where words matches cadence heads, for example, and then it will still find this document. So that's kind of handy. Full-text search indexes get very complicated if you want to. So because this is a very different database, the indexes that you configure full-text search indexes. And they are meant for querying natural language. So they have lots of index functionality for language analysis. So you can set up different ways of how strings of text need to be separated. Often you do it by space, but that isn't necessarily the best way of splitting up a string into words, depending on what your subject matter is. You can set up a language and language rules then define how words are being stemmed. So if you want to, in English, you want to have the words walks and walk matched at the same time you configure a stemmer for English that allows you then to match those words directly on top of each other. You can do stop words that are not part of your index. So words like the and the kind of pointers in your index, right, because it's not something you're ever going to look up on. And they take up lots of space in the index because it uses often. You can boost specific fields to make them more important, et cetera, et cetera, et cetera. I mean, there's lots of stuff in there. The indexes, as I mentioned before, asynchronously built. So it sometimes takes a little bit of time before you can find a document after you inserted them. In Postgres School, you have something called a GIN index. You can think about GIN as a solution later if you want to. I just like the image. So there are two types of indexes for JSON. B fills a key value indexes or a value indexes. Actually, I don't think I have an example showing you how to create the second one, but it shouldn't matter much. So again, we're inserting this whiskey in here. We have a key value match. After we create this index, and well, that GIN index in very simple words does, it looks at all the key and value pairs and store them in an index. So whenever you match against either of them, it can find back the documents out of this in quite a simple way. What it doesn't really allow you to do is create an index on specific subfields. You can only index the whole data type, which is different than what couch to be and monger to be and to a lesser extent, a less exerted it. But in most cases, it's pretty good, right? Again, you see this and present, I don't know, I don't know the name of that operator. But you can match against key value pairs again in here. You can also check whether just like in monger to be where I showed you that the words have a value called L1 in there, right? So we have the words here where you have L125. So the GIN index is a bit better than just collecting all the data like a full certain index, but it does this kind of things as well. Yeah, go ahead. From what I believe so, yes, that is the case. Yeah. So I could probably build an index with a functional index. Yes. A JSON number cast into an integer and put that as a key into an index. Yes. You can do that. But as I said, I can't talk about all the functionalities in your databases. But yeah. So the annoying thing is that MySQL doesn't do any sort of this whatsoever. The only way how you can index those JSON documents at the moment, as I said, this field moves quickly is only through generated columns, through virtual generated columns. So if you want to index on, say, the ABV, the L-combo volume, what you have to do is alter my table whiskey other column, ABV float generated always, being always there as properties ABV. So that plicks out this ABV field out of the properties JSON fields, create a virtual column out of that, and then you can put an index on this virtual column to index that. Which is what I'm doing here. We create this ABV index on whiskey ABV, which is the value I plucked out of it, even though I hadn't stored that in the document itself. You can make this a materialize you, but I don't believe you have to, actually. So yeah, that is currently how MySQL does that. So the last thing I want to talk about quickly is manipulating data. And the important word there in any database that you have is atomicity. You want to make sure that no two programs or scripts or threats at the same time can update data. Traditionally relational databases have like this asset compliance kind of thing that is not something that NoSQL solutions often do, but have different ways of making sure that you don't update data incorrectly. So important words to remember is that you should never retrieve a document updated in your client and then store it back again, because that's a very dangerous thing to do. You get really loopy. So how Redis does this? It has like special operators that you can run. So if this is the start of my document that starts so that we have these properties again, where the age is 19, you can run hincby, which increases the value of a specific property by the number that you specify. So what this says is increase the property h in this key by one. And here Redis does something more than just strings because it knows how to add one to the string 19. And then you get it back as a string 20. No, you do not get one. That'd be mean. I don't think it does hexadecimal actually. So that is how Redis does it. It has specific operators to operate on this data. And basically doing a set add is a very similar thing that I showed you before. You can also do queues like using lpush and lpop that push things on the front of an array, pop them over to start of an array. So you can implement a kind of queuing protocol with that as well, all using this atomic operations. Yeah, one more. In CouchDB it is, say we already had this document with the key Derek at local cylinder and we would do, we want to store this again. It tells you, no, can't do that because we already have a document with this key, which is kind of annoying because how would I update this document, right? Because if I want to update, I need to store it with the key again. So the way how you can do that is this is where the underscore rv thing comes into play. What you need to specify for a beta work is the rv field that you have gotten back previously. And the cool thing about this is that if you do this at the same time from two different threads, only one is going to work. You also know which one is going to work because it will tell you either the new revision number or it tells you the, you get a conflict because the document that you want to store with the revision number you thought it was is no longer there because the first thread has completed and that's stored a new revision number. So this is how to solve conflicts and strongness of the ethnicity data. And then your application can decide what to do out of that. It can decide to restore the document potentially probably overwriting the original, save that it's been made or just something more complicated where it compares the documents and see which values have been updated and issue that as a new document. And the revision is used like from couch to be for application. Yes, sure. Yes. Yes. Yeah. In a similar way, when MongoDB does replication has its internal things that work sort of in the same way. So I should mention that, oh, this is my second PHP example. I had forgotten I had two and it doesn't particularly matter. Basically what I'm doing here with find one is I find a document Derek R then update the data locally. I said the steps made key, the key 2017, zero, two, zero, two, and then I out 7,000, 124, two, this number and then I updated again with the document. This is not an atomic operation, right? Never, ever do something like this. So what you can do in stats is you can use update operators. So what this example says is that we're going to find all the documents that person equals Derek R, which is hopefully unique. And then we're going to increment the value that we have stored in steps made dot, uh, the, two, zero, one, seven, zero, two, zero, two, which is the dot operator just like in the Jason field teams that I've showed you before is an array differencing operator and we are in 7,124 to that. So this is one of the atomic operators that sort of like Redis had with the H incubi right. It's a similarity. Also, if you want to do sets in MongoDB, you can do the same thing. So you can do all to set, which is Redis set at or as at to the field tax. We then out the value of the tech. And again, you don't get duplicates in it because it's a set. You can also do is you can do push, which is very similar to out the set, but it doesn't care whether you have duplicate values. So what I'm doing here is I'm all in two tags MongoDB and XD book for example, and then I have an extra argument to this, which is called slice minus two, which basically says only keep the last two in the array. So the sort of allows you to keep only the last 10 tags that I've stored and things like that as an atomic operator. And of course, most of the day to a very many more of those atomic operators, but again, I can't really talk about it because I don't have enough time. So then the last thing I was, I was slightly want to touch on is that in a relation database, you of course aware that you have to create your scheme up front, right? In most of the knows call solutions, you don't really do that. You don't really do that by configuring it in the database. You do that by thinking about is really, really hard and then thinking about it once more to make sure that the application knows how to deal with the data, which is a very different thing than you do with relational data. It's very traditionally, you always start out by how can I store my data in the most efficient way, right? You create a schema according that you have no data duplication and so on and so on. Now I realized that that doesn't always work very, doesn't always work as well as expected to do. So you do denormalization. Now knows call solutions or document data stores, you don't tend to do that. What you tend to do is you create your schema, at least logically, depending on how your application interacts with your data. So that determines how you store your data. That often means that you end up storing some duplicate data, but that's all right and you shouldn't be too afraid of that. And it's a common, well, I wouldn't call it practice, but it's not a bad thing anymore. It's important that your application can interact with your data in the most efficient way, not necessarily how efficient it is to store your data. And you end up making trade-offs here, right? Because some database schemas are going to be really good for retrieving data really fast, but it sometimes can be more cumbersome to update because you have this duplicated data and you'd have to update it in multiple locations. So this is something that you need to think of when designing a schema, how this works. Now, to look a little bit at schema validation is that most NoSQL solutions will not do anything for that. It tends to be pushed to the application and that is also true for the relational databases where you have h2j and jcb. I'm sure that PostgreSQL has some functionality that you can enforce a little bit more on it, but it isn't going to be as rich as enforcing that on normal schemas in normal relational databases. And in NoSQL databases, generally, also not possible to do. Many people use ODM kind of patterns on top of that to deal with this data. I just want to show you MongoDB because it does have something like this. You can actually configure per collection a way to enforce a schema. This is a very basic way of doing it. What you can control is that specific fields need to be of specific data types or fit in specific values. I didn't find that. I couldn't find that, but I believe you when you say that. It's a very basic way of doing this still. You can't, for example, say that we only want these fields in here. You can't exclude fields from being stored in the database and stuff like that and so on and so on. But hopefully that's coming in the next release of MongoDB 3.6 where you'll likely have full support for JSON schema, which is something I'm going to expect to see coming in PostgreSQL and some of the other NoSQL solutions as well because it's such a nice way of defining a schema for sort of free form data, which is I think a useful thing to have. To draw to conclusions before we have some time for some questions, the way non-scaled data is stored in the other databases is quite different. In Redis, it is stored in only sets or hatches. In the document data stores, it tends to be JSON documents. In traditionally relational databases, you have fields where you store JSON documents and you tend to interact with that as there are strings. All of those solutions are going to be valid, all depending on what you want to do with your data. If you already heavily invested in relational database like MySQL and PostgreSQL, all of these JSON or JSON or HSTOR type is a valid way of doing things. If you want a full-text search index, want to search on natural data, elastic search is going to be the best thing. If you want a very fast caching key value store, a key value store like Redis is going to be a thing. If you have need for storing more complicated documents, which is sort of how your application would interact with this data, like the products catalog that I showed you before, a document data store is going to be your best bet of picking. There's lots of different things that you need to think of by picking the right solution. It is a very difficult thing to say which one is best for which use case because it is all depends on which data you are storing and how you interact with your data. I can't really give you a real, if you do this, then you have to pick this kind of database because there is such a vast array of them that you really need to think about how you do this kind of stuff. But things you need to think about is what sort of the format your data is in, how you interact with it, how you want to find data back and this is where the indexability comes back out of it and when you want to do data manipulations. As an example, elastic search doesn't really do updates of documents. You can only replace the whole document, for example. Whereas with both Redis and relational database, you can tend to want to update specific fields. But if you want to update a JSON-B type field, then because this is a single field, it is more difficult to update specific values of the sub fields in there. Although you can do things like that with PostgreSQL. So yeah, it is a very complicated thing. What I hope to show you here is more what all the different things are out there and sort of the difference between them more as a hint for you to go look at them in more detail. Because as I said, to start the presentation, if I want to talk about all of these things, I probably need about eight hours, which I don't have. So with that said, are there any more questions? Go ahead. If there are a difference between the JSON fields in MySQL and MariaDB, are there the same? Are there differences in MySQL or MariaDB, JSON-B types? I don't believe there are. And if there are, they are going to be very minimal. Said that, I would definitely expect MariaDB to be further along adding more of these features than MySQL would be. Because yeah, that's my feeling on this. But I have no specific evidence to us. Anything? Yeah, go ahead. Just in addition for choosing the right tool, we're using Redis, Cachete V, Elastic and Postgres. And choose your tool very wisely and think about backup restore. Yes. So all the NoSQL stuff, it's like all those Hips, the tools that anyone would like to backup their data. So you need external tools, scripts, whatever, to get the data out of your database again, which is a very natural thing for Postgres to do. You just do a PG dump and everything. Yeah, but MongoDB has the same thing, right? It has MongoDump. Yeah, but it comes with the database. How does Elastic search have a tool to dump it? No, you can snapshot by re-index. But MongoDB has that, for example. Sorry, yes. Arguments don't really work well for the streams. Clearly, you need to think about backups, but there are different tools that do that in a different way. I only know how MongoDB does this really well here, because I'm most familiar with that. There is a MongoDB dump tool. It comes with a database. And look at the backup streams you store. But there are other ways of doing it as well, right? Because the NoSQL solutions tend to be more distributed systems. What is also a common thing to see is that you have an extra node that is there just taking the backup. And having backups of a distributed set is often less necessary. Not saying that you shouldn't do it. You should always have a backup, but it's less necessary because you're going to have multiple copies of your data in the place. But things like Redis, because it all runs in memory, there's not really a way of doing it as a dump to disk. There's functionality for doing that, but it's not what it's made for, right? So you need to take those things into account or something. That's all about what I want to say about that. Anything else? No? All right. Well, in that case, I have one more slide that says, thank you very much. I am going to upload a slide to this year-all. I will probably do it in the next few days. Not only can you find the slides on this year-all, I also have a list of resources that points to some of the research things that I found while making the presentation. If you have any questions, also feel free to contact me. I'm more than happy to answer questions or integrate comments if you have them in the presentation as well. So with that said, thank you very much and enjoy the rest of your day.
 Welcome this morning. I'm Derek. I'm Dutch breakfast long story. And most people tend  to know me because of PHP stuff. I wrote some things for that. I won't be talking about  that whatsoever, but that might be an example that uses PHP code in one of these slides.  I like maps. I like beer. I like whiskey. And you'll see those items coming back in the  examples that I'm using. It's a bit, well, besides the maps, the beer and the whiskey,  it's a bit too early for that if you ask me. All right. So what are we going to talk about  this morning? I'm going to talk about non-scaled data. And first of all, I'd like to sort of  define what I mean by this. Traditionally, what you would store in a database, you have  like fields and you have values. And those values tend to be a single value, right? So  there can be strings or numbers or booleans or whatever thing you come up with there.  And then you have non-scaled things, which are things like arrays and nested objects,  arrays of objects and so on and so on. I'm sure some of you have in the past stored a  comma separated list of values in the database field at some point, right? Just raise a hand.  Yep. And there's people not paying attention or lying. Always the case. So yeah, this is  the thing, right? The non-scaled data aspect, you see a cliché up quite a bit more in many  relational databases. You couldn't traditionally do anything with that. So a few examples.  So a few examples here. If you have an article, you want to store some text with that, right?  Well, you ought to say that there's articles about Java and PHP and databases. So initially,  what you'd have to do in a relational database is you have to have this table that defines  all your text. Then you have to define the table that stores all your articles. And then  you have this link table that links the articles to the text, right? So you get three tables  for doing that. But that's not particularly very efficient because you need to do like  a two-dip table join any time you show an article. Now, other examples where this becomes  sort of important if you have things like a set of properties that you need to store  with something. A traditional example here is using like a big product catalog, right?  So if you have a product catalog, most products will have a name in it. They will have a price  in it, right? So those are the fields that are the same for almost all things. That wasn't  me. No, that wasn't me. I don't know. We'll keep an eye on that. So yes, you have the  set of properties that are always going to be the same for a product and you have a set  of properties that are going to be different. Like for books, you have an author and a number  of pages, right? Whereas for whiskey, you'll have how strong it is and where it is from  and how heavy it is for shipping and things and so on and so on. So there's a very different  set of properties. Traditionally, in a relational database, how you would store those is you  would store them all in very complex pattern called EAV, which is an entity attribute value  relationship. It works well, but it is not very easy to find back things in the database  when you look at it yourself. And it's kind of a complicated thing where you need to multi-table  joints to get all your properties out of it. Unless, of course, you normalize it, but that's  something. In a relational database, you don't do it. So those use cases here are the sort  of data things we're going to have a look at, but not with books. So what this presentation  is not about, I'm not going to talk much about scalability or high availability or benchmarks  or I don't have the time to go in that covering all the mentioned technologies here because  I probably need a whole day for that. I only have 57 minutes left. All right. So the things  we're going to look at is database type for non-scaled data. We're going to look at some  query and manipulating data sections and we have a bit of a recap at the end. We got started  with that. First of all, let's have a look at different database types. Now, you're probably  familiar with relational databases, MySQL, PostgreSQL, Oracle, IBM, DB2, MSS, SQL, one  of those, right? Probably should know. But in the last, say, what, 15, 20 years, a whole  bunch of other different types of database that are also on the market often put under  the no more no SQL, which I don't think is a particularly good name for them, but basically  non-relational database is probably a better way of describing that. So there's three groups  I want to look at. There's many more groups. If you look at Wikipedia, there's probably  80 different categories that people come up with. That's, I mean, overdoing it a little  bit, I think. But to look at the most simple thing that you have here is usually a key  value store. And a key value store is, it works really simple. They tend to be used  for caches because they are so simple. Lookups and storage is only done on the one specific  key and you can't do operations on anything else besides this key. A good example for  this is RADDIS. And in addition, I forgot to mention point three here, is that the values  that you associate with those keys, they don't have to be just single values, right? They  can be lists or hashed as well. Go back into those in a moment. So the keys in RADDIS are  binary safe strings. The values are safe strings. But the strings do a little bit more than  just strings sometimes. But besides the single, the scalar value of strings, they also can  store hashes, lists, sets, sorted sets and some others, which I'll show you in a moment.  The interaction of it goes through RADDIS CLI or PRADDIS PRADDIS, which is a PHP extension  to talk to RADDIS. The RADDIS protocol is particularly quite simple. It is still a binary  protocol, so you do need a little bit of logic to talk to it. But the RADDIS CLI tool that  comes with it is very simple, and I'll have an example of that in a moment. And the normal  strings are sort of special as well, because you can treat them as numbers, and I will  then use them as numbers instead of just pure strings. So let's have a look at one of those  of data types in. And I said before, in key value storage, the only operation you can  do on a key, right? So if you want to store, say, properties of a specific product, in  this case, the whiskey Glenfiddler, and I want to do something with the property tags, what  I actually construct myself is this key, convention dictates that you use the colon in between  the different things in here. So we have the product category whiskey here, we have the  name or the slug that we have here as Glenfiddler 12, and then we have the property tags. So  you build this key up yourself because as being a key value store, there's no other way of  doing that. There's no multi-column keys. And then with specific operators, you can  add tags to this. So the first one we do as add stands for set add, we add the tag fruity.  And then the next one, we add vanilla to it, and then we add fruity once more. But because  there's a set of data, and the set only contains the same values and not duplicate values,  we're not fruity again, I won't do anything with it because it's already part of the set.  And then if you want to do things with spaces in there, you need to use double quotes. Then  when you do a query, you can do many different queries on sets. You can do test whether the  set contains a member. So when we set, well, if there's a tag pt set on this whiskey, return  one or zero. And of course, because we hadn't set it, we get a zero back out of it. So it  means false, not part of it. And of course, you can query all the members of the set.  And that's probably wise if you sit on that side of the room. I'm just warning you. So  those are the sets. Now, an additional thing to this is hashes. And they're working a little  bit of a slightly different way because in a hash, you have key value pairs that you  store with a property. So to go back to the example that I have about a product catalog,  if I want to store multiple properties, what I'm doing here, I'm storing for the whiskey  Ben-Nevis 19. I store in the props field, I will store the key value combination distillery  Ben-Nevis. So it says basically, the distillery for this whiskey is Ben-Nevis. You can also  do HM set, which is that's what hash multisets. And basically, I'm setting two properties  here. The first key value pairs region, Scotland, Thailand. And then the second one is H19.  And that's how you specify the non-command line. Of course, when you put things in the  database, you can get things out of it again. So you get the hashes out of it again by using  hash get all, which gives you all the key value pairs that are in there in A array, starting  with one. I don't know particularly why they chose that, but that's how it works. And of  course, you can get values for specific properties as well. And I have to mention here that although  age, we stored like 19 as a number, right? It comes back as a string because the values  are always going to be strings, but some exceptions. So that's what Veritas does as an example  of a key value store. And there's all the examples, of course, as well. So slightly more complex  data, data stores are the document data stores. They're often more richer data types. You  can often do operations on both keys and values. In many cases, when you see documentation  for these document stores, all the examples will show you the data objects in JSON. That  does not mean that those things are actually stored as JSON in the database, but it's often  a way for communicating it or at least visualizing the data that goes in and out of it. And examples  of this group are the ones that Lisa will be showing here is MongoDB, couch will be  in Elasticsearch, but of course, there doesn't more than these three here.  All right, so let's have a quick look at MongoDB. It's an open source product, just like all  of the others really, so it doesn't really particularly need mentioning. The documentation  is often using JSON, but it stores the documents as something called BISN, which is binary JSON  on disk. The interaction with those documents is often through language-specific data structures.  So if you are using Java, you build like the documents of like a Java builder kind of pattern.  If you're using PHP, you can use PHP arrays and objects to store the data. And the driver  will automatically convert us to this BISN layer under the hood. It's not something you  have to take care of yourself. The interaction, however, is a bit more complicated than with  Redis because it is now a binary protocol, so you need an extension for languages to  interact with that in PHP. It's called MongoDB, but there's drivers for any other language  you can think of or so. So the interaction is a bit more tricky, but it makes it easier  to deal with from your language because it's a natural way of dealing with data in there.  The documents are a bit more complex now because we have key value combinations, right? So we  have at least MongoDB, there's always this underscore default, which is your primary key.  It's immutable, so you can't change that. And then you have a whole bunch of properties,  and I just want to point out a few. Like the words one is an array of words, and that is  the native data type in there. It's an array of words that you can do queries again, see  them. And then batches is a bit more complicated as well because it's an array of like objects  again. So not only can you store just an array of things, you can store an array of those  nest documents. And this is basically the same about all the other document data source  they store something very similar as you'll see in a moment.  So this is, I think the only PHP example that I have, but it should illustrate enough how  this sort of works. If you want to insert something, you create a connection to a database  and a table name, which in MongoDB we call collections. We insert two documents. As you  can see, they're just simple two PHP arrays that we insert. We treat them as key value  pairs. And then with insert one, we insert one document, and then insert many, we do  many. I mean, it's not particularly very difficult. And all the languages, it works  very similar ways. Having a look at CouchDB, it's also an open source of patchy products.  The documents are JSON objects. And that is also what you talk over the wire. So CouchDB  exposes a REST API, which you can talk to with curl, or if you want to use an HTTP  peak line, there's things like guzzle and all the things. So it's not a binary protocol,  it's simple, very much a REST API, which is handy because you don't have to divide specific  language drivers, but then it's also not a binary protocol, so you end up having more  data over the wire. So that's a little different trade of that, mate.  So to have a look at the documents, as you can see, this is exactly the same document  with one exception, is that every document, so they have this unique ID, but they also  have this underscore REST stands for revision, which is important. We'll see later when we  update documents. And this is something that is generated by the database for you. The  number one, it just increases every time you update a document, and then you have a hash  describing the contents of the documents. So that's CouchDB. Inserting in it is not very  hard either. I forgot to mention that both MongoDB and CouchDB, you don't necessarily  have to create a database, they'll just get created for you without having to do anything,  which is kind of handy. So that's what I'm doing here, right? I'm posting in this demo  collection, I'm posting with a unique key, which is in this case, Derek at localhost,  as my underscore ID value, basically, telling that it's a JSON, and then I give it a JSON  string containing information. And you can see here that the underscore ID is not in  here because you don't have to set that. And a return value that you got back includes  this revision number, which is important to know. And I'll show you why later.  All right, elastic search, again, is a very different thing. It is not so much a database,  it is more like a full text search engine, but it is also sort of a database because  you can store documents in it. They use fancy words like near real-time text search engine,  basically what it means is that as a database, if you store new documents in it, it takes  some time for the index to parse this document and add them to the full text search index.  That is often really fast, but it isn't instantaneously, whereas in other databases, when you store  something, you have it immediately available through the indexes, right? So that's slightly  different here. It is based on Lucene, just like Solr is, and Lucene is more, I think,  originally, a Java-based storage engine that knows how to store full text search information,  basically. And as many other tools like elastic search have been built on top of that, and  probably one of the selling points of elastic search is that it's very easy to cluster.  Actually, it is so easy to cluster that you sometimes end up doing it without wanting  to do it. About a year and a half ago, I was speaking in the user group in Amsterdam, and  I was borrowing the afternoon an offer of somebody, and I was giving a slightly different talk,  but it involves elastic search, and it connected my laptop to the network, and it started replicating  somebody else's data because we're also playing with elastic search. I mean, it wasn't important  data, but it started replicating it. Luckily, they have now changed their default configuration,  so that doesn't do that out of the box anymore. Sorry?  Yeah, it's changed now, yeah, absolutely. Yeah. Yeah, you now need to configure your  cluster key, I think they call that. That's not a problem anymore, but at that time, I  thought it was quite funny. But it still illustrates that it is very easy to cluster because you  just start all the nodes up in the network with the same configuration name, and it'll  just start replicating. So that's really handy. So they don't, as I said, because it's  not a traditional database, they don't really call them database and tables. They tend to  call them indexes and types, and the type is basically a collection where there's lots  of common fields, although you don't necessarily need to have the common fields because they  are being no scale solutions. Again, you interact with this with JSON objects. You can do it  with the REST interface or with some helper extensions or libraries, in this case, that's  elastic search PHP, which is something that the people from Elastic Search have written,  and it's there for all the languages as well. Again, elastic search documents. Do you see  any differences? Nope, I don't either, so that's good. And you can insert this in, yeah,  again with curl or REST API back into the database. The curl URLs were a bit more complicated,  but other than that, it's practically the same thing, right? There's not much difference.  So one thing that elastic search doesn't really do is, again, that doesn't mean, what it doesn't  really do is update documents. Anytime you do updates, it basically replaces the document  and has three index the whole thing as well. So we spoke a bit about key value stores and  we spoke a bit about document data storage, but we also want to have a quick look at relational  databases because I thought, well, those document stores with all those properties that could  get kind of handy, and we want to have this functionality in a relational database because  lots of people already use them, right? That makes sort of sense to add some of the properties  from the document stores and key value stores as part of, in this case, I'm showing you  Is MySQL and Post-SQL. Of course, other database will have different things, but let me just  talk about these two. So the first one to have a look at is MySQL, which has support for a  JSON type. I call this very basic because the interaction with it are very different  from what Post-SQL allows you to do. They're also working on something called MySQL document  store, which in my opinion is not a great name because the only thing it really allows  you to do is, it's like, crud operation, like create, remove, delete, and delete. No, read.  That's what the Airstand, where it's not both, remove and delete because that would be a bit  silly. So it's a very simple layer built on top of MySQL that allows you to interact  like it as it was like a NoSQL-ish data store. I don't think it's been released yet, but  this field goes really quickly, so the slides might be out of date a little bit, but I don't  think it's been in a, this has been in a release yet. So this JSON type, the interaction with  this is through strings containing JSON, which is a little bit different than what the rest  APIs for Couch, TV and MLS exerted us because you still need to construct this as part of  a SQL statement. The storage, again, is better than just storing JSON data because that's  kind of pointless to search and things do with, right? And the manipulation is with specific  SQL operators. They are usually, luckily, part of the standards, so at least between  MySQL and PostgreSQL, they're practically the same. And one of the properties that my  phone and website said that the field order in these, in the JSON type for the fields,  isn't guaranteed, which is sort of the case with the other document stores as well, but  they tend to not do anything bad about a meter. So in general, they should be alright.  Just to have a quick look at how to store something like this in MySQL. So if you look  at on the MySQL command line, you can do create table uses and you have the JDOC field and  we call this the JSON type. And then in there, you can insert this JSON document. As you  can see, I used a single quote around it, difficult to see, and then with all the fields  in there. So you're inserting this string as it was a JSON document, which means you  need to be careful about escaping at some point because the SQL itself needs something  to make sure you're not escaping. For example, if you had a quote in one of the fields here,  you need to make sure you escape it because it's part of a string.  PostgreSQL has a few different data types in there. They have something called hstore,  which is very similar to a key value kind of data type. They also have a JSON type,  but I won't be talking about that because they basically don't want to recommend it  anymore. The JSON data type is much better anyway. So there's no need to use the other  one particularly anymore. And the query syntax is, and the index support is quite a bit better  than my SQL, so postgreSQL. So again, let's have a look at hstore first. So hstore is again  a column type, which is a list of key value pairs, and there are both a string. So it  doesn't support richer data types here than just scalar values for the hstore field, I  think. That's probably true, but I did not try that. Yes, I did not try that. Okay. I'll  give that a shot later. Okay, good. Okay. I'll play with that. Let's pretend we have only  the simple set of document, right? So yeah, what is important that I said is that even  though you store numbers and booleans in it, there are still strings. So you can't do particularly  queries against that. And what they style is that it is often a field that is often  not queried, although you can index this, for properties that are not necessarily part  of every document. So for example, as I showed you with the product catalog, it could be  those extra properties. I do quite a lot of stuff with open stream map stuff, and open  stream map users postgreSQL, and I do quite a lot for starting map data. So there's lots  of descriptions of objects in there that create a style of the map, but you can't necessarily  store all the tags that objects can have because it's an unbound set of tags, and the way how  the rendering of those maps work is that for every tag that you put in their schema, in  the rendering schema, you have a specific column, and of course you can't have an unbounded  set of columns, right? So what they often do is they have this set of properties describing  one document that are often there and necessary for styles, and if all those stuff is hardly  ever used, they put it in an h-store, so you can still do something with it, but you lose  performance, but it allows you to store all the things that you want in there. So that  is sort of the use case for that. The JSON-B type is similar to MySQL's JSON type, but  also quite a bit better because unlike MySQL, you can actually index it, and it supports  better or richer data types in the h-store because it supports all the JSON data types,  really. The column type is called JSON-B, and yeah, we have now this richer, well, you  might remember this JSON document from before, because it's practically the same that I've  inserted in both Elasticsearch and CouchB and MongoDB, right? So it looks like this.  So after inserting data, we need to sort of query data, because database are sort of useful  to get data back out of again, and yeah, I already sort of showed you how to do it with  Redis, right? You can, in sets, you can check for whether a key value is part of the set.  You can retrieve all the members of a key. The hashes, you have hget all to get all the  information out of a key, or you can do hmget, which you get a set of keys out of the head,  hash, and I didn't show you the lists, but there's also like lists that you can set ranges  from that you want to retrieve and stuff, and there's all the data types too, but there's  many more like this. So that's how you query there, or of course you can request the whole  property by its key. So in CouchB, queries are primarily done through the underscore  ID field, which is the default. Now, it is possible to set up secondary index and secondary  keys as well, but you do that to something called MapReduce, so you need to define those  views upfront. It's clever enough that it doesn't have to recalculate a view every time  you insert a document, because it stores the keys any time you insert or update a document.  No, it doesn't do that?  It does an incremental update of the index once you query it.  So the insert is cheap?  Right. The first query is slow.  It updates incrementally the view.  I did not know that. Well, thanks for that.  So yeah, if you do look up on that, you tend to do it only a primary key. Now, I have to  say CouchB is not the database in our best in here, but that should be all right.  MongoDB allows for doing queries without having setting up secondary keys upfront, so you can  do those queries right away. This is a simple query that you look up, which is basically  what we find out once again, that wasn't me. I find out all the check-ins where the, there  we go, region slug matches Scotland Islay, and the rating is larger or greater than three,  and we only want to store the whiskey rating in H fields.  So this is the quality match. There's this match with a query operator, and there's many  more than just GTE, that's for greater and equal. And there's a projection. Basically,  what it says is the following as go. That's what it says. Select whiskey rating in H  from check-ins, where region slug is Scotland Islay, and the rating is larger or equal than  two three. So what like you mean this morning?  The stream doesn't help the people here, does it?  Okay. There's also another way of doing queries is something called an aggregation pipeline  where you run different operations on a whole collection ago. So in this case, what we're  doing is we want to find all the documents where the region slugs start with Scotland.  It's a regular expression operator. And then we do a group by region. So basically this  says, find me all the whiskeys in the region Scotland, and then group them by specific  subregion. So something you get back out of this. Okay. I said those things already.  And then you get something back like this. So you have the ID fields, which is the name  of the region. And then it has an array of the whiskeys that come back out of that. So  it's a simple group by that you do here. And of course, unlike your relational database,  it actually just returns the array here instead of doing a single record per, single record  per return, basically. All right. So elastic search has two different ways of doing queries.  You can do it on its primary key, or you can do this on a field value combination. That's  a simple way. So here we're searching in users count 16. So that's key value per count has  to match 16. And you get a result back out of that. But very elastic search really shines  is, of course, its full text search capabilities. So you can construct queries like the following.  Like it's a Boolean query, and they have all the queries as well, like just Geo queries  and a whole bunch of others. And it says the documents that I find should match, the set  of equals pandering, and then a match risky should be sherry. And in this case, they both  need to match. But you can construct queries where there's an or query or a Boolean not  query. And it's so much richer than I can show you here, which is really, really good  for building full text search index, like having a search box saying, well, give me  all the documents or all the products that match these keywords and stuff and so on.  It, yeah, that works really well for that. Quering JSON objects in myScrown, I found  bit complicated because there's new SQL operators that I didn't really know in front. So if  we have this table where we store the name of a whiskey and the properties, in this case  we store the properties age, cost, strength, and ABV or colored. So the last one isn't  shared among the documents. We can construct queries, say, select name and age for all  the whiskeys where cost, strength equals true. And that's what you get out of it, right?  So cost strength in this one is true. And what we want to get out of it is the name  of the whiskey, which is there, and then the age as a value. I'm not sure why it showed  only a single arrow here. I'm expected to show two, really. In any case, what I want  to point out is that the way how you match against subfields is by using the dollar dot,  that is the root of your document, then you have the field name. And if you have nested  field names, you can use another dot and then another field name to match against these  queries. The difference between arrow and double arrow is that this escapes the, unescapes  the JSON value, whereas the single arrow does not. In this case, that doesn't show  a difference because of the scale of value, but otherwise you would have seen that.  All right. So the age story, you can also query a slightly small example here. So what  you can do is get a value for a key. You can, again, use this arrow operator. So find me  for every document in the table, find me whether the is admin field, the value of the is admin  field. And then you get that back. You can find all the unique user count with larger  than 10 by, again, using a finding from the age doc, the unescapes default, which is there  could localhost where the count unique is larger than 10. So then you find the documents  there. So you can do queries on this, on this fields. Quite well. You can also check  whether keys exist, which of course you sort of need now. And you can match against key  value pairs as well. So let's have a look at indexes. So Redis is primary, only looks  at the key. The only key is your index. That isn't quite true because the data structures  that you store for each keys, like sets and lists, they are also optimized in its in-memory  base index. So that is sort of an additional index to it, but it isn't something that is  exposed. So this data structure can provide you some with additional indexing. In CouchB,  indexes as I said, there's by default you have the primary key being indexed and then  you have the secondary keys that you can construct with MapReduce. MapReduce is basically a way  for every document that you have. It will emit something, key value pairs, which you  then can use to do queries against it. It's kind of tricky to explain, but it is sort  of how it works. The output you get back from that is also sort of defined by the view because  you define the sort order in there as well. You construct a new document. Yes. So what  I've done here is, so those secondary indexes you store in something, I believe it's called  a design document as part of the database and then you can do queries against those  views. So that's why the word design pops up when you use the query. So I've only showed  you really the map function. Basically what I'm doing here for every risky document that  I have, I emit something based on a region slug and then a slug of the document. When  I do a query against this then, against this design document in docs, I do the view by  region because that is what I named this design document and then you get a result out of  it. So you get all the rows. So you have the key scull and the full ID field because I  emitted that and then the value, because that's the one that comes out of the document.  And you do queries against that. It works quite well, but you need to think upfront  very clearly on which bits of data you don't do queries on, which is something you don't  really need with a relational database with the cave yet, of course, that if you do a  query on something that you have an index, it's going to be slow. And that is basically  the case for everything that you have an index, right, in any database.  Think of it as a post-gres computational index.  I'll get back to the MySQL version of those in a moment. So in MySQL, you can sort of,  different words starting with an M. In MongoDB, you can do indexes, as I said already there,  on the underscore ID field because that's unique primary key. So that's on there. You  can set up, of course, indexes on keys as well as you can set indexes on nested fields.  You can do that in CouchDB in a few years as well. Of course, post-gres call, you can  do this as well.  And what is kind of useful is that if you set an index on a key that has an array of  values, you can actually do an equality match against words. So you can say, find me all  the documents where words matches cadence heads, for example, and then it will still find this  document. So that's kind of handy.  Full-text search indexes get very complicated if you want to. So because this is a very different  database, the indexes that you configure full-text search indexes. And they are meant for querying  natural language. So they have lots of index functionality for language analysis. So you  can set up different ways of how strings of text need to be separated. Often you do it  by space, but that isn't necessarily the best way of splitting up a string into words,  depending on what your subject matter is. You can set up a language and language rules  then define how words are being stemmed. So if you want to, in English, you want to have  the words walks and walk matched at the same time you configure a stemmer for English that  allows you then to match those words directly on top of each other. You can do stop words  that are not part of your index. So words like the and the kind of pointers in your  index, right, because it's not something you're ever going to look up on. And they take up  lots of space in the index because it uses often. You can boost specific fields to make  them more important, et cetera, et cetera, et cetera. I mean, there's lots of stuff in  there. The indexes, as I mentioned before, asynchronously built. So it sometimes takes  a little bit of time before you can find a document after you inserted them. In Postgres  School, you have something called a GIN index. You can think about GIN as a solution later  if you want to. I just like the image. So there are two types of indexes for JSON.  B fills a key value indexes or a value indexes. Actually, I don't think I have an example  showing you how to create the second one, but it shouldn't matter much. So again, we're  inserting this whiskey in here. We have a key value match. After we create this index,  and well, that GIN index in very simple words does, it looks at all the key and value pairs  and store them in an index. So whenever you match against either of them, it can find  back the documents out of this in quite a simple way. What it doesn't really allow you  to do is create an index on specific subfields. You can only index the whole data type, which  is different than what couch to be and monger to be and to a lesser extent, a less exerted  it. But in most cases, it's pretty good, right? Again, you see this and present, I don't  know, I don't know the name of that operator. But you can match against key value pairs  again in here. You can also check whether just like in monger to be where I showed you  that the words have a value called L1 in there, right? So we have the words here where you  have L125. So the GIN index is a bit better than just collecting all the data like a full  certain index, but it does this kind of things as well. Yeah, go ahead.  From what I believe so, yes, that is the case. Yeah. So I could probably build an index with  a functional index. Yes.  A JSON number cast into an integer and put that as a key into an index.  Yes. You can do that. But as I said, I can't talk about all the functionalities in your  databases. But yeah. So the annoying thing is that MySQL doesn't do any sort of this  whatsoever. The only way how you can index those JSON documents at the moment, as I  said, this field moves quickly is only through generated columns, through virtual generated  columns. So if you want to index on, say, the ABV, the L-combo volume, what you have  to do is alter my table whiskey other column, ABV float generated always, being always there  as properties ABV. So that plicks out this ABV field out of the properties JSON fields,  create a virtual column out of that, and then you can put an index on this virtual column  to index that. Which is what I'm doing here. We create this ABV index on whiskey ABV, which  is the value I plucked out of it, even though I hadn't stored that in the document itself.  You can make this a materialize you, but I don't believe you have to, actually. So yeah,  that is currently how MySQL does that. So the last thing I want to talk about quickly is  manipulating data. And the important word there in any database that you have is atomicity.  You want to make sure that no two programs or scripts or threats at the same time can  update data. Traditionally relational databases have like this asset compliance kind of thing  that is not something that NoSQL solutions often do, but have different ways of making  sure that you don't update data incorrectly. So important words to remember is that you  should never retrieve a document updated in your client and then store it back again,  because that's a very dangerous thing to do. You get really loopy. So how Redis does this?  It has like special operators that you can run. So if this is the start of my document  that starts so that we have these properties again, where the age is 19, you can run hincby,  which increases the value of a specific property by the number that you specify. So what this  says is increase the property h in this key by one. And here Redis does something more  than just strings because it knows how to add one to the string 19. And then you get  it back as a string 20. No, you do not get one. That'd be mean. I don't think it does  hexadecimal actually. So that is how Redis does it. It has specific operators to operate  on this data. And basically doing a set add is a very similar thing that I showed you  before. You can also do queues like using lpush and lpop that push things on the front  of an array, pop them over to start of an array. So you can implement a kind of queuing  protocol with that as well, all using this atomic operations. Yeah, one more. In CouchDB  it is, say we already had this document with the key Derek at local cylinder and we would  do, we want to store this again. It tells you, no, can't do that because we already  have a document with this key, which is kind of annoying because how would I update this  document, right? Because if I want to update, I need to store it with the key again. So  the way how you can do that is this is where the underscore rv thing comes into play. What  you need to specify for a beta work is the rv field that you have gotten back previously.  And the cool thing about this is that if you do this at the same time from two different  threads, only one is going to work. You also know which one is going to work because it  will tell you either the new revision number or it tells you the, you get a conflict because  the document that you want to store with the revision number you thought it was is no longer  there because the first thread has completed and that's stored a new revision number. So  this is how to solve conflicts and strongness of the ethnicity data. And then your application  can decide what to do out of that. It can decide to restore the document potentially  probably overwriting the original, save that it's been made or just something more complicated  where it compares the documents and see which values have been updated and issue that as  a new document.  And the revision is used like from couch to be for application. Yes, sure. Yes. Yes.  Yeah. In a similar way, when MongoDB does replication has its internal things that work  sort of in the same way. So I should mention that, oh, this is my second PHP example. I  had forgotten I had two and it doesn't particularly matter. Basically what I'm doing here with  find one is I find a document Derek R then update the data locally. I said the steps  made key, the key 2017, zero, two, zero, two, and then I out 7,000, 124, two, this number  and then I updated again with the document. This is not an atomic operation, right? Never,  ever do something like this.  So what you can do in stats is you can use update operators. So what this example says  is that we're going to find all the documents that person equals Derek R, which is hopefully  unique. And then we're going to increment the value that we have stored in steps made  dot, uh, the, two, zero, one, seven, zero, two, zero, two, which is the dot operator  just like in the Jason field teams that I've showed you before is an array differencing  operator and we are in 7,124 to that. So this is one of the atomic operators that sort of  like Redis had with the H incubi right. It's a similarity. Also, if you want to do sets  in MongoDB, you can do the same thing. So you can do all to set, which is Redis set at  or as at to the field tax. We then out the value of the tech. And again, you don't get  duplicates in it because it's a set.  You can also do is you can do push, which is very similar to out the set, but it doesn't  care whether you have duplicate values. So what I'm doing here is I'm all in two tags  MongoDB and XD book for example, and then I have an extra argument to this, which is  called slice minus two, which basically says only keep the last two in the array. So the  sort of allows you to keep only the last 10 tags that I've stored and things like that  as an atomic operator. And of course, most of the day to a very many more of those atomic  operators, but again, I can't really talk about it because I don't have enough time.  So then the last thing I was, I was slightly want to touch on is that in a relation database,  you of course aware that you have to create your scheme up front, right? In most of the  knows call solutions, you don't really do that. You don't really do that by configuring  it in the database. You do that by thinking about is really, really hard and then thinking  about it once more to make sure that the application knows how to deal with the data, which is  a very different thing than you do with relational data. It's very traditionally, you always  start out by how can I store my data in the most efficient way, right? You create a schema  according that you have no data duplication and so on and so on. Now I realized that that  doesn't always work very, doesn't always work as well as expected to do. So you do denormalization.  Now knows call solutions or document data stores, you don't tend to do that. What you  tend to do is you create your schema, at least logically, depending on how your application  interacts with your data. So that determines how you store your data. That often means  that you end up storing some duplicate data, but that's all right and you shouldn't be  too afraid of that. And it's a common, well, I wouldn't call it practice, but it's not  a bad thing anymore. It's important that your application can interact with your data in  the most efficient way, not necessarily how efficient it is to store your data. And you  end up making trade-offs here, right? Because some database schemas are going to be really  good for retrieving data really fast, but it sometimes can be more cumbersome to update  because you have this duplicated data and you'd have to update it in multiple locations.  So this is something that you need to think of when designing a schema, how this works.  Now, to look a little bit at schema validation is that most NoSQL solutions will not do anything  for that. It tends to be pushed to the application and that is also true for the relational databases  where you have h2j and jcb. I'm sure that PostgreSQL has some functionality that you  can enforce a little bit more on it, but it isn't going to be as rich as enforcing that  on normal schemas in normal relational databases. And in NoSQL databases, generally, also not  possible to do. Many people use ODM kind of patterns on top of that to deal with this  data. I just want to show you MongoDB because it does have something like this. You can  actually configure per collection a way to enforce a schema. This is a very basic way  of doing it. What you can control is that specific fields need to be of specific data  types or fit in specific values. I didn't find that. I couldn't find that, but I believe  you when you say that. It's a very basic way of doing this still. You can't, for example,  say that we only want these fields in here. You can't exclude fields from being stored  in the database and stuff like that and so on and so on. But hopefully that's coming in  the next release of MongoDB 3.6 where you'll likely have full support for JSON schema, which  is something I'm going to expect to see coming in PostgreSQL and some of the other NoSQL  solutions as well because it's such a nice way of defining a schema for sort of free  form data, which is I think a useful thing to have.  To draw to conclusions before we have some time for some questions, the way non-scaled  data is stored in the other databases is quite different. In Redis, it is stored in only  sets or hatches. In the document data stores, it tends to be JSON documents. In traditionally  relational databases, you have fields where you store JSON documents and you tend to  interact with that as there are strings. All of those solutions are going to be valid,  all depending on what you want to do with your data. If you already heavily invested  in relational database like MySQL and PostgreSQL, all of these JSON or JSON or HSTOR type is  a valid way of doing things. If you want a full-text search index, want to search on  natural data, elastic search is going to be the best thing. If you want a very fast caching  key value store, a key value store like Redis is going to be a thing. If you have need for  storing more complicated documents, which is sort of how your application would interact  with this data, like the products catalog that I showed you before, a document data  store is going to be your best bet of picking. There's lots of different things that you  need to think of by picking the right solution. It is a very difficult thing to say which  one is best for which use case because it is all depends on which data you are storing  and how you interact with your data. I can't really give you a real, if you do this, then  you have to pick this kind of database because there is such a vast array of them that you  really need to think about how you do this kind of stuff. But things you need to think  about is what sort of the format your data is in, how you interact with it, how you  want to find data back and this is where the indexability comes back out of it and when  you want to do data manipulations. As an example, elastic search doesn't really do updates  of documents. You can only replace the whole document, for example. Whereas with both Redis  and relational database, you can tend to want to update specific fields. But if you want  to update a JSON-B type field, then because this is a single field, it is more difficult  to update specific values of the sub fields in there. Although you can do things like  that with PostgreSQL. So yeah, it is a very complicated thing. What I hope to show you  here is more what all the different things are out there and sort of the difference between  them more as a hint for you to go look at them in more detail. Because as I said, to  start the presentation, if I want to talk about all of these things, I probably need  about eight hours, which I don't have. So with that said, are there any more questions?  Go ahead.  If there are a difference between the JSON fields in MySQL and MariaDB, are there the  same?  Are there differences in MySQL or MariaDB, JSON-B types? I don't believe there are.  And if there are, they are going to be very minimal. Said that, I would definitely expect  MariaDB to be further along adding more of these features than MySQL would be. Because  yeah, that's my feeling on this. But I have no specific evidence to us.  Anything? Yeah, go ahead.  Just in addition for choosing the right tool, we're using Redis, Cachete V, Elastic and  Postgres. And choose your tool very wisely and think about backup restore.  Yes.  So all the NoSQL stuff, it's like all those Hips, the tools that anyone would like to  backup their data. So you need external tools, scripts, whatever, to get the data out of  your database again, which is a very natural thing for Postgres to do. You just do a PG  dump and everything.  Yeah, but MongoDB has the same thing, right? It has MongoDump.  Yeah, but it comes with the database. How does Elastic search have a tool to dump it?  No, you can snapshot by re-index.  But MongoDB has that, for example. Sorry, yes. Arguments don't really work well for  the streams. Clearly, you need to think about backups, but there are different tools that  do that in a different way. I only know how MongoDB does this really well here, because  I'm most familiar with that. There is a MongoDB dump tool. It comes with a database.  And look at the backup streams you store.  But there are other ways of doing it as well, right? Because the NoSQL solutions tend to  be more distributed systems. What is also a common thing to see is that you have an extra  node that is there just taking the backup. And having backups of a distributed set is  often less necessary. Not saying that you shouldn't do it. You should always have a backup,  but it's less necessary because you're going to have multiple copies of your data in the  place. But things like Redis, because it all runs in memory, there's not really a way of  doing it as a dump to disk. There's functionality for doing that, but it's not what it's made  for, right? So you need to take those things into account or something. That's all about  what I want to say about that. Anything else? No? All right. Well, in that case, I have one  more slide that says, thank you very much. I am going to upload a slide to this year-all.  I will probably do it in the next few days. Not only can you find the slides on this year-all,  I also have a list of resources that points to some of the research things that I found  while making the presentation. If you have any questions, also feel free to contact me.  I'm more than happy to answer questions or integrate comments if you have them in the  presentation as well. So with that said, thank you very much and enjoy the rest of your day.
froscon2017-1942-storing_non-scalar_data_0_47.png froscon2017-1942-storing_non-scalar_data_48_143.png froscon2017-1942-storing_non-scalar_data_144_215.png froscon2017-1942-storing_non-scalar_data_216_371.png froscon2017-1942-storing_non-scalar_data_372_875.png froscon2017-1942-storing_non-scalar_data_876_1115.png froscon2017-1942-storing_non-scalar_data_1116_2135.png froscon2017-1942-storing_non-scalar_data_2136_2963.png froscon2017-1942-storing_non-scalar_data_2964_4907.png froscon2017-1942-storing_non-scalar_data_4908_5195.png froscon2017-1942-storing_non-scalar_data_5196_5831.png froscon2017-1942-storing_non-scalar_data_5832_5879.png froscon2017-1942-storing_non-scalar_data_5880_6287.png froscon2017-1942-storing_non-scalar_data_6288_6503.png froscon2017-1942-storing_non-scalar_data_6504_6719.png froscon2017-1942-storing_non-scalar_data_6720_7991.png froscon2017-1942-storing_non-scalar_data_7992_9239.png froscon2017-1942-storing_non-scalar_data_9240_11747.png froscon2017-1942-storing_non-scalar_data_11748_13667.png froscon2017-1942-storing_non-scalar_data_13668_14759.png froscon2017-1942-storing_non-scalar_data_14760_16343.png froscon2017-1942-storing_non-scalar_data_16344_16595.png froscon2017-1942-storing_non-scalar_data_16596_17507.png froscon2017-1942-storing_non-scalar_data_17508_18323.png froscon2017-1942-storing_non-scalar_data_18324_19151.png froscon2017-1942-storing_non-scalar_data_19152_19883.png froscon2017-1942-storing_non-scalar_data_19884_20939.png froscon2017-1942-storing_non-scalar_data_20940_24443.png froscon2017-1942-storing_non-scalar_data_24444_24611.png froscon2017-1942-storing_non-scalar_data_24612_25475.png froscon2017-1942-storing_non-scalar_data_25476_25727.png froscon2017-1942-storing_non-scalar_data_25728_26267.png froscon2017-1942-storing_non-scalar_data_26268_27635.png froscon2017-1942-storing_non-scalar_data_27636_28943.png froscon2017-1942-storing_non-scalar_data_28944_29975.png froscon2017-1942-storing_non-scalar_data_29976_30671.png froscon2017-1942-storing_non-scalar_data_30672_33611.png froscon2017-1942-storing_non-scalar_data_33612_33947.png froscon2017-1942-storing_non-scalar_data_33948_34487.png froscon2017-1942-storing_non-scalar_data_34488_34667.png froscon2017-1942-storing_non-scalar_data_34668_36959.png froscon2017-1942-storing_non-scalar_data_36960_37679.png froscon2017-1942-storing_non-scalar_data_37680_38255.png froscon2017-1942-storing_non-scalar_data_38256_39287.png froscon2017-1942-storing_non-scalar_data_39288_39383.png froscon2017-1942-storing_non-scalar_data_39384_39959.png froscon2017-1942-storing_non-scalar_data_39960_41579.png froscon2017-1942-storing_non-scalar_data_41580_43571.png froscon2017-1942-storing_non-scalar_data_43572_44711.png froscon2017-1942-storing_non-scalar_data_44712_46475.png froscon2017-1942-storing_non-scalar_data_46476_48347.png froscon2017-1942-storing_non-scalar_data_48348_48935.png froscon2017-1942-storing_non-scalar_data_48936_49727.png froscon2017-1942-storing_non-scalar_data_49728_51767.png froscon2017-1942-storing_non-scalar_data_51768_51983.png froscon2017-1942-storing_non-scalar_data_51984_54803.png froscon2017-1942-storing_non-scalar_data_54804_56243.png froscon2017-1942-storing_non-scalar_data_56244_56339.png froscon2017-1942-storing_non-scalar_data_56340_56987.png froscon2017-1942-storing_non-scalar_data_56988_57275.png froscon2017-1942-storing_non-scalar_data_57276_57599.png froscon2017-1942-storing_non-scalar_data_57600_59111.png froscon2017-1942-storing_non-scalar_data_59112_59759.png froscon2017-1942-storing_non-scalar_data_59760_61559.png froscon2017-1942-storing_non-scalar_data_61560_62315.png froscon2017-1942-storing_non-scalar_data_62316_64439.png froscon2017-1942-storing_non-scalar_data_64440_68111.png froscon2017-1942-storing_non-scalar_data_68112_69875.png froscon2017-1942-storing_non-scalar_data_69876_70127.png froscon2017-1942-storing_non-scalar_data_70128_73835.png froscon2017-1942-storing_non-scalar_data_73836_73979.png froscon2017-1942-storing_non-scalar_data_73980_74351.png froscon2017-1942-storing_non-scalar_data_74352_74747.png froscon2017-1942-storing_non-scalar_data_74748_74807.png froscon2017-1942-storing_non-scalar_data_74808_74939.png froscon2017-1942-storing_non-scalar_data_74940_75887.png froscon2017-1942-storing_non-scalar_data_75888_77711.png froscon2017-1942-storing_non-scalar_data_77712_77819.png froscon2017-1942-storing_non-scalar_data_77820_78503.png froscon2017-1942-storing_non-scalar_data_78504_78623.png froscon2017-1942-storing_non-scalar_data_78624_78746.png