Implementing SearchCondition Not Equal

Implementing SearchCondition Not Equal

niels_deboer
Participant Participant
271 Views
6 Replies
Message 1 of 7

Implementing SearchCondition Not Equal

niels_deboer
Participant
Participant

Tl;dr

We are trying to find the “ModelItems” in our document that do not equal a certain property value using a plugin for Navisworks 2025. In our implementation we filter items from our model and create a selection. We hide or unhide items from this selection and then save it to a viewpoint (using COM). This way we can also validate if our not equal filter solution works.

 

Contrast this to the Navisworks GUI where we go about this by applying a search set, selecting a category, property, condition and value.

niels_deboer_0-1755534716664.png

Seems simple, but there is no “not equals” condition in the API. We’ve tried and found various solutions and leads, but nothing works so far. Has anyone been successful in implementing a not equal filter?

 

What we’ve tried so far

1. Documentation

First solution is found in the documentation where it is mentioned that you can use the “Negate” method or “NotEqual” under SearchConditionComparison Enumeration. However, this does not work. We decompiled the .dll and NotEqual doesn’t exist. Attempting to use negate in this fashion will result in an empty ModelSelection. We know this, because our script saves it to a viewpoint, and contrary to s1, the s2 viewpoint is completely empty. Here is the example from the documentation example:

 

 

//set a search condition

SearchCondition s1 = SearchCondition.HasCategoryByName(PropertyCategoryNames.Transform);

//invert the search, i.e. NOT Has Category By Name

SearchCondition s2 = s1.Negate();

 

2. NET

Next, we tried to make a custom filter using NET. A very basic not equal filter on the entire document could be made with a workaround. Consider three sets of model items. All the items in the model, set A. All the items in the model equal to phase created 1, set B. All the items in the model not equal to phase created B. We know that A = B + C. Therefore C = A -B. I made a script from this, since we already have the equal value, we call B as variable ”results”. We loop through all the items in the model, A, which we get from the variable “doc” and check if it is a member of B and then add it to C. Problem with this script is that it never finishes running, probably because something is wrong with it or my dataset is too large (we have more than 800 thousand items in the model) and the script is inefficient.

 

        public static void NotEqualFilter(Document doc,ModelItemCollection results)
        {
            // Reference collection A. Get the itmes in the current model
            ModelItemCollection referenceItems = doc.Models.RootItem.DescendantsAndSelf;  // This SHOULD select all unhidden items.
            // Build HashSet of B for fast lookup
            HashSet<int> searchHashes = new HashSet<int>();
            foreach (ModelItem item in results)
            {
                searchHashes.Add(item.GetHashCode());
            }
            // Collection C: items in A,referenceItems, but not in B,results.
            ModelItemCollection filteredItems = new ModelItemCollection();
            foreach (ModelItem item in referenceItems)
            {
                if (!searchHashes.Contains(item.GetHashCode()))
                {
                    filteredItems.Add(item);
                }
            }
            // Optional: update current selection
            doc.CurrentSelection.CopyFrom(filteredItems);
        }

 

 3. Linq

The third thing we’ve tried was using linq for finding items, filtering on item.HasGeometry and negating the search condition. See link below but couldn’t get that one to work. So, we quickly went on to try using COM.

https://forums.autodesk.com/t5/navisworks-api-forum/search-only-visible-items-and-exclude-items-that...

 

4. COM

The fourth thing we’ve tried was converting it to COM, which did work. We followed post 6 in https://forums.autodesk.com/t5/navisworks-api-forum/hide-items/td-p/3313411 The script is as follows:

 

doc.CurrentSelection.CopyFrom(results); //results is the ModelItemCollection “equal to”
                InwOpState10 myState = Autodesk.Navisworks.Api.ComApi.ComApiBridge.State;
                InwOpSelection2 myCurrentSelection = myState.CurrentSelection.Copy() as InwOpSelection2;     // Create a copy of current selection
                InwOpSelection2 myRestOfModel = myState.ObjectFactory(nwEObjectType.eObjectType_nwOpSelection, null, null) as InwOpSelection2;    // Create a new empty selection
                myRestOfModel.SelectAll();     // Get the new selection to contain the entire model
                myRestOfModel.SubtractContents(myCurrentSelection);    // Subtract the current selection, so it contains the unselected part of model
                ModelItemCollection netSelection = ComApiBridge.ToModelItemCollection(myRestOfModel);  // Convert back to .net api
                doc.CurrentSelection.CopyFrom(netSelection);

 

