Query with Inheritance and
Recursion

A Completely New Query Category that Efficiently Resolves the Data Structures of Search Results

RQL supports recursively resolving the roots of query result objects with inheritance.

  • The use of inheritance is frequently required in common, everyday object models.
  • Automatic recursion resolution makes database searches intuitive to write and understand, even when dealing with complex data structures.

The image below illustrates an object model used to store projects and their sub-projects in the object database.

The Inheritance and Recursion Object Model

Below is the Java source code for the Project class from the object model, where the list field subProjects contains objects of type SubProject. Although the source code for the SubProject class is not shown, it inherits from the Project class.

Object Model: Project class source code:

    package fi.rootrql.example5;
    import java.sql.Date;
    import java.util.ArrayList;
    import java.util.List;
    import fi.rootrql.mvanno.clazz.Index;
    @Index(name = "index1", fields = { "name" }, unique = true)
    @Index(name = "index2", fields = { "startDate" }, unique = false)
    @Index(name = "index3", fields = { "endDate" }, unique = false)
    public class Project {
        public Long id;
        public String name;
        public String description;
        public Date startDate;
        public Date endDate;
        public String location;
public List<SubProject> subProjects;
public Project() { super(); } }

A person named person15 is queried from the object database by searching through the list of persons performing tasks in the sub-projects, stored in the taskList field.

Query example 1: Person.name = 'person15' at Project.subProjects.taskPersonList:

    RootStore r1 = rdb.getRootStore("fi.rootrql.example5.Project");
    Query query =  Query.createQuery()
        .addQ1Query(r1,	"(
subProjects.
taskPersonList.
person.
name='person15')")
.setParts(true) .setQ1ResolveRoots(true); List<Project> list = query.queryQ1Objects();

Query results: The query retrieves two projects, as shown in the debug view below. The returned result objects are complete Project objects, including all related objects in their data structures. The data structure of the first Project object has been expanded to include the searched person (person.name='person15').

Note that the searched person is located within a sub-project of a sub-project. This highlights the power of automatic recursion resolution, as the queried objects are found regardless of their depth within the recursive data structure.

subProjectPersonName15-120

The Attributes acceptCanonicalName and acceptCanonicalNameOnly can be used to limit the accepted classes in a database search

Query example 1: In the object model above, all Project objects can be queried by passing the RootStore of the Project class as a parameter in the addQuery method, as shown in the addQuery(r1) method call below.

RootStore r1 = rdb.getRootStore("fi.rootrql.example5.Project");
Query query = Query.createQuery()
                .addQuery(r1);
List<Project> list = query.queryObjects();

Query results: In fact, the result includes all Project and SubProject objects from the database, since SubProject inherits from the Project class.

queryAllProjects-130

Query example 2: Similar to Query 1 above, but now the result objects are limited to Project objects by setting the attributes acceptCanonicalName and acceptCanonicalNameOnly in the parameter list of the setAcceptCanonicalName method call. The acceptCanonicalName attribute specifies the accepted class but still allows subclasses. Therefore, the acceptCanonicalNameOnly attribute must be set to true to prevent the inclusion of subclasses.

RootStore r1 = rdb.getRootStore("fi.rootrql.example5.Project");
boolean acceptCanonicalNameOnly = true;
Query query = Query.createQuery()
                .addQuery(r1)
                .setAcceptCanonicalName("fi.rootrql.example5.Project",
                                            acceptCanonicalNameOnly);
List<Project> list = query.queryObjects();

Query results: The result contains only Project objects, as the query is restricted to include them exclusively.

queryAllProjectsOnly-130

Query example 3: Next, the result objects are limited to SubProject objects by passing the acceptCanonicalName attribute as a parameter in the call to the setAcceptCanonicalName method. The second parameter, acceptCanonicalNameOnly, is not required in this case because SubProject has no subclasses.

RootStore r1 = rdb.getRootStore("fi.rootrql.example5.Project");
Query query = Query.createQuery()
                .addQuery(r1)
                .setAcceptCanonicalName("fi.rootrql.example5.SubProject");
List<Project> list = query.queryObjects();

Query results: The result contains only SubProject objects, as the query is limited to them exclusively.

