cancel
Showing results for 
Search instead for 
Did you mean: 

set d:noderef property?

aweber1nj
Champ in-the-making
Champ in-the-making
How would I go about setting a noderef property in CMIS (I'm using Apache Chemistry if it make a difference)?

I'm trying to use the cm:referencesnode aspect, and want to set the cm:noderef property to point to another object so I can maintain a relationship.

Not sure what property I would get from the target object (target of the reference), and thus what Object I would be adding to the properties I pass to create… or updateProperties.

Thanks in advance,
AJ
8 REPLIES 8

jpotts
World-Class Innovator
World-Class Innovator
I'm going to show you some Python code that illustrates this as I go along…

If you ask the repo for the type definition for cm:referencesnode and then ask the property for its type, you'll see that it is an ID.

>>> client = CmisClient("http://cmis.alfresco.com/cmisatom", "admin", "admin")
>>> repo = client.defaultRepository
>>> typeDef = repo.getTypeDefinition("P:cm:referencesnode")
>>> props = typeDef.getProperties()
>>> prop = props['cm:noderef']
>>> prop.getUpdatability()
u'readwrite'
>>> prop.getPropertyType()
u'id'

The ID it wants, in this case, is an Alfresco node reference, not a CMIS object ID. So I'll create two test documents, add the cm:referencesnode aspect to the first one, and then set the cm:noderef property with the Alfresco node reference of the second test document.

>>> folder = repo.getObjectByPath('/cmislib')
>>> testFolder = folder.createFolder('testfolder')
>>> testDoc = testFolder.createDocument('testdoc')
>>> testDoc.addAspect('P:cm:referencesnode')
>>> testDoc2 = testFolder.createDocument('testdoc2')
>>> testDoc2.properties['alfcmis:nodeRef']
'workspace://SpacesStore/1c1a8b94-6679-4d02-8d7a-f6c4699d29bc'
>>> props = {'cm:noderef': testDoc2.properties['alfcmis:nodeRef']}
>>> testDoc.updateProperties(props)
<cmislib.model.Document object at 0x10f631510>
>>> testDoc.properties['cm:noderef']
'workspace://SpacesStore/1c1a8b94-6679-4d02-8d7a-f6c4699d29bc'

Hope that makes sense and clarifies things for you.

Jeff

aweber1nj
Champ in-the-making
Champ in-the-making
That helps a lot, and the example is excellent (as usual).

Curious, if "alfcmis:nodeRef" is an Alfresco-specific ID, why the alfcmis prefix?  Is it specific to CMIS use only?

Just to clarify a related item:  for the session.getObject(ObjectId x) and getObject(String objID) methods, those expect the ID returned from something like Document.getId() right?  NOT the alfcmis:nodeRef.

Many thanks again,
AJ

jpotts
World-Class Innovator
World-Class Innovator
Curious, if "alfcmis:nodeRef" is an Alfresco-specific ID, why the alfcmis prefix? Is it specific to CMIS use only?
That is correct, the alfcmis prefix is something you will only see when using CMIS against Alfresco. In plain old Alfresco, there is no "nodeRef" property in the model. When you ask for the nodeRef you can get it, obviously, but it isn't defined in the model. It is helpful to have the Alfresco nodeRef available when using CMIS, especially if you are making both CMIS and non-CMIS calls, so it made sense to add it to the CMIS model. The "alfcmis" prefix distinguishes it from the pure CMIS stuff.

As an aside, CMIS on its own doesn't have a "nodeRef" it has an object ID. The Alfresco nodeRef and the CMIS object ID look similar, but it is important that you always treat the CMIS object ID as an opaque string that has no meaning.

Just to clarify a related item: for the session.getObject(ObjectId x) and getObject(String objID) methods, those expect the ID returned from something like Document.getId() right? NOT the alfcmis:nodeRef.
Correct, you should always assume you are dealing with CMIS Object ID's when you are working with CMIS. Only use a node ref when you are doing something Alfresco specific.

Jeff

aweber1nj
Champ in-the-making
Champ in-the-making
OK, so I came to the realization that I didn't ask the inverse of the original question…

Now that I have a nodeRef on my object, how do I use it to get the "related"/referenced object from CMIS?

So if I have "myObj" and I can get the nodeRef property from it, how do I then use that to retrieve the referenced object if, as you said, I should not treat the nodeRef and the objectId as the same?

Thanks again,
AJ

jpotts
World-Class Innovator
World-Class Innovator
If possible, you should use associations to relate nodes to each other. Then you'd be fully supported by the CMIS API because you'd simply ask the node for its associations and you'd get those back as a collection.

But you've chosen to use a property to store the Alfresco NodeRef, so let's look at your options. The best option would be to write a query against the alfcmis:nodeRef property. But if you look at the type definition you'll see that the property is marked as queryable = false, so that's not an option.

The other option is to rely on the fact that the CMIS version series ID happens to be the same thing as the Alfresco nodeRef. If you do a Document.getObject call using the Alfresco nodeRef (aka, the CMIS version series ID), you'll actually get an object back. If you then do a getLatestVersion() you'll have the object.

I believe that relying on the version ID matching the Alfresco node ref is is very dangerous because the CMIS version ID is supposed to be opaque. Alfresco might change how this works in the future and then your code will break.

But here's how it looks in Python…

First, I grab an existing document, then I create a new document, and stick the new document's Alfresco NodeRef into the cm:noderef property:
>>> doc = folder.getChildren()[0]
>>> doc.name
u'test'
>>> doc2 = folder.createDocument('testrel')
>>> doc.addAspect('P:cm:referencesnode')
>>> props = {'cm:noderef': doc2.properties['alfcmis:nodeRef']}
>>> doc.updateProperties(props)
<cmislib.model.Document object at 0x10caeacd0>
>>> doc2.properties['cm:noderef']
'workspace://SpacesStore/0e9f1749-e311-4559-85e9-7be3104b7b02'

Now I call getObject using an Alfresco NodeRef instead of a CMIS object ID. The nodeRef happens to match the CMIS version series ID, so this returns an object. I may not be guaranteed to have the latest object, so I call getLatestVersion():
>>> reldoc = repo.getObject('workspace://SpacesStore/0e9f1749-e311-4559-85e9-7be3104b7b02')
>>> reldoc = reldoc.getLatestVersion()
>>> reldoc.name
u'testrel'
>>> reldoc.id
'workspace://SpacesStore/0e9f1749-e311-4559-85e9-7be3104b7b02;1.0'

Now I know reldoc has the associated document.

Using associations is definitely preferred. Is that an option for you?

Jeff

aweber1nj
Champ in-the-making
Champ in-the-making
Using associations is definitely preferred. Is that an option for you?
Everything's on the table.

Do you have a link or some handy example on how I would just use an association instead?

I guess I need to associate the "original folder" with its "offspring", though I assume it works the same for any object-to-object.

Thanks for the help.
-AJ

jpotts
World-Class Innovator
World-Class Innovator
This tutorial shows you how to define associations in your custom content model and then how to create associations through CMIS using OpenCMIS.

Jeff

aweber1nj
Champ in-the-making
Champ in-the-making
The document doesn't really describe what the source/target elements should contain (but there is a section of good code later in the document that is really helpful).

Can I put it here and ask that if you find the time, you could "annotate it" for us?

<associations>
    <association name="sc:relatedDocuments">
        <title>Related Documents</title>
        <source>
            <mandatory>false</mandatory>
            <many>true</many>
        </source>
        <target>
            <class>sc:doc</class>
            <mandatory>false</mandatory>
            <many>true</many>
        </target>
    </association>
</associations>
For example, I assume "source" refers to the type within which the Association is being defined, and "target" is the acceptable object-types that can be associated. 
"Mandatory" is kind-of self-explanatory, means we don't actually require any associations to be present.
"Many" on both sides is very interesting.  I could see a particular source-object having many target associations, but since this association is defined on a type, how would "many" apply to the source-side?

Oh, and in the code sample, you refer to methods getSourceObjectId(), etc.  Are you passing a ObjectId class/object, or are you just passing the String cmisSmiley SurprisedbjectId ?
EDIT: Forget that last question; your hashmap is of string/string.

Thanks again,
AJ