Problem with this method is that it didn’t produce the results as expected. It filtered parents and hid them (because we did not filter on HasGeometry), but more problematic, items that were supposed to be hidden or unhidden were not. This would also happen to sibling items under one parent. E.g. filter on phase created not equal to 1, then some phase created 61 items would be hidden and others unhidden with the same parent, same phase creation and with no other explanation. See picture. This indicated to me that something went wrong under the hood. We didn’t do any garbage collection, but we doubt this is the cause, since some items are selected and we use this function only once during the test. At this point we decided it was time to ask for help given the brittle and opaque nature of it all.

niels_deboer_1-1755534928695.png

In conclusion

At this point we found that:

  • Solutions in the documentation don’t work (negate() or NotEqual)
  • Using COM API seems the best path
  • COM is opaque and we found little documentation, but the API reference manual and the 2016 NavisWorks labs
  • We don’t know about memory limitations, and it could just stop adding items to the collection because I try to select more than 700k items.
  • Need to filter out items with no geometry, but this impedes performance.
  • It should also ignore any items that don't have this category instead of selecting them by the not equal filter.
  • We need to implement garbage collection, although we doubt this is the root cause of the issue
  • We believe we overcomplicated things and that there should be an easy way to implement this

Any help would be appreciated!

 

0 Likes
Accepted solutions (2)
272 Views
6 Replies
Replies (6)
Message 2 of 7

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @niels_deboer ,

 

According to the engineering team, in 2026, the entire Find Items functionality is implemented using the .NET API, and it appears that no API extensions were required to achieve this. Could you please confirm whether your Find Items query produces consistent results in both 2025 and 2026?

 

Furthermore, could you clarify the purpose of NotEqual and explain why it is considered incorrect?


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes
Message 3 of 7

niels_deboer
Participant
Participant

Thanks for your quck reply Naveen!

 

I find items in NET using SearchCondition constructor with their given methods. See code, this works completely fine. I also did this for contains and other conditions all present under the search condition methods. 

 
condition = SearchCondition.HasPropertyByDisplayName(Category, Property).EqualValue(VariantData.FromDisplayString(Value.tostring()));
Search search = new Search();
search.Selection.SelectAll();
search.SearchConditions.Add(condition);
ModelItemCollection results = search.FindAll(doc, false);
 
 Unlike in the Navisworks GUI as seen in the first image, there is no "not equal" method under the SearchCondition constructor. Did this change in the 2026 version? Trying to make a not euqal filter didn't work because:
  • There is no "Not Equal" method under SearchCondition constructor.
  • The solution I found to this is appending .Negate() to the condition object. However, this yields an empty ModelItemCollection under the results object when I tried this
  • I coudln't get NotEqual from SearchConditionComparison Enumeration to work and I coudln't find it in the autodesk.navisworks.api namespace where it was supposed to be according to the NET API reference manual. 
  • I tried the three workarounds and that didn't work. COM was most successful, but:
    • This started to go wrong when testing on larger models.
    • Selects items if they do not have "Category" present in the item. It also seems to not consequently do this.
    • Validation shows that items that should pass the filter didn't. Some items with phasing not equal 1 are passed, others are not. SearchCondition logic seems to be lost in the process or could it be a memory issue? 

Why we need not Equal? Phase 1 in our model is the starting phase. Filtering on not equal phase 1 selects all the items that we construct or demolish. By hiding these items we make our reference point for other phasings. 

0 Likes
Message 4 of 7

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @niels_deboer ,

 

Looks like, FindItems uses

public SearchCondition( NamedConstant categoryCombinedName, NamedConstant propertyCombinedName, SearchConditionOptions options, SearchConditionComparison comparison, VariantData value)
to create the SearchCondition with the SearchConditionComparison.NotEqual

Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes
Message 5 of 7

niels_deboer
Participant
Participant

