10 Replies Latest reply on Nov 13, 2014 1:58 PM by Brett Meyer

    Find all references to a specific node?

    Brett Meyer Apprentice

      I'm trying to implement a set of constraints that would, for example, not allow a referenceable node to be deleted if any other node references it.  Are either of the following possible?

       

      1. Configure Modeshape to throw some sort of referential constraint violation if the above is attempted?
      2. A JCR-SQL2 query (or any alternative) that would either result in the nodes referencing the given node, or result in a boolean (ex: "isReferenced")?

       

      Thank you for any help available!  I haven't had much luck finding solutions elsewhere...

        • 1. Re: Find all references to a specific node?
          Horia Chiorean Master

          JCR has built-in types for REFERENCE (strong) and WEAKREFERENCE (weak). Using hard-references enforces integrity validation.

          For example: modeshape/references.cnd at master · ModeShape/modeshape · GitHub

          1 of 1 people found this helpful
          • 2. Re: Re: Find all references to a specific node?
            Randall Hauch Master

            Like Horia said, JCR's REFERENCE properties (not WEAKREFERENCE or SIMPLEREFERENCE) will prevent removal of referenced nodes. There is nothing to configure, other than registering a custom node type (which can be done programmatically).

             

            As for the JCR-SQL2 queries and references, one option is to find all nodes that refer (via any properties) to a specific set of nodes:

             

                 SELECT * FROM [nt:unstructured] WHERE REFERENCE() = $id


            where the value of the "$id" variable can be bound to a specific node identifier (e.g., the result of 'node.getIdentifier()'). Of course, you can use a literal string rather than having to use a bind variable. If you want to find nodes that have references to one of a set of nodes, you can use the IN clause, this time using 3 string literals:

             

                 SELECT * FROM [nt:unstructured] WHERE REFERENCE() IN ('id1','id2','id3)


            The right side of the IN clause can even be a subquery:


                 SELECT * FROM [nt:unstructured] WHERE REFERENCE() IN (

                      SELECT referenced.[jcr:uuid] FROM [nt:unstructured] AS referenced WHERE ... )

             

            Note that the REFERENCE clause is specific to ModeShape, but it has multiple forms in case you want to constrain any REFERENCE property (like those above) or a specific REFERENCE via the property's name:

             

                 SELECT * FROM [nt:unstructured] AS referring WHERE REFERENCE(referring.[someReferenceProperty]) IN (

                      SELECT referenced.[jcr:uuid] FROM [nt:unstructured] AS referenced WHERE ... )

             

            Alternatively, you can use a JOIN rather than the REFERENCE clause that specifies a single REFERENCE property. The following query should produce similar results to the previous query, if:


                 SELECT referring.* FROM [nt:unstructured] AS referring

                 JOIN [nt:unstructured] AS referred ON referring WHERE referring.[someReferenceProperty] = referred.[jcr:uuid]

                 WHERE ...


            Hope this helps.

            1 of 1 people found this helpful
            • 3. Re: Find all references to a specific node?
              Brett Meyer Apprentice

              Thanks guys, that's what I originally thought would work as well.  However, with the following setup, using strong references, the violations do not occur.

               

              https://gist.github.com/brmeyer/76d9ddbccc0df39a9c95

               

              The only differences I can see:

              • The referenceable is a mixin.
              • The references use "multiple"

               

              Any other ideas what else we could be missing?  Thanks for the help

              • 4. Re: Find all references to a specific node?
                Randall Hauch Master

                The only differences I can see:

                • The referenceable is a mixin.
                • The references use "multiple"

                 

                Any other ideas what else we could be missing?  Thanks for the help

                The 'mix:referenceable' node type is a mixin, so there's no option here. Try removing the 'multiple' to see if that results in constraint violation exceptions when removing the referenced node; but I'd be really surprised if that's changed.

                 

                Just to confirm, you have several nodes:

                1. node A of type 'sramp:baseArtifactType'
                2. node R of type 'sramp:relationship', with a property 'sramp:relationshipTarget' that contains a reference to node A

                 

                If you save this state, then when you try to remove node A and then save those changes, you should get a constraint violation exception. Are you saying that you are able to save the removal of node A even though node R contains a REFERENCE to it?

                • 5. Re: Find all references to a specific node?
                  Brett Meyer Apprentice

                  Say you have 2 nodes, A & B, both of type sramp:baseArtifactType (ignore the fact that it's abstract).  A->relationship->relationshipTarget references B.  I'm currently able to delete B with no violation.

                   

                  Note that both relationship and relationship#relationshipTarget are multiple.  Each relationship can have multiple targets, and each artifact can have multiple relationships.

                  • 6. Re: Find all references to a specific node?
                    Randall Hauch Master

                    Say you have 2 nodes, A & B, both of type sramp:baseArtifactType (ignore the fact that it's abstract).  A->relationship->relationshipTarget references B.  I'm currently able to delete B with no violation.

                    And you're able to save your changes removing node B?

                     

                    Note that both relationship and relationship#relationshipTarget are multiple.  Each relationship can have multiple targets, and each artifact can have multiple relationships.

                    Like I said, try changing the 'multiple' to be singular and see if this changes the behavior, but this should affect what you're doing.

                     

                    If nothing else, we'll need a test case that replicates the problem. We have a number of existing test cases that you could follow, or you could just create a new unit test or new test project from scratch.

                    • 7. Re: Find all references to a specific node?
                      Brett Meyer Apprentice

                      > And you're able to save your changes removing node B?

                       

                      Correct.  The removal and save results in no exceptions.

                       

                      > we'll need a test case that replicates the problem

                       

                      Of course.  It'll be nearly impossible to strip "multiple" within S-RAMP itself.  However, I'll work on something standalone ASAP.

                      • 8. Re: Find all references to a specific node?
                        Brett Meyer Apprentice

                        So, I'm not the brightest crayon in the box.  I completely forgot that S-RAMP doesn't actually delete the node.  We move it to a separate "trash" branch of the tree and keep it permanently, for historical and auditing purposes.

                         

                        Despite my stupidity, Randall, thanks much for the queries.  That will be really helpful for rolling our own checks.

                        • 9. Re: Find all references to a specific node?
                          Randall Hauch Master

                          No worries, Brett. I'm glad everything is working for you.

                          • 10. Re: Find all references to a specific node?
                            Brett Meyer Apprentice

                            For completeness' sake, in case someone else is looking for a solution:

                             

                            In our tree, relationships are defined as separate nodes, in order to support 1.) ad-hoc relationships and 2.) relationships carrying additional metadata (relationship type name, etc.).  So, the CND includes something like:

                             

                            [sramp:relationship]
                            - sramp:relationshipType (string)
                            ... (additional properties)
                            - sramp:relationshipTarget (reference) multiple < 'sramp:baseArtifactType'
                            
                            [sramp:baseArtifactType] > mix:referenceable abstract mixin
                            + * (sramp:relationship)
                            
                            

                             

                            Then, the following query will find all [sramp:relationship]'s that point to the given target.

                             

                            // This query will find all *relationship* nodes that point *to* the given artifact UUID. In addition,
                            // the ISDESCENDANTNODE call ensures the relationship node is on the root path, *not* in the trash.
                            String query = String.format("SELECT * FROM [sramp:relationship] WHERE " +
                              "ISDESCENDANTNODE([sramp:relationship],'" + JCRConstants.ROOT_PATH + "') AND " +
                            "REFERENCE() IN (SELECT referenced.[jcr:uuid] FROM [sramp:baseArtifactType] AS referenced WHERE referenced.[sramp:uuid] = '%1$s')", uuid);