Registering Custom Document Types with Solr in Telligent 6.x

Registering Custom Document Types with Solr in Telligent 6.x

This question is answered

I am trying to add a customType to with solr following the example code from this post:

telligent.com/.../1357309.aspx

But it seems things have changed between 5.x and 6.x as now the search indexing is performed from the task service. I have performed the following taks:

1. Written a GroupMapper class for my custom type.

2. Written a ContentHandler Class for my type.

3. Registered MyContentHandler in the tasks.config handler section.

4. Copied the complied DLL for my types over to the Jobscheduler folder.

Now the strange part is that I have added logging to both MyContentHandler and MyGroupMapper, and MyGroupmapper is being called, but MyContentHandler isn’t even though it is referenced in tasks.config. How does this work now, do you not have to create a ContentHandler in 6.0, or is there another way to register it that i am unaware of

Here is my line in task.config:

<add key="handler-2" value="MyProject.MyComponents.Search.MyContentHandler, MyProject.MyComponents "/>

namespace Myprojects.MyComponents.Search

{

   public class MyDocumentMapper : DocumentMapper

   {

       private readonly ISecurityService _securityService = Services.Get<ISecurityService>();

       public override void Load()

       {

           Telligent.Evolution.Components.EventLogs.Info("MyDocumentMapper Load started...", "myproject", -1);

           Map<MyGroup>(group =>

           {

               try

               {

                   SearchCategory.Group.MapTo(SearchFields.Category);

                   SearchContentType.Group.MapTo(SearchFields.ContentType);

                   String.Format("{0}:{1}", SearchContentType.Group, group.ID).MapTo(SearchFields.CollapseField);

                   string name = TextFormatter.CleanTextForIndexing(group.Name);

                   name.MapTo(SearchFields.Title);

//more fields here

                  //custom fields

                   group.Category.MapTo("group_categories");

                   group.Tags.MapTo("group_tags");

                   group.MapPermissionsTo(_securityService.For(group).GetRolesFor(GroupPermission.DiscoverGroup), SearchFields.Roles);

               }

               catch (Exception ex)

               {

                   throw new CSException(CSExceptionType.SearchIndexingError, string.Format(

                                             "Error while mapping group '{0}'. Message: {1} Object Type:{2}. See inner exception for more details",

                                             group.ID, ex.Message, group.GetType()), ex);

               }

           });

           Rehydrate<MyGroup>(id => new MyGroup(int.Parse(id.ToString())));

           Telligent.Evolution.Components.EventLogs.Info("MyDocumentMapper Load ended...", "myprjetc", -1);

       }

       public override int Priority

       {

           get { return 20; }

       }

   }

namespace MyProject.MyComponents.Search

{

   public class MyContentHandler : BaseContentHandler

   {

       public MyContentHandler() { }

       public MyContentHandler(IContentCalls content, ISearchIndexProvider contentIndexProvider) : base(content, contentIndexProvider) { }

       public MyContentHandler(IContentCalls content, ISearchIndexProvider contentIndexProvider, ICoreSearchIndexProvider coreIndexProvider) : base(content, contentIndexProvider, coreIndexProvider) { }

       static readonly GroupType[] IndexableGroupTypes = new[] { GroupType.Joinless, GroupType.PublicOpen, GroupType.PublicClosed, GroupType.PrivateListed, GroupType.PrivateUnlisted };

       public override int IndexDocuments(int count)