Thanks Naveen,

 

I've tried several things as you suggested, I feel that we are getting closer. Here some code excerpts. I ommit everything, but the filtering.

 

First I tried setting the SearchConditionComparison to NotEqual, but it's ready only. I then tried to use SerachCondition as a method, but I wasn't allowed to use it as a method. Comparewith addresses the SearchConditionComparison property, but I couldn't get it to work. 

 

 

Spoiler

 

To change the property of the constructor I found

 

C# 
public SearchConditionComparison Comparison { get; }
 

 

 

 

1. When trying to build the following it returns that Comparison is read-only, becuase it's {get;}. I did not find a metehod to set Comparison to NotEqual

 

 

condition = SearchCondition.HasPropertyByDisplayName(propertyDisplayName, propertyName)
                                .EqualValue(VariantData.FromDisplayString(targetValue.ToString()));
condition.Comparison = NotEqual;

 

 

2. I tried this, but I cannot use SearchCondition as a method.

 

public static void FilterItems(string propertyDisplayName, string propertyName, string FilterCondition, object targetValue, string filterType) 
{ ...
SearchConditionOptions options = SearchConditionOptions.None;
SearchConditionComparison NotEqual = SearchConditionComparison.NotEqual; 
condition = SearchCondition(propertyDisplayName, propertyName, options, NotEqual, targetValue.ToString());
... }

 

 

3. This builds, but it crashes when running:

public static void FilterItems(string propertyDisplayName, string propertyName, string FilterCondition, object targetValue, string filterType) 
{ ...
SearchCondition.HasPropertyByDisplayName(propertyDisplayName, propertyName).EqualValue(VariantData.FromDisplayString(targetValue.ToString()));
condition.CompareWith(NotEqual, VariantData.FromDisplayString(targetValue.ToString()));
...}

 

4. And the best for last 😁 This builds, I tested it, but it doesn't select anything

public static void FilterItems(string propertyDisplayName, string propertyName, string FilterCondition, object targetValue, string filterType) 
{ ...
SearchCondition propertyCondition = SearchCondition.HasPropertyByName(propertyDisplayName, propertyName);
                            condition = propertyCondition.CompareWith(
                            SearchConditionComparison.NotEqual,
VariantData.FromDisplayString(targetValue.ToString()));
...}

 

5. Edit. Also tried Relfection, but that didn't work. Just behaves as a normal equal filter