queryAllSubProjectsOnly-130

Data Structures Can Have an Unambiguous Root(s), but not Always.

Data structures in object models can include cycles, yet an unambiguous root may still exist.

Query example 1: Below is a data structure consisting of four objects, where objects p1, p2, and p3 form a cycle.

resolveRootsAndCycles1Ex2

The object p2 is queried from the database, and its root objects are resolved, with the corresponding source code shown below.

        RootStore r1 = rdb.getRootStore("fi.rootrql.example1.Person");
        Query query = Query.createQuery()
                        .addQuery(r1, "(name = 'p2')")
                        .setResolveRoots(true)
                        .setResolveCycles(true);
                        .setParts(true)
        List<Person> list = query.queryObjects();
        

Query results: The search condition locates object p2 (marked in red), and resolving the roots finds object p10 (marked in blue). The query result returns object p10 and its data structure up to object p2. The returned data structure includes object p10 and the cycle involving objects p1, p2, and p3, as indicated by dark yellow dashed lines.

rootExistsGraphQuery1

The data structure of object p10 is expanded in the debug view below, clearly showing the cycle involving objects p1, p2, and p3. Note that the same instance of object p1 is displayed twice to highlight its presence due to recursion, confirming that no other instance of p1 exists.

resolveRootsAndCycles1DebugResult1Ex2

Query example 2: In the modified version of Query Example 1, only the cycle of objects p1, p2, and p3 remains, as illustrated in the figure below.

resolveRootsAndCycles1Ex3

The database query is identical to that in Query Example 1. Object p2 is searched, and its roots are resolved. The corresponding source code is provided below.

        RootStore r1 = rdb.getRootStore("fi.rootrql.example1.Person");
        Query query = Query.createQuery()
                        .addQuery(r1, "(name = 'p2')")
                        .setResolveRoots(true)
                        .setResolveCycles(true);
                        .setParts(true)
        List<Person> list = query.queryObjects();
        

Query results: The search condition locates object p2 (marked in red), and resolving the roots finds object p2 again (marked in blue). Now, the data structure of object p2 contains a cycle involving objects: p2, p3, p1, and back to p2. The data structure of p2 is highlighted with dark yellow dashed lines

rootExistsGraphQuery2

The data structure of object p2 is expanded in the debug view below, clearly showing the cycle involving objects p2, p3, and p1. Note that the same instance of object p2 appears twice to confirm its presence is due to recursion, ensuring no other instance of p2 exists.

rootExistsQuery2

Query example 3: In the modified version of Query Example 1, object p10 has been placed below the cycle formed by objects p1, p2, and p3, creating a triangle. Additionally, p10 has no references to other objects but is referenced from object p2.

resolveRootsAndCycles1Ex4

Now, the database query searches for object p10, and its root objects are also resolved. What makes this search particularly interesting is that the data structure appears to lack any unambiguous root object.

        RootStore r1 = rdb.getRootStore("fi.rootrql.example1.Person");
        Query query = Query.createQuery()
                        .addQuery(r1, "(name = 'p10')")
                        .setResolveRoots(true)
                        .setResolveCycles(true);
                        .setParts(true)
        List<Person> list = query.queryObjects();
        

Query results: The search condition locates object p10 (marked in red), and resolving the roots finds object p2 (marked in blue). The data structure of p2 (outlined with dark yellow dashed lines) contains the cycle involving objects p2, p3, and p1, as well as a reference from p2 to p10. In fact, any object in the cycle could serve as a valid root, but the cycle resolution algorithm selects p2 in this case. Since the resolution process both starts and ends at p2, it is chosen as the root object.

rootExistsGraphQuery3

The data structure of object p2 is expanded in the debug view below, clearly showing the cycle of objects p2, p3, and p1. The friends list field of object p2 contains two items, which reference objects p3 and p10. Note that the same instance of object p2 appears twice, confirming that its presence is due to recursion and that no other instance of p2 exists

rootExistsQuery3

In summary, cycles and recursion are fully resolved in all object models, covering both single class and multiple class object models.

The automatic resolution of recursive data structures in database searches offers significant advantages.

Links to other pages on this site.


Page content © 2024 

company name

Contact us:

mail address