Supporting Content Type Searching

Version 1

    You can add support that allows people to find instances of your content type when searching. The content in this topic assumes you've implemented a manager class to locate content type instances based on their ID in the system.

     

    Note: The code in this topic is from the Memo sample content type. You'll find the sample in the Jive public sample Subversion repository.

     

    Note: The content type API is still a new feature that might change as developers provide feedback about it.

     

    Here are the high-level steps. You'll find more about each in the sections below.

    • Describe how to index your instance content by implementing IndexInfoProvider.
    • Signal that your content type supports searching by implementing IndexableType or FilteredIndexableType.

    Provide Information About Search Support

    Implement the IndexableTypeInfoProvider interface to describe how your content type supports searching, including how instance content should be structured for building the search index. Application search support is provided by Lucene, an open source search engine that views your instance's content as fields of text. To support this view, you map your content type's instance data to fields from which Lucene will create an index for searching.

    • Create an info provider class that implements IndexInfoProvider, such as by extending BaseIndexInfoProvider.
    • Add setters that Spring will use to inject instances your code will need.

     

    The following example implements IndexInfoProvider by extending BaseIndexInfoProvider. Extending the base class should meet the needs of most content types that will support search. In your implementation, you return the SQL statements through which the application can query for the instances that the user wants to search within -- such as those within a particular modification date range. Your class will also describe how the data in instances should be mapped to fields that will be indexed.

    • Create an index info provider class and provide a set method through which Spring can inject an instance of your manager class. Your code will use this manager to get a particular memo to be indexed.
    /**
     * Provides information about how this content type supports being searchable.
     */
    public class MemoIndexInfoProvider extends BaseIndexInfoProvider {
    
        private MemoManager memoManager;
    
        /**
         * Called by Spring to inject a memo manager instance.
         *
         * @param memoManager The injected instance.
         */
        public void setMemoManager(MemoManager memoManager) {
            this.memoManager = memoManager;
        }
    
        // Code follows.
    }
    
    • Add the SQL statements and accessor methods through which the application will retrieve the memo instances according to range parameters specified by the person who's doing the searching. Here's you're overriding base class methods that are called by other base class code.
    // SQL statements returned by methods defined in this class.
    public static final String MAX_ID = "SELECT MAX(memoID) FROM jiveMemo WHERE modificationDate <= ?";
    public static final String COUNT = "SELECT count(*) FROM jiveMemo WHERE modificationDate <= ?";
    public static final String MIN_ID = "SELECT MIN(memoID) FROM jiveMemo WHERE modificationDate >= ?";
    public static final String IDS = "SELECT memoID FROM jiveMemo WHERE " + "memoID >= ? " + "AND memoID <= ? "
            + "AND modificationDate >= ? " + "AND modificationDate <= ?";
    
    /**
     * Gets the SQL statement that queries for the count of memo instances
     * modified after a specified date. This is called by the base class
     * implementation of getID, which specifies the date.
     *
     * @return The SQL statement.
     */
    protected String getCountSQL() {
        return COUNT;
    }
    
    /**
     * Gets the SQL statement that queries for the largest ID of memo
     * instances modified after a specified date. This is called by the base class
     * implementation of getMaxID, which specifies the date.
     *
     * @return The SQL statement.
     */
    protected String getMaxIDSQL() {
        return MAX_ID;
    }
    
    /**
     * Gets the SQL statement that queries for the smallest ID of memo
     * instances modified after a specified date. This is called by the base class
     * implementation of getMinID, which specifies the date.
     *
     * @return The SQL statement.
     */
    protected String getMinIDSQL() {
        return MIN_ID;
    }
    
    /**
     * Gets the IDs of memo instances modified within a specified date range, and whose
     * IDs are within a particular range.
     *
     * @return The SQL statement.
     */
    protected String getIDsSQL() {
        return IDS;
    }
    
    • Describe how instance data should be mapped to the fields that will be indexed by Lucene. The Jive IndexField enumeration lists many field types to which you can map content.
    /**
     * Gets the fields that should be included in the search index for the specified
     * memo instance. Each of the indexable fields is defined by the application's
     * search API. Instance data is mapped to these fields in order to structure them
     * for indexing.
     *
     * @param id The ID of the memo to add to the index.
     * @return The field map, in which fields are keys and instance data are values.
     */
    protected Map<IndexField, String> getIndexFields(long id) {
        Memo content = memoManager.getMemo(id);
        Map<IndexField, String> fields = new HashMap<IndexField, String>();
        if (content != null) {
            fields.put(IndexField.objectID, "" + content.getID());
            fields.put(IndexField.objectType, "" + content.getObjectType());
            fields.put(IndexField.subject, StringUtils.trimToEmpty(content.getSubject()));
            fields.put(IndexField.creationDate, "" + content.getCreationDate().getTime());
            fields.put(IndexField.body, "" + content.getPlainBody());
            fields.put(IndexField.modificationDate, "" + content.getModificationDate().getTime());
            fields.put(IndexField.containerID, "" + content.getContainerID());
            fields.put(IndexField.containerType, "" + content.getContainerType());
            fields.put(IndexField.tags, tagManager.getTagsAsString(content));
        }
        return fields;
    }
    
    • Implement other methods expected by the search subsystem.
    /**
     * Get the type ID for memos.
     *
     * @return The type ID.
     */
    protected int getObjectTypeID() {
        return MemoObjectType.MEMO_TYPE_ID;
    }
    
    /**
     * Gets the language that the search engine will assume when
     * analyzing memo content.
     *
     * @param id The ID of the memo instance.
     * @return The language, or null if there is none.
     */
    protected String getLanguage(long id) {
        // There's no defined language for memos.
        return null;
    }
    
    /**
     * Called by the application to discover whether the specified
     * Jive object is indexable. Returns true for an instance of Memo.
     *
     * @param jiveObject The object that might be searchable.
     * @return true if jiveObject is an instance of the memo content
     * type; otherwise, false.
     */
    public boolean isIndexable(JiveObject jiveObject) {
        return jiveObject instanceof Memo;
    }
    
    /**
     * Called by the application to discover whether the specified
     * Jive object can be viewed by the user who is requesting it.
     *
     * @param The requested Jive object.
     * @return true if jiveObject is a memo instances that can be viewed
     * by the requesting user.
     */
    public boolean getCanViewObject(JiveObject jiveObject) {
        // Use MemoPermHelper to get this permission based on the current
        // execution context.
        return MemoPermHelper.canReadMemo((Memo) jiveObject);
    }
    
    • Configuring Spring to inject the instances that your index info provider will need.
    <bean id="memoIndexInfoProvider" class="com.jivesoftware.clearspace.plugin.test_dynamic.action.MemoIndexInfoProvider">
        <property name="dataSource" ref="searchDataSource"/>
        <property name="memoManager" ref="memoManagerImpl"/>
        <property name="tagManager" ref="tagManagerImpl" />
    </bean>
    

    Signal Support for Search

    Tell the application that your content type supports searching by implementing the IndexableType interface. You can also implement FilteredIndexableType, which extends IndexableType, if you want to signal that your content type can be used to filter search results.

    • Implement the interface, which has one method to return your index info provider.
    public class MemoObjectType implements IndexableType {
    
        // Field and accessors for your info provider instance. Spring will
        // use the set method to inject the instance; the get method is
        // part of the IndexableType interface.
        private IndexInfoProvider indexInfoProvider;
        public void setIndexInfoProvider(IndexInfoProvider indexInfoProvider) {
            this.indexInfoProvider = indexInfoProvider;
        }
        public IndexInfoProvider getIndexInfoProvider() {
            return indexInfoProvider;
        }
    }
    
    • Configure Spring to inject the index info provider instance into your object type class.
    <bean id="memoObjectType" class="com.jivesoftware.clearspace.plugin.test_dynamic.MemoObjectType">
        <property name="indexInfoProvider" ref="memoIndexInfoProvider"/>
    </bean>