4 ms·
For me, I mentioned object databases a bit, and I think there's still a gap here not yet filled by RDBMSes or NoSQL. The old object databases (now mostly-forgo
by atombender 8y ago
For me, I mentioned object databases a bit, and I think there's still a gap here not yet filled by RDBMSes or NoSQL.
The old object databases (now mostly-forgotten products like POET, ObjectStore, Matisse and Objectivity) were too tightly tied to a serialization format. Many of them wanted location transparency, where your app just worked with "objects", and they happened to be remote and persisted to disk — the same awful design mistake that doomed CORBA and, to an extent, DCOM. Turns out this data model requires a tight coupling between a language's OO classes and the persistence model, and it's also means that code that looks like it's working on RAM objects can't optimize for data paths in the way that SQL can.
Then there were systems like GemStone and Caché, which were tightly coupled to a single language/runtime (Smalltalk and M respectively). Probably very productive, but ultimately a dead evolutionary end.
At this point I think it's possible to have a hybrid database that's good at both relational-type and object-graph-type queries, i.e. to collect a huge amount of structured data with joins etc., but without the tabular requirement.
- scarface74 8y agoWhat’s the difference between an object database and a database like Mongo? I used Mongo with C# and Linq and used strongly typed Linq queries that looked and worked exactly the same whether you were working with an in memory List<T> or a IMongoQueryable<T>. Of course when you work with an IMongoQueryable the Linq is translated to MongoQuery at runtime.
- atombender 8y agoI don't know Mongo or C#/LINQ very well, but I'll take a stab. For the most part, you could probably replicate the classic object database experience with MongoDB, assuming a sufficiently rich language integration. For example, in most of these databases (e.g. POET), you'd do something like "p = db.persons.find(1)" and then call p.save() on it to make your changes persistent. You could also wrap multiple save calls inside something like Tx.begin() and Tx.commit(), and everything in between would operate within the transaction (which of course leads to an interesting challenge where objects "belong" to a specific transaction, and a rollback has to invalidate them). You could build something like this on top of Mongo, I think. You could also do "p.employers[0].board.chairman". The access would be seamless and transparent. Under the hood, it would use the object graph to find and load the right object from the server. In other words, dereferencing a pointer to another object would follow the graph. This requires some language magic. With many of these products, you would write normal OO classes (often C++ and Java back then; Matisse supported Python), and then there were tools to inject the magic persistent code needed to get this level of transparency. The Java version of POET took your compiled .class files and modified the bytecode so that "p.employers" or whatever actually called the database to load itself. You'd have to have a step in your build pipeline to call this tool. I bet LINQ would make this easy. As an aside, for many years none of these databases supported any type of SQL-type query; you were expected to use the graph. You'd always start with a root to "reach" any part of the graph. So you'd do "db.persons" or "db.companies" or whatever; what the root contained would be up to you, usually. But any filtering would have to be done client-side; so if you're looking at a subset of companies, you'd have to something like db.companies.filter(lambda c: c.stockPrice > 10). It could be quite fast since clients could cache all the objects it fetched, and listen to changes from the server to invalidate them. On the other hands, no joins. Later, there was an attempt at standardizing a query language called OQL (which would allow db.query("from company where stockPrice > 10") or whatever), but it was too little too late. It's worth pointing out that many of these object databases prided themselves in being able to keep the in-RAM copies in sync. So if you hold on to that "p" variable, and someone makes a change to the Person object with ID 1, then changes are reflected in your copy, automatically. So the client would listen on a server channel to receive change notifications (similar to MongoDB change streams, I guess). Every time you fetched an object, you'd basically start a subscription to changes on that object. One thing that these object databases had that Mongo doesn't (as far as I know) is strong references. If your person object has a reference to a company object, then you can't delete the company without first unsetting it from the person. This is a good thing.
- scarface74 8y agoC#'s Linq lets you treat queries as first class citizens. For instance: say you have: var customers = db.GetCollection<Customer>().AsQueryable(); Where Customer is a C# object and it can have nested objects. When you right a query: var seniors = from c in customers where c.Age > 30 select c; Assuming that db is a Mongo context object and hover over the seniors variable at runtime, you will see the MongoQuery that is generated. If you then write: seniors = seniors.where(s => s.age < 90) It will add the second predicate and modify the MongoQuery at runtime. The query isn't actually run until you do something like var oldCustomers = seniors.ToList(); If you went in and changed the customers initialization to an in memory List<Customers>, the same queries would still work, the ToList() it would generate actual code instead of a MongoQuery. If customers were a reference to an Entity Framework SQL Server DbContext, the same Linq statements would generate SQL. You can even write a method that takes in a Linq expression: List<Customers> Find(Expression<Func<bool,Customer>>) and call it var oldFolks = Find(c => c.Age > 65) C# translates Linq expressions to Expression Trees and then the Linq provider you choose (or write) parses the expression tree at runtime and converts it to the appropriate query language or for in memory lists, code. You could switch out Mongo for an RDMS and all of the clients using the Find method would be non the wiser. I guess OQL is a lot like Linq but since C# understand Linq natively you get strong typing, support for joins, etc.
- atombender 8y agoRight, this is a bit different and not really seamless (e.g. an object database wouldn't require a ToList()). The ability to express queries this way is nice, though.
- scarface74 8y agoThe idea of ToList(), .Count() etc., is that the query isn’t executed until you use such a function, you can keep composing the query and it will be translated and run on the server instead of the client. For instance.... var seniors = customers.Where(c => c.Age > 65).ToList() foreach(c in seniors) {} Will be translated to a query and run on the database server. var seniors = customers.ToList().Where(c => c.age > 65) foreach(c in customers) {} Will give you back the same results, but you will get back every record in the table/collection and the filtering will be done client side. Btw: The first query could be rewritten as var seniors = (from c in Customers where c.Age > 65).ToList(). You would only do .ToList() if you want to keep the scope of the connection small if for instance you are passing it outside of the class. You can iterate the results directly from the query instead of returning the results in memory (using ToList()) and it would work just like any other database style cursor. EDIT: Just to clarify from my first post, the example should have been List<Customers> Find(Expression<Func<bool,Customer>> predicate) { .... return customers.Where(predicate).ToList(); } This pattern also works great for unit testing. You can mock out your database Context and use a List<T> Customer to store data and you can still test your queries without having a dependency on a database.