using System.Reflection;
public static void FilterItems(string propertyDisplayName, string propertyName, string FilterCondition, object targetValue, string filterType)
{...
condition = SearchCondition.HasPropertyByDisplayName(propertyDisplayName, propertyName).EqualValue(VariantData.FromDisplayString(targetValue.ToString()));
var field = typeof(SearchCondition).GetField("<Comparison>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

if (field != null)
{
field.SetValue(condition, SearchConditionComparison.NotEqual);
}
...}

 

 

0 Likes
Message 6 of 7

naveen.kumar.t
Autodesk Support
Autodesk Support
Accepted solution

Hi @niels_deboer ,

 

I recommend using the NavisLookup tool(https://github.com/chuongmep/NavisLookup) to identify the correct values to use in your code.

Below are the steps I followed:

  1. Create the desired Search Conditions in Find Items via the Navisworks UI.

  2. Go to NavisLookupSnoop Active DocumentCurrent SearchValueSearch ConditionsSearch Condition[0].

  3. You will now see the values, as shown in the attached screenshot. Please see the attached screenshot

  4. Use these same values in the sample code as shown below.

  5. Ensure you are passing the correct data type into VariantData value. For example, if the value is a string, pass a string; if it is a boolean, pass a boolean.

  6. With this search condition, run a search on your document. It should return the same results as the Find Items.

Sample Code:

Document doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
           
NamedConstant categoryCombinedName = null;
NamedConstant propertyCombinedName = new NamedConstant(DataPropertyNames.ItemHidden);
SearchConditionOptions options = SearchConditionOptions.IgnoreNames;
SearchConditionComparison comparison = SearchConditionComparison.NotEqual;
VariantData value = new VariantData(false);
SearchCondition searchCondition = new SearchCondition(categoryCombinedName,propertyCombinedName,options,comparison,value);

Search serach = new Search();
serach.Locations = SearchLocations.Descendants; 
serach.SearchConditions.Add(searchCondition);
serach.Selection.SelectAll();              

ModelItemCollection items = serach.FindAll(doc, true);

 Search Condition.png


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

Message 7 of 7

niels_deboer
Participant
Participant
Accepted solution

Amazing this works! Thanks Naveen,

 

I finally managed to make the NotEqual filter and tested it on a small test sample, which seems to behave correctly. 

 

I've installed Navislookup and found the properties and the following code works for a not equal condition.

niels_deboer_0-1755693621579.png

 

Here is the code for the making the condition for a not equal filter. 

 

NamedConstant category = new NamedConstant("LcSvF_Phasing", "Phasing");
NamedConstant property = new NamedConstant("Phase Created", "Phase Created");

SearchConditionOptions options = SearchConditionOptions.None;
SearchConditionComparison NotEqual = SearchConditionComparison.NotEqual;
VariantData target = new VariantData(targetValue.ToString()); // VariantData.FromDisplayString(targetValue.ToString());
condition = new SearchCondition(category, property, options, NotEqual, target)

 

 

I have found another solution to the problem that doesn't require Navislookup. It is very useful for if you want to automatically apply search sets and expand its capabilities. All you need is the propertyName variable. This is equal to the string shown in the GUI. In Naveen's screenshot case it would be "hidden". This script retrieves the information to create the condition. No need for navislookup. You can use this script. Just make sure to have a selection of items as input, add some code to apply the search condition and clean the code 😂

 

 

public static void FilterItems(string propertyDisplayName, string propertyName, string FilterCondition, object targetValue, string filterType)
{   
    var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
    SearchCondition condition = null;
        // Apppend your own filter conditions, this everything below is only for NotEqual 
    PropertyCategory searchCategory = null;
    DataProperty searchProperty = null;
    string categoryName = null;
    string categoryDisplayName = null;
    string dataPropertyName = null;
    string dataPropertyDisplayName = null;
    
    ModelItem firstItem = null;
    bool found = false;
    foreach (ModelItem item in Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems)
    {
        firstItem = item; 
        // break; }                                 
        if (firstItem != null)
        {
            foreach (PropertyCategory category in firstItem.PropertyCategories)
            {
                foreach (DataProperty dataProperty in category.Properties)
                {
                    if (dataProperty.DisplayName == propertyName)
                    {
                        categoryName = category.Name;
                        categoryDisplayName = category.DisplayName;
                        dataPropertyName = dataProperty.Name;
                        dataPropertyDisplayName = dataProperty.DisplayName;

                        string logPath = @"C:\Data\Navisworks Plugin\navisworks_properties_log.txt";
                        using (StreamWriter writer = new StreamWriter(logPath, true))
                        {
                            writer.WriteLine($"Category name:displayname: {category.Name} : {category.DisplayName}");
                            writer.WriteLine($"Property name:displayname: {dataProperty.Name}: {dataProperty.DisplayName}");
                            
                        }
                        found = true; // Just need the first category and property from the first item
                        goto Done;
                    }
                }
            }
        }
    }
Done:
    if (!found)
    {
        // Log error and Raise exception if we don't find our target value
        File.AppendAllText("navisworks_property_log.txt", "Error! Property not found.\n");
        throw new Exception("Target DataProperty.DisplayName not found in first ModelItem.");
    }
        
    Application.ActiveDocument.CurrentSelection.Clear();
    // Execute not equal search
    NamedConstant search_category = new NamedConstant(categoryName, categoryDisplayName); // see named constant in the documentation
    NamedConstant search_property = new NamedConstant(dataPropertyName, dataPropertyDisplayName);
    SearchConditionOptions options = SearchConditionOptions.None;
    SearchConditionComparison NotEqual = SearchConditionComparison.NotEqual;
    VariantData target = new VariantData(targetValue.ToString()); 
    condition = new SearchCondition(search_category, search_property, options, NotEqual, target);
// Then add your script to apply the condition to select the items. 
}

 

 

 

 

 

0 Likes