This is a long one so you....
I have a constraint attribute rule expression that iterates over each feature in a FeatureSet (RelatedInfo table), and looks at a relationship class (LandParcel_Details_RelatedInfo_rel), which is M:N, for matching RelatedInfo GlobalIDs and returns all the Origin Foreign Keys for the LandParcel_Details Table. I use those Origin Foreign Keys to query LandParcel_Details Table and do checks. This is all in a file geodatabase.
An example of the relationships look like this
Still with me? Great!
Strangely enough at one point I used to be able to just use FeatureSetByName on the LandParcel_Details_RelatedInfo_rel relationship class and do my work everything was right as rain!
(Original Code below)
Sent it to the some folks for testing and they got an error when they tried to save the attribute rule that the LandParcel_Details_RelatedInfo_rel relationship table cannot be found. It was working on my file geodatabase, which is the same data. Yet when we work with a new copy of the same file geodatabase, now we get an error when we try to save the attribute rule that the LandParcel_Details_RelatedInfo_rel relationship cannot be found. Example below with dummy data.
So at some point it was working. Then at some point it stopped. The really weird stuff happened when doing other testing, writing console logs and error messaging for code validation, and getting a successful attribute rule save and run (similar code but with the consoles or error messages), then replace that rules expression with the one that was working then didn't, it saved. If you deleted the rule and reapply a new rule it saved. If you deleted the file geodatabase and create a new copy (we have a zip file we just unzip a new copy) the error then persists.
Not too concerned about this, just trying to get the code to function (outlined below).
Still with me?....I swear I'll get there.....
So, okay instead of using that feature FeatureSetByName I swapped out for FeatureSetByRelationshipClass and in my testing to loop through a FeatureSet and using each object as the inputFeature of FeatureSetByRelationshipClass, just using Console to check outputs and Pro 3.3.2 keeps crashing.....like a lot. Like every time.
Lets crash pro together, I created test data for folks in the attached pro project package! See attached LoopCrash.ppkx
In the project package you'll find a fgdb with TableA and TableB with a M:N relationship class called TableA_To_TableB.
Data is already related between the two.
On the TableA is a constrain attribute rule. To trigger just add any value to the TableA "FieldToEdit" field to trigger. You should get an error outlined in the code below line 6.
var x
for (var row in FeatureSetByName($datastore, "TableA",["*"],false)){
x = FeatureSetByRelationshipClass(row, "TableA_To_TableB", ["*"], false);
}
return {"errorMessage": Text(x)}
If you replace the above code with the following, and repeat the test of add any value to the TableA "FieldToEdit" field to trigger you should crash ArcGIS Pro just validating:
var x
var z
for (var row in FeatureSetByName($datastore, "TableA",["*"],false)){
x = FeatureSetByRelationshipClass(row, "TableA_To_TableB", ["*"], false);
for (var y in x){
z = y.GlobalID
}
}
return {"errorMessage": Text(z)}
HERE IS THE MAIN PART AND QUESTION OF THE POST:
If you look at the original code below, the function getRelatedLandParcelGlobalIDs takes in a list of RelatedInfo GlobalIDs, filters the LandParcel_Details_RelatedInfo_rel relationship class for matches, then loops through and creates a dictionary of RelatedInfo GlobalIDs and all LandParcel_Details GlobalIDS that match returning something like:
{"relatedinfoglobalid":"{8BC198FA-89F3-4924-A1B3-7B1586A80E3A}","landparcelglobalid":"{F09CB8F5-6E83-4A80-A3C3-5113BFF60AF2}","{03382B6C-BAD3-4E13-A9DD-20061D8F4424}","{9F5EEA9E-9620-4176-A14F-1B053085D017}"},
{"relatedinfoglobalid":"{36591372-61C1-4D66-B1BF-65517BD5B100}","landparcelglobalid":"{03382B6C-BAD3-4E13-A9DD-20061D8F4424}","{9F5EEA9E-9620-4176-A14F-1B053085D017}","{3700974E-0ED4-455C-9908-42D5252D4131}"}
This dictionary is pushed into an array in which I use to go get matching records in the LandParcel table to do other checks.
/* ***************FUNCTIONS**************** */
function checkMoreThanOneROWDoc(table,global,category){
var ROWRecords = Filter(table, `${global} = @recordGlobalId AND ${category} = 'ROW'`)
Push(multiRelatedInfo, $feature.GlobalID);
for(var record in ROWRecords){
Push(multiRelatedInfo, record.GlobalID);
}
if(Count(multiRelatedInfo) > 1){
return true;
}
return false
}
function checkParcelRecordType(table,global,typeCode){
var ParcelFabricRecord = Filter(table, `${global} = @recordGlobalId`)
for(var record in ParcelFabricRecord){
if(record[typeCode] == 'Easement' || (Find('EasementReview', record[typeCode]) > -1)){
return true
}
return false
}
}
function getRelatedLandParcelGlobalIDs(table){
if (Count(multiRelatedInfo)<1){
return
}
var filterRelated = Filter(table, `relatedinfoglobalid IN @multiRelatedInfo`)
var LandParcelList = []
var RelatedInfoMap = {};
for (var row in filterRelated) {
var docId = row.relatedinfoglobalid;
var LandParcelId = row.landparcelglobalid;
if (!HasKey(RelatedInfoMap, docId)) {
RelatedInfoMap[docId] = [];
}
Push(RelatedInfoMap[docId], LandParcelId);
}
for (var docId in RelatedInfoMap) {
var featureDict = {};
featureDict["relatedinfoglobalid"] = docId;
featureDict["landparcelglobalid"] = RelatedInfoMap[docId];
Push(LandParcelList, featureDict);
}
return LandParcelList
}
function findMatchingLandParcelIds(LandParcelList) {
var matchingLandParcelIds = [];
for (var i = 0; i < Count(LandParcelList); i++) {
var doc1 = LandParcelList[i];
if (doc1.relatedinfoglobalid == $feature.GlobalID){
for (var j = 0; j < Count(LandParcelList); j++) {
var doc2 = LandParcelList[j];
if (doc1.relatedinfoglobalid != doc2.relatedinfoglobalid) {
var commonIds = [];
for (var k = 0; k < Count(doc1.landparcelglobalid); k++) {
if (IndexOf(doc2.landparcelglobalid, doc1.landparcelglobalid[k]) >= 0) {
Push(commonIds, doc1.landparcelglobalid[k]);
}
}
if (Count(commonIds) > 0) {
for (var m = 0; m < Count(commonIds); m++) {
if (IndexOf(matchingLandParcelIds, commonIds[m]) == -1) {
Push(matchingLandParcelIds, commonIds[m]);
}
}
}
}
}
}
}
return matchingLandParcelIds;
}
function checkMoreThanOneLandParcelType(records,LandParcelFeatures){
var relatedlandparcelglobalids = getRelatedLandParcelGlobalIDs(records)
var matchingLandParcelIds = findMatchingLandParcelIds(relatedlandparcelglobalids);
if(Count(matchingLandParcelIds)<1){
return 99
}
var filteredLandParcelFeatures = Filter(LandParcelFeatures, `GlobalID IN @matchingLandParcelIds AND (${landParcelStatus_field} = 'Current' OR ${landParcelStatus_field} = 'InReview')`);
if (Count(filteredLandParcelFeatures) >=1){
return 1
}
return 0
}
/* ***************MAIN**************** */
if ($originalfeature.category != "ROW"){
return true
}
if ($originalFeature.category == "ROW" && (($feature.category != "ROW") || $editcontext.editType == "DELETE")){
// Script Variables
var multiRelatedInfo = []
var relatedInfoCheck = false
var recordCheck = false
var landParcelCheck = false
var recordGlobalId = $feature.recordglobalid
// RelatedInfo Table Fields
var relatedinfoglobalid_field = "GlobalID"
var recordGlobalId_field = "recordglobalid"
var category_field = "category"
// ParcelFabric Records Fields
var type_field = "type"
var parcelFabricRecordsGlobalID_field = "GlobalID"
// LandParcel_Details_RelatedInfo_rel Fields
var LandParcelrelatedinfoglobalid_field = "GlobalID"
// LandParcel_Details Fields
var landparcelglobalid_field = "GlobalID"
var landParcelStatus_field = "status"
// get feature sets
var RelatedInfoTable = FeatureSetByName($datastore, "RelatedInfo", [relatedinfoglobalid_field, category_field, recordGlobalId_field], false)
var parcelFabricRecords = FeatureSetByName($datastore, "ParcelFabric_Records", [parcelFabricRecordsGlobalID_field, type_field ], false)
var relatedRecords = FeatureSetByName($datastore, "LandParcel_Details_RelatedInfo_rel", ["*"], false);
var LandParcelFeatures = FeatureSetByName($datastore, "LandParcel_Details", [landparcelglobalid_field ,landParcelStatus_field], false)
// run checks
relatedInfoCheck = checkMoreThanOneROWDoc(RelatedInfoTable,recordGlobalId_field,category_field)
recordCheck = checkParcelRecordType(parcelFabricRecords,parcelFabricRecordsGlobalID_field,type_field)
landParcelCheck = checkMoreThanOneLandParcelType(relatedRecords,LandParcelFeatures)
// if there is only one ROW RelatedInfo and the related parcel fabric record type is Easement or EasementReview; no edit or delete
if((!(relatedInfoCheck)) && recordCheck){
return {"errorMessage": "Cannot Edit the category field or delete this records. There is only one ROW RelatedInfo related to the Parcel Fabric Record with Easement or LandParcel type"}
}
// if there is only one ROW RelatedInfo and the related parcel fabric record type is not Easement or EasementReview; let edit or delete
if((!(relatedInfoCheck)) && (!(recordCheck))){
return true
}
// if there is more than one ROW RelatedInfo and the related parcel fabric record type is not Easement or EasementReview; let edit or delete
if((relatedInfoCheck) && (!(recordCheck))){ // let edit or delete
return true
}
// if there is more than one ROW RelatedInfo the related parcel fabric record type is Easement or EasementReview;
if((relatedInfoCheck) && (recordCheck)){
// if there is more than one related Land Parcel feature with status Current or InReview
if(landParcelCheck == 1){
return true
}
// if there are no related land Parcel features
if(landParcelCheck == 99){
return {"errorMessage": "Cannot Edit the category field or delete this records. The are no related LandParcel features"}
}
else{
return {"errorMessage": "Cannot Edit the category field or delete this records. There is only one related LandParcel feature that is type Current or InReview"}
}
}
}
return true
Is what I'm trying to accomplish even possible?
Authors Note: Okay full disclosure, my wife and I added a new addition to our family a few months ago. During nap/hangout time we've been watching the Star Wars films. So this is where all this is coming from. Sorry, not sorry.
Update.....upgrading to ArcGIS Pro 3.4 fixes the crash issue. It does not, however, fix the issue with calling a relationship class using the FeatureSetByName function.