       {

           try

           {

               Telligent.Evolution.Components.EventLogs.Info("MyContentHandler IndexDocuments started...", "MyProject", -1);

               var query = new GroupQuery

               {

                   ExcludeGroupIDs = new List<int> { Groups.GroupService.GetRootGroup().ID },

                   GroupTypes = IndexableGroupTypes,

                   PageSize = count,

                   IgnorePermissions = true,

                   IsIndexed = false

               };

               var groups = Content.GetObjectsToIndex<Group>(() => CommonDataProvider.Instance().GetGroups(query).Items);

               var myGroups = ConvertToMyGroups(groups);

               int processed = Services.Get<IMultipleSearchIndexesProvider>().UpdateIndex(myGroups.ToMappedDocumentCollection(), Service, CoreService);

               if (processed > 0)

                   Content.MarkObjectsAsIndexed(() => Groups.GroupService.UpdateIndexStatus((from g in groups select g.ID).ToArray(), true));

               Telligent.Evolution.Components.EventLogs.Info("MyContentHandler IndexDocuments complete...", "MyProject", -1);

               return groups.Count;

           }

           catch (Exception ex)

           {

               if (ex is TargetInvocationException && ex.InnerException is CSException)

                   (ex.InnerException as CSException).Log();

               else

                   new CSException(CSExceptionType.SearchIndexingError, ex.Message, ex).Log();

               throw;

           }

       }

       private List<MyGroup> ConvertToMyGroups(List<Group> groups)

       {

           List<MyGroup> myGroupsGroups = new List<MyGroup>();

           foreach (var group in groups)

           {

               myGroupsGroups.Add(new MyGroup(group.ID));

           }

           return myGroupsGroups;

       }

   }

}

Verified Answer
  • Are you using the SQL job store (which is the default for the job scheduler)?

    Before reviewing the code the behavior sounds like the recently added content handler is not being recognized.  The list of registered ContentHandlers is stored in job state. If using the SQL store you may not be picking up this change.  Can you try clearing out the job state and retry.  

    delete from QRTZ_CRON_TRIGGERS where TRIGGER_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

    delete from QRTZ_TRIGGERS where TRIGGER_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

    delete from QRTZ_JOB_DETAILS where JOB_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

All Replies
  • Are you using the SQL job store (which is the default for the job scheduler)?

    Before reviewing the code the behavior sounds like the recently added content handler is not being recognized.  The list of registered ContentHandlers is stored in job state. If using the SQL store you may not be picking up this change.  Can you try clearing out the job state and retry.  

    delete from QRTZ_CRON_TRIGGERS where TRIGGER_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

    delete from QRTZ_TRIGGERS where TRIGGER_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

    delete from QRTZ_JOB_DETAILS where JOB_NAME = 'Telligent.Evolution.Search.Tasks.SearchIndexingContentHandlerTask, Telligent.Evolution.Search'

  • many thanks for your help kevin, that has done the trick.

    I do have a further issue though, were my MyDocumentMapper code that maps the fields is not being called.

    From the Event log i can see "MyDocumentMapper Load started" gets hit, as does "MyContentHandler IndexDocuments complete...", but i added the "MyDocumentMapper mapping started..." but this not being hit.

    any ideas why this might be, does MyGroup have to implement anything to support this?

    public override void Load()

          {

              Telligent.Evolution.Components.EventLogs.Info("MyDocumentMapper Load started...", "myproject", -1);

              Map<MyGroup>(group =>

              {

                  try

                  {

                    Telligent.Evolution.Components.EventLogs.Info("MyDocumentMapper mapping started...", "myproject", -1);                  

                     SearchCategory.Group.MapTo(SearchFields.Category);

                      SearchContentType.Group.MapTo(SearchFields.ContentType);

                      String.Format("{0}:{1}", SearchContentType.Group, group.ID).MapTo(SearchFields.CollapseField);

  • Just to support this, here is the additional fields i am trying to add in my solr schema.config:

      <field name="group_categories" type="string" indexed="true" stored="true" default="None"/>

      <field name="group_tags" type="string" indexed="true" stored="true" default="None"/>

  • The content handler in the example is calling mapping (via .ToMappedCollection()) on the groups collection returned by the call to the Groups API. The datatypes here are Group(s), not MyGroups.  The mapper, MyDocumentMapper, is registered for mappings against MyGroup classes/entities (line Map<MyGroup> does this).  Based on the ContentHandler you have I think your mapper should just be against group, ex. Map<Group>, and simply add whatever you need OR your content handler needs to return a set of MyGroup entities.

  • that makes sense, many thanks Kevin.