I'm looking at elements that have XAttributes on them. I can create a ECQuery to find instances on an element. When I look at what is found, it is not uncommon to find multiple Classes on an element. For example:
[DgnElementSchema:LineElement][DgnCustomItemTypes_SRSFeature:Air__x0020__Tunnel]
I'm interested in the 'SRSFeature' XAttributes. Is it possible to (and how would you) set up a WhereCriterion to restrict the ECQuery to return only ones from the 'DgnCustomItemTypes_SRSFeature' schema? USe CreateStringFilter()?
Bruce
Bruce Reeves SRNS said:I'm looking at elements that have XAttributes on them
Bruce Reeves SRNS said:[DgnCustomItemTypes_SRSFeature:Air__x0020__Tunnel]
Your question is about Item Types and you want to filter on the property values of an Item Type instance? Are XAttributes relevant?
using namespace Bentley::DgnPlatform; FindInstancesScopeOption option (DgnECHostType::Element); // In entire DGN file //scope = FindInstancesScope::CreateScope (*Utilities::GetActiveDgnFile (), option); // In active model Bentley::DgnPlatform::FindInstancesScopePtr scope = FindInstancesScope::CreateScope ( *ISessionMgr::GetActiveDgnModelP (), option);
bool ECQueryFactory::ComposeCriteria (ECQueryPtr query, WCharCP property, WCharCP filter) { using namespace Bentley::DgnPlatform; bool composed { false }; if (query.IsValid()) { WhereExpressionPtr expression { WhereExpression::CreatePropertyExpression (property) }; const bool& IfMatch { true }; const bool& CaseSensitive { true }; WhereCriterionPtr where; WhereCriterion::StringFilterError error; if (RegEx::IsRegEx (filter)) { where = WhereCriterion::CreateRegexFilter (&error, *expression, filter, !CaseSensitive); } else { where = WhereCriterion::CreateStringFilter(&error, *expression, filter, IfMatch, !CaseSensitive); } } if (!where.IsValid () && WhereCriterion::STRING_FILTER_ERROR_InvalidSyntax == error) { // CreateStringFilter syntax error filter= filter } else { query->SetWhereCriterion (*where); composed = query.IsValid (); } } return composed; }
DgnECInstanceIterable ECQueryManager::FindInstances () { return ECQueryFactory::FindInstances (scope_, query_, propertyName_, strFilter_.c_str (), nullptr); }
Thanks to Paul Connelly for guiding me through that jungle.
Regards, Jon Summers LA Solutions
Jon Summers said:Are XAttributes relevant?
Yes. I have models that originated in V8 that have XAttributes added to some elements using XMLInstanceAPI::Native. That API is no longer available in CONNECT. When You examine one of these elements, in the Properties dialog they show up as ItemTypes.
Now I need to (in CONNECT) report on these XAttributes-turned-ItemTypes. I know I can scan the model, and extract ALL the element's ECClasses, but that returns not only the Item Types, but all the 'other' XAttributes (like DgnElementSchema). I'm wondering if there is a way to "filter" the ones returned to ONLY those in the schema I'm interested in.
The ClassName is reported (I assume) in the format Schema::Class. I'd like to find any/all that belong to a specific schema without having to test each Class returned to ensure it's in the schema I want.
It's my impression that when the class name is returned (e.g. DgnCustomItemTypes_SRSFeature:Air__x0020__Tunnel), that since I have a ECClass object, that the Schema and Class names are not really 'properties' of the Class, so I'm not sure how filter on them. I am able to filter them using AddSearchClass() and providing the schema and class, but I'd like to accept ALL Classes and don't want to have explicitly name each one...If I could supply '*' to get all Classes, that would be perfect, but unfortunately that doesn't work.
Bruce Reeves SRNS said:DgnCustomItemTypes_SRSFeature:Air__x0020__Tunnel
Item Type Library::Class Name
Here's a method to get a named schema from the active DGN file...
bool SchemaFactory::GetNamedSchema (ECN::ECSchemaPtr& pSchema, WCharCP name) { DgnFileP pDgnFile = ISessionMgr::GetActiveDgnFile (); const UInt32& VerMajor = 1; const UInt32& VerMinor = 0; DgnPlatform::SchemaInfo schemaInfo (ECN::SchemaKey (name, VerMajor, VerMinor), *pDgnFile); DgnECManagerR ecMan = DgnPlatform::DgnECManager::GetManager (); pSchema = ecMan.LocateSchemaInDgnFile (schemaInfo, ECN::SchemaMatchType::SCHEMAMATCHTYPE_LatestCompatible); return pSchema.IsValid (); }
Create a query for a named Item Type class...
ECQueryPtr ECQueryFactory::CreateQueryForItemType (WCharCP itemTypeName) { ECQueryPtr ecQuery; ItemTypeLibraryMgr libMgr (L"library name"); bool found { libMgr.ExistsInActiveFile () }; if (found) { ItemTypeLibraryPtr pLib { libMgr.Get () }; ItemTypeP itemType = pLib->GetItemTypeByName(itemTypeName); if (nullptr == itemType) { // Unable to get Item Type itemTypeName } else { // Note reliance on internal library and class names WString internalName (ItemTypes::GetInternalName (itemType)); ecQuery = CreateQuery (pLib->GetInternalName (), internalName.c_str ()); } } return ecQuery; }
Many of the EC interfaces require a class's internal name rather than the name that you & I would expect. This article discusses internal names and how to obtain them.
Thanks for the explanation and sample. Is not possible to query base on library name alone? For example (using a SQL like syntax):
SELECT Library:* FROM Element WHERE Library="MyLIbrary"
Bruce Reeves SRNS said:Is it not possible to query base on library name alone? For example using a SQL like syntax
If only that were so!
I don't understand the concepts or design that went into the EC query language. I find it hard to use and suspect that I am not alone. Here are some other paths that Bentley Systems might have considered...
Instead, we have a proprietary query API that is poorly documented. It's a combination of classes and text expressions: classes are formally defined and have the usual terse documentation; expressions are not formally defined and have no documentation. Examples are sparse.
And why are Report queries different and also undocumented?
Hi John,
I guess it goes a bit beyond scope of this discussion, but anyway... ;-)
Jon Summers said:I don't understand the concepts or design that went into the EC query language. I find it hard to use and suspect that I am not alone.
That's probably true. But I guess it wise to split EC query "language" and how it's implemented in API.
Jon Summers said:Here are some other paths that Bentley Systems might have considered...
That's a bit unfair in my opinion, because you want to evaluate technology invented (I guess) more than 10 years ago with pretty complex structure of features and priorities from today perspective. The then decisions can be decided not best easily in such position.
Jon Summers said:If data are stored as XML fragments and schemas are XML, then why not use the XML query language XQuery?
XQuery focuses unstructured data and it's not context-sensitive tool. Which does not mean XQuery (and XPath) is not seriously powerful tool, but ECQuery is designed to work in defined EC Schema context, which I guess can be hardly implemented into XQuery.
Jon Summers said:Why not make them exactly like SQL by incorporating a domain specific language (DSL) compiler?
ECSQL is available already in iModelJS API. It's all about steady development of the whole technology and implementing new features and tools, where some are not backward compatible (e.g. some changes in ECSchemas in PowerPlatform products and iModelJS).
Jon Summers said:Why not use a LINQ data source to convert query statements into an ECQuery?
I guess LINQ did not exist when EC technology was invented ;-)
But actually ... nobody protect you from implementing own LINQ extension to work with geometry, standard properties and also custom structures defined in EC schemas. But it sounds like long-term complex task.
Jon Summers said:Instead, we have a proprietary query API that is poorly documented.
The documentation is the biggest problem in my opinion. Even not well structured API can be used efficiently when documented properly. The opposite situation is much worse.
With regards,
Jan
Bentley Accredited Developer: iTwin Platform - AssociateLabyrinth Technology | dev.notes() | cad.point