This seems like a rather simple query but I've been banging my head for hours on this. I've got a node structure similar to below:
food-group
jcr:content
nuts -> type=almonds
meat -> beef=true
fruit -> type=apples,oranges,bananas
There are three types of properties I need to gather from my child nodes: one that is a string, boolean and string array. I would think that the following sql2 query would work and get the properties of them but for whatever reason I'm getting an error:
QUERY
SELECT
parent.*
FROM
[cq:PageContent] AS parent
INNER JOIN
[nt:base] as child ON ISCHILDNODE(parent)
WHERE
ISDESCENDANTNODE(parent, [/content/grocerystore/food/])"
ERROR:
Need to specify the selector name because the query contains more than one selector.
Any help is appreciated as I've been at this for the past few days.
There are two places the ISCHILDNODE function can be used in a JCR-SQL2 query: in the WHERE clause and in the join criteria. Unfortunately, they take different parameters.
Your query is attempting to use the ISCHILDNODE in the join criteria, which requires two parameters: the selector name for the child nodes, and the selector name for the parent nodes.
Here's the query I think you want:
SELECT parent.*
FROM [cq:PageContent] AS parent
INNER JOIN [nt:base] as child ON ISCHILDNODE(child,parent)
WHERE ISDESCENDANTNODE(parent, [/content/grocerystore/food/])
The only change is the parameters to the ISCHILDNODE function.
Unfortunately, the error message doesn't really make this clear. To understand why, it helps to know about the other form of ISCHILDNODE - the one used in the WHERE clause. That form also takes two parameters: the name of the selector that will represent the child nodes, and the literal path of the parent (under which the resulting nodes are children). Here's a contrived query that uses this form:
SELECT node.*
FROM [nt:base] AS node
WHERE ISCHILDNODE(node,[/content/grocerystore])
The query result would contain all nodes that are children of the /content/grocerystore node.
Now, if the query defines just one selector (e.g., a non-join), then there is only one selector name that can be passed to the ISCHILDNODE function. Strictly speaking, the selector is implicitly known, so JCR-SQL2 allows you to pass in just the path. Here's a query that is semantically identical to the previous contrived example:
SELECT node.*
FROM [nt:base] AS node
WHERE ISCHILDNODE([/content/grocerystore])
This is the only form of the ISCHILDNODE that takes a single parameter, and I think this is the form that I think CQ5 is expecting: the error says that the query defines more than one selector, so the selector must be supplied as the first parameter.
Of course, this is very misleading because you're actually using the form of the function that appears in the join criteria. A better error message would say that function requires the child selector name and the parent selector name.
This exception will be thrown if you invoke getNodes() method on a QueryResult that have more than one selector (in your case it's parent and child). This method will also be used if you call findResources() on the ResourceResolver.
Use the QueryResult#getRows() method instead:
Session session = resourceResolver.adaptTo(Session.class);
QueryManager queryManager = session.getWorkspace().getQueryManager();
Query query = queryManager.createQuery("...", Query.JCR_SQL2);
QueryResult result = query.execute();
// consider using result.getColumnNames() here
RowIterator rows = result.getRows();
while (rows.hasNext()) {
Row row = rows.nextRow();
// use row.getValue() or row.getValues()
}
A few remarks:
You need to fix the SQL query adding child as the first parameter to the ISCHILDNODE(parent).
Pass properties list explicite in the SELECT clause rather than using parent.*, so you don't have to invoke result.getColumnNames().
The query is quite complex and the way to invoke it is even more complex. Why don't you use Sling methods to iterate over the food space in your repository? It seems to be a perfect use-case for iterating over the tree.
You may be also interested in the SlingQuery project.
Related
I am trying to create a custom template fragment that builds a table of value properties. I started by creating a SQL query fragment that pulls all properties classified by a Value Type. Now I would like to pull in the default (initial) value assigned. I figured out that it's in the Description table of t_xref, with the property guid in the client field, but I don't know how to write a query that will reliably parse the default value out since the string length may be different depending on other values set. I tried using the template content selector first but I couldn't figure out how to filter to only value properties. I'm still using the default .qeax file but will be migrating to a windows based DBMS soon. Appreciate any help!
Tried using the content selector. Successfully built a query to get value properties but got stuck trying to join and query t_xref for default value.
Edited to add current query and image
Value Properties are block properties that are typed to Value Types. I'm using SysML.
This is my current query, I am no SQL expert! I don't pull anything from t_xref yet but am pulling out only the value properties with this query:
SELECT property.ea_guid AS CLASSGUID, property.Object_Type AS CLASSTYPE, property.Name, property.Note as [Notes], classifier.Name AS TYPE
FROM t_object property
LEFT JOIN t_object classifier ON property.PDATA1 = classifier.ea_guid
LEFT JOIN t_object block on property.ParentID = block.Object_ID
WHERE block.Object_ID = #OBJECTID# AND property.Object_Type = 'Part' AND classifier.Object_Type = 'DataType'
ORDER BY property.Name
I guess that Geert will come up with a more elaborate answer, but (assuming you are after the Run State) here are some details. The value for these Run States is stored in t_object.runstate as one of the crude Sparxian formats. You find something like
#VAR;Variable=v1;Value=4711;Op==;#ENDVAR;
where v1 is the name and 4711 the default in this example. How you can marry that with your template? Not the faintest idea :-/
I can't give a full answer to the original question as I can't reproduce your data, but I can provide an answer for the generic problem of "how to extract data through SQL from the name-value pair in t_xref".
Note, this is heavily dependent on the database used. The example below extracts fully qualified stereotype names from t_xref in SQL Server for custom profiles.
select
substring(
t_xref.Description, charindex('FQName=',t_xref.Description)+7,
charindex(';ENDSTEREO',t_xref.Description,charindex('FQName=',t_xref.Description))
-charindex('FQName=',t_xref.Description)-7
),
Description from t_xref where t_xref.Description like '%FQName%'
This works using:
substring(string, start, length)
The string is the xref description column, and the start and length are set using:
charindex(substring, string, [start position])
This finds the start and end tags within the xref description field, for the data you're trying to parse.
For your data, I imagine something like the below is the equivalent (I haven't tested this). It's then a case of combining it with the query you've already got.
select
substring(
t_xref.Description, #the string to search in
charindex('#VALU=',t_xref.Description,charindex('#NAME=default',t_xref.Description)+6, #the start position, find the position of the first #VALU= tag after name=default
charindex('#ENDVALU;',t_xref.Description,charindex('#VALU=',t_xref.Description))
-charindex('#VALU=',t_xref.Description,charindex('#NAME=default',t_xref.Description))-6 #the length, find the position of the first #ENDVALU tag after the start, and subtract it from the start position
),
Description from t_xref where t_xref.Description like '%#NAME=default%' #filter anything which doesn't contain this tag to avoid "out of range" index errors
Hello experts of the world. Need some help concerning executing a query with SpringData.
The expectation is to execute the Query below in the Spring Data annotation by combining with the repository method name (Automated Query Construction) to get a unique result. Apparently it fails from time to time by saying the result is not Unique.
The question here is if the method name is still considered in Query Construction while also executing the query in the annotation.
#Query("SELECT r from Revision r WHERE r.revisionBid = ?1 AND r.revisionStatusId = ?2 ORDER BY r.lastModifiedDate DESC")
Optional<Revision> findFirst(Integer revisionBid, Integer revisionStatusId);
Thanks in advance!
The query creation for limiting to 1 result is defined here with FIRST & TOP included in the method name.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
I don't think "findFirst" will work when you're using an #Query, as the query will be constructed from HQL expression in the #Query rather than the fluent API passing over the method name. Because of this, when the query returns multiple results, it will throw the exception as the Optional is told to wrap a single returned object, not a collection. Add a LIMIT clause to the HQL query and you should be good.
I am calculating average length of identifiers with CQLinq in NDepend, and I want to get the length of the names of classes, fields and methods. I walked through this page of CQlinq: http://www.ndepend.com/docs/cqlinq-syntax, and I have code like:
let id_m = Methods.Select(m => new { m.SimpleName, m.SimpleName.Length })
let id_f = Fields.Select(f => new { f.Name, f.Name.Length })
select id_m.Union(id_f)
It doesn't work, one error says:
'System.Collections.Generic.IEnumerable' does not
contain a definition for 'Union'...
The other one is:
cannot convert from
'System.Collections.Generic.IEnumerable' to
'System.Collections.Generic.HashSet'
However, according to MSDN, IEnumerable Interface defines Union() and Concat() methods.
It seems to me that I cannot use CQLinq exactly the same way as Linq. Anyway, is there a way to get the information from Types, Methods and Fields domains within a singe query?
Thanks a lot.
is there a way to get the information from Types, Methods and Fields domains within a singe query?
Not for now, because a CQLinq query can only match a sequence of types, or a sequence of methods or a sequence of field, so you need 3 distinct code queries.
For next version CQLinq, will be improved a lot and indeed you'll be able to write things like:
from codeElement in Application.TypesAndMembers
select new { codeElement, codeElement.Name.Length }
Next version will be available before the end of the year 2016.
I was looking at my profiler and it seem that the Linq extension method Single selects 2 rows. So why does the Single LINQ extension method create a query Select TOP(2)? Shouldn't it be select top(1) instead?
First only requests a single result. Single requests two results because it only returns a result if there is exactly one member of the result set. If there are zero or two (or more than two) it throws an exception, so it has to request at least two results.
In an XmlData column in SQL Server 2008 that has no schema assigned to it, how can I pull the first item at a particular node level? For example, I have:
SELECT
XmlData.value('//*/*[1]','NVARCHAR(6)')
FROM table
where XmlData.Exist('//*/*[1]') = 1
I assume this does not work because if there are multiple nodes with different names at the 2nd level, the first of each of those could be returned (and the value() requires that a singleton be selected.
Since I don't know what the names of any nodes will be, is there a way to always select whatever the first node is at the 2nd level?
I found the answer by chaining Xquery .query() and .value()
XMLDATA.query('//*/*[1]').value('.[1]','NVARCHAR(6)')
This returns the value of the first node and works perfectly for my needs.