Search code examples
orchardcms

Field index for queries not updating when value set programmatically


My module creates a custom content item through the controller:

   private ContentItem createContentItem()
    {
        // Add the field
        _contentDefinitionManager.AlterPartDefinition(
            "TestType",
            cfg => cfg
            .WithField(
                "NewField",
                f => f
                    .OfType(typeof(BooleanField).Name)
                    .WithDisplayName("New Field"))
            );

        // Not sure if this is needed
        _contentDefinitionManager.AlterTypeDefinition(
            "TestType",
            cfg => cfg
                .WithPart("TestType")
            );

        // Create new TestType item
        var newItem = _contentManager.New("TestType");
        _contentManager.Create(TestItem, VersionOptions.Published);

        // Set the added boolean field to true
        BooleanField newField = ((dynamic)newItem).TestType.NewField as BooleanField;
        newField.Value = true;

        // Set title (as date created, for convenience)
        var time = DateTime.Now.ToString("MM-dd-yyyy h:mm:ss tt", CultureInfo.InvariantCulture).Replace(':', '.');
        newItem.As<TitlePart>().Title = time;

        return newItem;
    }

The end result of this is a new TestType item with a field that's set to true. Viewing the content item in the dashboard as well as examining ContentItemVersionRecord in the database confirms that the value was set correctly.

However, queries don't seem to work properly on fields that are set in this manner. I found the record IntegerFieldIndexRecord, which is what I assume projections use to fill query result pages. On this, the value of TestField remains at 0 (false), instead of 1 (true).

Going to the content item edit page and simply clicking 'save' updates IntegerFieldIndexRecord correctly, meaning that the value is now picked up by the query. How can the record be updated for field values set programmatically?

Relevant section of migration:

SchemaBuilder.CreateTable(typeof(TestTypePartRecord).Name, table => table
            .ContentPartRecord()
        );

        ContentDefinitionManager.AlterTypeDefinition(
            "TestType",
            cfg => cfg
                .DisplayedAs("Test Type")
                .WithPart(typeof(TitlePart).Name)
                .WithPart(typeof(ContainablePart).Name)
                .WithPart(typeof(CommonPart).Name)
                .WithPart(typeof(IdentityPart).Name)
            );

Edit: The fix for this is to manually change the projection index record whenever changing a field value, using this call:

_fieldIndexService.Set(testResultItem.As<FieldIndexPart>(),
     "TestType", // Resolves as TestTypePart, which holds the field
     "newField",
     "", // Not sure why value name should be empty, but whatever
     true, // The value to be set goes here
     typeof(bool));

Solution

  • There are 2 ways to fix this:

    1) Ensure the newly created item is getting published by calling ContentManager.Publish() as Orchard.Projections.Handlers.FieldIndexPartHandler listens to the publish event to update the FieldIndexPartRecord

    2) use IFieldIndexService to update FieldIndexPartRecord manually, see implementation of Orchard.Projections.Handlers.FieldIndexPartHandler to get in idea how to do this

    Hope this helps.

    :edit

    Due to calling Create(...Published) the ContentManager.Published() won't do anything as the item is already considered published.

    You can do the following to force the publish logic to run:

    bool itemPublished = newItem.VersionRecord.Published;
    
    // unpublish item first when it is already published as ContentManager.Publish() internally first checks for published flag and when set it aborts silently
    //  -> this behaviour prevents calling publish listeners 
    if (itemPublished)
      _contentManager.Unpublish(newItem);
    
    // the following call will result in calls to IContentHandler.Publishing() / IContentHandler.Published()
    _contentManager.Publish(newItem);
    

    or just create the item as a draft and publish it when everything is setup correctly.