I'd like to make a ndb.PolyModel child entity an ancestor of another ndb.Model entity, because I need the child entity to be strongly consistent for queries.
Something about this makes me nervous. Is this a bad idea?
From what I understand, the secret sauce behind a ndb.PolyModel is that instances of it have a special property called 'class'.
This is a repeated property of strings of all the class names in the inheritance chain, which ndb then uses to instantiate the appropriate subclass when it pulls it out of datastore.
The key for each instance will still be of the parent class.
So for the following example:
class Animal(ndb.PolyModel):
class Cat(Animal):
class Dog(Animal):
If you do
d = Dog(...)
d.put()
d.key will still be something like ndb.Key('Animal', 123456789)
In your Datastore Viewer, you will only have an Animal table and not a Dog table nor a Cat table.
So I would imagine you should be safe to use ndb.Key('Animal', 123456789) as the ancestor for some other model.
Related
An example would be I have an array of the abstract class Production which has derived classes of Movie and Series. I have an array in the schema that is a Production[] which can hold either a Movie or Series. I'm validating schema that is being provided to us by a third party. I am only validating, not deserializing so I am validating it with the validation stream.
When I validate that array, it only validates against the properties within Production. It skips any properties that are within the derived classes. How can I get it to validate against the correct derived class properties? I have a ProductionType property on Production which is a one to one match to the derived class.
I have tried adding the derived class schemas to the "AdditionalItems" in the ProductionGenerationProvider and manually validate against it in the ProductionValidationProvider. Did this by value.Validate(matchedSchemaItem). This causes a whole new validation to be spawned off instead of validate it within the context of the current validation.
I also tried using the OneOf property on the schema by passing in the references to all the derived types but it just tells me that it didn't validate against any of the items in OneOf.
Is there a way another way to do this? I assume this is a solved issue and I just haven't come across the solution.
I am trying to figure out best way to model class inheritance in DocumentDb.
Say my classes are structured as
class A
property X
property Y
property Z
class B inherits from A
property W
Is there a built in support to handle this? If I use Lambda expressions in my client code, will they be able to distinguish between the types automatically?
Will the following query only return objects of type B back? Or will it also consider instances of base class A?
var bCollection = from o in client.CreateDocumentQuery<B>(collectionLink)
where X > 2
select o;
Is there a built in support to hand this? If I use Lambda expressions in my client code, will it be able to distinguish b/w the types automatically?
Yes, you can use lambda syntax in client side, as long as you specify the specific type in the generic method, as client.CreateDocumentQuery<YourType>(collectionLink).
Will following query only bring objects of type B back? Or will it also consider instances of base class A?
DocumentDB is a schemaless store and does not store type information. The overloaded generic methods provided in the client are syntactic sugar, to let you easily create queries.
All queries are evaluated against json documents, which do not have type information.
Inheritance scenario
So, if you fire a query for a property which is present only in your derived class, you will get values corresponding only to your derived class. But, if the property you are querying on is in both base class and derived class, you'll get back both results. For example, in your case filtering on W would give you results of only class B, but filtering on X, Y or Z would give you values for both classes A and B.
Classes with shared schema in same collection
Note that this does not just happen in the base-derived class scenario. Same behavior would happen if you have 2 separate classes, which do not inherit each other, but have a property with the same name. Querying on that property will return results of both classes.
For example, if you have 2 classes which are stored in the same collection:
class A1 { int x; }
class A2 { int x; }
Even if you form your query using the client.CreateDocumentQuery<A1>(collectionLink), results of both class A & B will be returned. As I mentioned earlier, the type specification in the client is just to make your life easier while forming the query.
I you want to be able to query different types of data, having shared schema elements, stored in the same collection - I would recommend having a separate property to store the type information manually and filtering on that property.
class DocumentDbData
{
string DataType;
DocumentDbData(string type) { DataType = type;}
}
class A1 : DocumentDbData
{
string x;
A1() : base("A1")
}
class A2 : DocumentDbData
{
string x;
A2() : base("A2")
}
The query, client.CreateDocumentQuery<A1>(collectionLink).Where(d => d.DataType == "A1" && d.x == "xvaluefilter") will now return only data for class A1.
I use node.js not .NET, so I don't recognize the CreateDocumentQuery<B> syntax. Is there a way to inspect the actual query that is sent by the .NET SDK to see if there is something added to the WHERE clause to restrict the results to type B? Alternatively, is there some field like _Type added to your documents without your intervention? I highly doubt the query clause you provided is so modified. I'm less sure that an _Type field isn't added, but I consider it less than 50% likely. My instinct is just that the <B> specification on the CreateDocumentQuery simply casts the returned objects to type B for you. I have no idea if that will cause an error if your result set included class A objects or if it will use null or some other default for missing fields.
Assuming all of the above, you'll probably have to model class hierarchy yourself. I have modeled this in two different ways:
Use a materialized array to indicate the entire class hierarchy for each document. So, ["Animal", "Mammal", "Cat"]. Then when you want all Mammals, you can query with WHERE "Mammal" IN c.class.
More recently, I've switched to having a separate field for each type that something is. So, isAnimal = true, isMammal = true, and isCat = true. This is more of a mixin approach, but you can still model class hierarchies this way.
I am trying to make a replacement VB6 class for the Scripting.Dictionary class from SCRRUN.DLL. Scripting.Dictionary has (among other things) a "Keys" method that returns an array of keys, and a read/write "Item" property that returns the item associated with a key. I am confused about this, because both of them seem to be defaults for the class. That is:
For Each X In MyDict
Is equivalent to:
For Each X In MyDict.Keys
Which to me implies that "Keys" is the default operation for the class, but:
MyDict("MyKey") = "MyValue"
MsgBox MyDict("MyKey")
Is equivalent to:
MyDict.Item("MyKey") = "MyValue"
MsgBox MyDict.Item("MyKey")
Which to me implies that "Item" is the default operation for the class.
I've never before created a VB6 class that had a default operation, so upon realizing this, I thought perhaps I could define multiple default operations as long as they all have different signatures, which they do: Keys is nullary, the Item getter takes a Variant, and the Item setter takes two Variants. But this doesn't seem to be allowed: When I use "Tools/Procedure Attributes" to set the Keys function to be the default, and then I use it to set the Item property to be the default, the IDE complains that a default has already been set.
So I think I'm misunderstanding something fundamental here. What is going on in the Scripting.Dictionary object that makes it able to act as if "Keys" is the default in some contexts, but as if "Item" is the default in others? And whatever it is, can I accomplish the same thing in VB6?
OK, answering my own question: I haven't tried this yet, but I gather that "Item" should be made the default, and that I should add an entirely new function called "NewEnum" that looks something like the following (slightly modified from an example in Francesco Balena's "Programming Microsoft Visual Basic 6.0" book):
Public Function NewEnum() As IUnknown
Set NewEnum = m_Keys.[_NewEnum]
End Function
(where "m_Keys" is a Collection containing the keys), and then use Tools/Procedure Attributes to hide NewEnum and to set its ProcID to -4.
What you are observing is the difference between the default member and a collection enumerator. A COM object (including VB6 classes) can have both.
You can identify the default property of a class by looking in the Object Browser for the tiny blue globe or the words "default member of" in the description (see Contents of the Object Browser). The Object Browser will not identify an enumerator method, but if you look at the class's interface definition using OLE View or TypeLib Browser (free but registration required) it's DispId will be 0xfffffffc or -4.
In your own class, you can mark the default property by setting the Procedure ID to "(default)" in the Procedure Attributes dialog (see Making a Property or Method the Default). You already listed the steps for setting up the collection enumerator in your own answer, but you can find this listed as well in the Programmer's Guide topic Creating Your Own Collection Class: The House of Bricks.
Scripting.Dictionary has a dirty secret:
It does not handle enumeration at all, it returns big ugly Variant arrays and your For Each loops iterate over those.
This is one of the reasons why a Dictionary can actually be far less efficient than a standard VB6 Collection.
From UML user guide,chapter 5, I've found the following:
To model structural relationships,
For each pair of classes, if you need to navigate from objects of one to objects of another, specify an association between the two. This is a data-driven view of associations.
For each pair of classes, if objects of one class need to interact with objects of the other class other than as parameters to an operation, specify an association between the two. This is more of a behavior-driven view of associations
And this is my understanding about the first type of association,data-driven view of associations through the following example: a class, User, with three attributes, one of which is another class, Address.
class User {
String firstName;
String lastName;
Address address;
}
class Address {
String streetName;
int streetNumber;
String postalCode;
}
and the UML diagram of the above situation is:
Note that the third attribute of User converted to association end(as far as I know because it is of Address class type)
My questions:
1- Is this the correct interpretation of data-driven view of associations?
2- What about behavior-driven view of associations? is there an example explains it?
Data - driven associations are normal associations with aggregation, multiplicity, navigability and all these things. They are well defined.
Behavior-driven associations you use when showing functions that belong to one class and use other class instance as a parameter or the result. Also here belongs any complex connection, such as "listens", "registers" and so on. They are shown as dependencies, maybe with some additional letter, such as <<u>> (usage). They are not strictly defined and can't be used for code generation.
Don't believe much to the words "UML user guide" - all of them are simply books and are not the part of UML standard. They are not saint and are full of personal views (and alas, fallacies) of authors. Nowhere in UML standard is forbidden to use Dependencies for showing the use of other class in some parameter list.
I interface with two data stores, one of them is RDMS and the other one is LDAP. What I want to do is to have a hierarchy persisted using "superclass-table" in the RDBMS and "complete-table" in LDAP.
Is such a thing possible? That is, can you specify multiple inheritance strategies, separately for each data store in the *.orm files or am I forced to use a single inheritance strategy?
I couldn't find this information in the documentation of DataNucleus, they only thing I'm sure of is that you can put inheritance elements in an .orm file instead of the .jdo but that still doesn't answer my question...
The hierarchy is rather simple and consists of an abstract class and several subclasses like this:
abstract class Foo implements IFoo
{
...
}
class Foo1 extends Foo
{
...
}
class Foo2 extends Foo
{
...
}
...
I tried to configure inheritance in the two *.orm files AND in the .jdo file (having in mind that it's going to get overriden by the former) but I get an exception
Caused by: org.datanucleus.metadata.InvalidMetaDataException: Class "...Foo1..." has been specified with an inheritance strategy of "superclass-table", yet no superclass exists or none exists with its own table!
at org.datanucleus.metadata.AbstractClassMetaData.validateUserInputForInheritanceMetaData(AbstractClassMetaData.java:903)
at org.datanucleus.metadata.ClassMetaData.populate(ClassMetaData.java:214)
at org.datanucleus.metadata.MetaDataManager$1.run(MetaDataManager.java:2393)
at java.security.AccessController.doPrivileged(Native Method)
at org.datanucleus.metadata.MetaDataManager.populateAbstractClassMetaData(MetaDataManager.java:2387)
at org.datanucleus.metadata.MetaDataManager.populateFileMetaData(MetaDataManager.java:2224)
at org.datanucleus.jdo.metadata.JDOMetaDataManager.loadMetaDataForClass(JDOMetaDataManager.java:741)
at org.datanucleus.jdo.metadata.JDOMetaDataManager.getMetaDataForClassInternal(JDOMetaDataManager.java:353)
at org.datanucleus.jdo.metadata.JDOMetaDataManager$MetaDataRegisterClassListener.registerClass(JDOMetaDataManager.java:184)
at javax.jdo.spi.JDOImplHelper.registerClass(JDOImplHelper.java:376)
I then tried to remove the inheritance elements from the .jdo but the enhancer fails with the following message:
Class "...Foo1..." has been specified to use an inheritance strategy of "superclass-table", persisting to the table of class ...Foo..., however this class doesnt have a discriminator specified.
The individual configurations are correct (new-table with discriminator at base-class and superclass-table at subclasses for the first case and complete-table only at base-class for the second).
You can put that information in the orm file, yes, and indeed that does answer your question since you have one ORM file for RDBMS, and one for LDAP. So package-rdbms.orm, and package-ldap.orm, and then simply set persistence property "javax.jdo.option.Mapping" to either "rdbms" or "ldap". Simple