@Override
  protected void addSearchLocalizedTerm(
      BooleanQuery searchQuery, SearchContext searchContext, String field, boolean like)
      throws Exception {

    if (Validator.isNull(field)) {
      return;
    }

    String value = String.valueOf(searchContext.getAttribute(field));

    if (Validator.isNull(value)) {
      value = searchContext.getKeywords();
    }

    if (Validator.isNull(value)) {
      return;
    }

    field = DocumentImpl.getLocalizedName(searchContext.getLocale(), field);

    if (searchContext.isAndSearch()) {
      searchQuery.addRequiredTerm(field, value, like);
    } else {
      searchQuery.addTerm(field, value, like);
    }
  }
  @Override
  public void postProcessContextQuery(BooleanQuery contextQuery, SearchContext searchContext)
      throws Exception {

    addStatus(contextQuery, searchContext);

    if (searchContext.isIncludeAttachments()) {
      addRelatedClassNames(contextQuery, searchContext);
    }

    contextQuery.addRequiredTerm(Field.HIDDEN, searchContext.isIncludeAttachments());

    addSearchClassTypeIds(contextQuery, searchContext);

    String ddmStructureFieldName = (String) searchContext.getAttribute("ddmStructureFieldName");
    Serializable ddmStructureFieldValue = searchContext.getAttribute("ddmStructureFieldValue");

    if (Validator.isNotNull(ddmStructureFieldName) && Validator.isNotNull(ddmStructureFieldValue)) {

      String[] ddmStructureFieldNameParts =
          StringUtil.split(ddmStructureFieldName, StringPool.SLASH);

      DDMStructure structure =
          DDMStructureLocalServiceUtil.getStructure(
              GetterUtil.getLong(ddmStructureFieldNameParts[1]));

      String fieldName =
          StringUtil.replaceLast(
              ddmStructureFieldNameParts[2],
              StringPool.UNDERLINE.concat(LocaleUtil.toLanguageId(searchContext.getLocale())),
              StringPool.BLANK);

      try {
        ddmStructureFieldValue =
            DDMUtil.getIndexedFieldValue(ddmStructureFieldValue, structure.getFieldType(fieldName));
      } catch (StructureFieldException sfe) {
      }

      contextQuery.addRequiredTerm(
          ddmStructureFieldName, StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);
    }

    String[] mimeTypes = (String[]) searchContext.getAttribute("mimeTypes");

    if (ArrayUtil.isNotEmpty(mimeTypes)) {
      BooleanQuery mimeTypesQuery = BooleanQueryFactoryUtil.create(searchContext);

      for (String mimeType : mimeTypes) {
        mimeTypesQuery.addTerm(
            "mimeType", StringUtil.replace(mimeType, CharPool.FORWARD_SLASH, CharPool.UNDERLINE));
      }

      contextQuery.add(mimeTypesQuery, BooleanClauseOccur.MUST);
    }
  }
  @Override
  public void postProcessContextBooleanFilter(
      BooleanFilter contextBooleanFilter, SearchContext searchContext) throws Exception {

    Long classNameId = (Long) searchContext.getAttribute(Field.CLASS_NAME_ID);

    if ((classNameId != null) && (classNameId != 0)) {
      contextBooleanFilter.addRequiredTerm(Field.CLASS_NAME_ID, classNameId.toString());
    }

    addStatus(contextBooleanFilter, searchContext);

    addSearchClassTypeIds(contextBooleanFilter, searchContext);

    String ddmStructureFieldName = (String) searchContext.getAttribute("ddmStructureFieldName");
    Serializable ddmStructureFieldValue = searchContext.getAttribute("ddmStructureFieldValue");

    if (Validator.isNotNull(ddmStructureFieldName) && Validator.isNotNull(ddmStructureFieldValue)) {

      QueryFilter queryFilter =
          _ddmIndexer.createFieldValueQueryFilter(
              ddmStructureFieldName, ddmStructureFieldValue, searchContext.getLocale());

      contextBooleanFilter.add(queryFilter, BooleanClauseOccur.MUST);
    }

    String ddmStructureKey = (String) searchContext.getAttribute("ddmStructureKey");

    if (Validator.isNotNull(ddmStructureKey)) {
      contextBooleanFilter.addRequiredTerm("ddmStructureKey", ddmStructureKey);
    }

    String ddmTemplateKey = (String) searchContext.getAttribute("ddmTemplateKey");

    if (Validator.isNotNull(ddmTemplateKey)) {
      contextBooleanFilter.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
    }

    boolean head = GetterUtil.getBoolean(searchContext.getAttribute("head"), Boolean.TRUE);
    boolean relatedClassName =
        GetterUtil.getBoolean(searchContext.getAttribute("relatedClassName"));
    boolean showNonindexable =
        GetterUtil.getBoolean(searchContext.getAttribute("showNonindexable"));

    if (head && !relatedClassName && !showNonindexable) {
      contextBooleanFilter.addRequiredTerm("head", Boolean.TRUE);
    }

    if (!relatedClassName && showNonindexable) {
      contextBooleanFilter.addRequiredTerm("headListable", Boolean.TRUE);
    }
  }
  @Override
  protected Map<String, Query> addSearchLocalizedTerm(
      BooleanQuery searchQuery, SearchContext searchContext, String field, boolean like)
      throws Exception {

    if (Validator.isNull(field)) {
      return Collections.emptyMap();
    }

    String value = String.valueOf(searchContext.getAttribute(field));

    if (Validator.isNull(value)) {
      value = searchContext.getKeywords();
    }

    if (Validator.isNull(value)) {
      return Collections.emptyMap();
    }

    String localizedField = DocumentImpl.getLocalizedName(searchContext.getLocale(), field);

    Map<String, Query> queries = new HashMap<>();

    if (Validator.isNull(searchContext.getKeywords())) {
      BooleanQuery localizedQuery = new BooleanQueryImpl();

      Query query = localizedQuery.addTerm(field, value, like);

      queries.put(field, query);

      Query localizedFieldQuery = localizedQuery.addTerm(localizedField, value, like);

      queries.put(field, localizedFieldQuery);

      BooleanClauseOccur booleanClauseOccur = BooleanClauseOccur.SHOULD;

      if (searchContext.isAndSearch()) {
        booleanClauseOccur = BooleanClauseOccur.MUST;
      }

      searchQuery.add(localizedQuery, booleanClauseOccur);
    } else {
      Query query = searchQuery.addTerm(localizedField, value, like);

      queries.put(field, query);
    }

    return queries;
  }
  @Override
  protected void addSearchKeywords(BooleanQuery searchQuery, SearchContext searchContext)
      throws Exception {

    String keywords = searchContext.getKeywords();

    if (Validator.isNull(keywords)) {
      return;
    }

    super.addSearchKeywords(searchQuery, searchContext);

    String field = DocumentImpl.getLocalizedName(searchContext.getLocale(), "localized_title");

    searchQuery.addTerm(field, keywords, true);
  }
  protected static AssetSearcher getAssetSearcher(
      SearchContext searchContext, AssetEntryQuery assetEntryQuery, int start, int end)
      throws Exception {

    Indexer<?> searcher = AssetSearcher.getInstance();

    AssetSearcher assetSearcher = (AssetSearcher) searcher;

    assetSearcher.setAssetEntryQuery(assetEntryQuery);

    Layout layout = assetEntryQuery.getLayout();

    if (layout != null) {
      searchContext.setAttribute(Field.LAYOUT_UUID, layout.getUuid());
    }

    String ddmStructureFieldName = (String) assetEntryQuery.getAttribute("ddmStructureFieldName");
    Serializable ddmStructureFieldValue = assetEntryQuery.getAttribute("ddmStructureFieldValue");

    if (Validator.isNotNull(ddmStructureFieldName) && Validator.isNotNull(ddmStructureFieldValue)) {

      searchContext.setAttribute("ddmStructureFieldName", ddmStructureFieldName);
      searchContext.setAttribute("ddmStructureFieldValue", ddmStructureFieldValue);
    }

    String paginationType = GetterUtil.getString(assetEntryQuery.getPaginationType(), "more");

    if (!paginationType.equals("none") && !paginationType.equals("simple")) {

      searchContext.setAttribute("paginationType", paginationType);
    }

    searchContext.setClassTypeIds(assetEntryQuery.getClassTypeIds());
    searchContext.setEnd(end);
    searchContext.setGroupIds(assetEntryQuery.getGroupIds());

    if (Validator.isNotNull(assetEntryQuery.getKeywords())) {
      searchContext.setLike(true);
    }

    searchContext.setSorts(getSorts(assetEntryQuery, searchContext.getLocale()));
    searchContext.setStart(start);

    return assetSearcher;
  }
  @Override
  public void postProcessContextBooleanFilter(
      BooleanFilter contextBooleanFilter, SearchContext searchContext) throws Exception {

    addStatus(contextBooleanFilter, searchContext);

    if (searchContext.isIncludeAttachments()) {
      addRelatedClassNames(contextBooleanFilter, searchContext);
    }

    if (ArrayUtil.contains(
        searchContext.getFolderIds(), DLFolderConstants.DEFAULT_PARENT_FOLDER_ID)) {

      contextBooleanFilter.addRequiredTerm(Field.HIDDEN, searchContext.isIncludeAttachments());
    }

    addSearchClassTypeIds(contextBooleanFilter, searchContext);

    String ddmStructureFieldName = (String) searchContext.getAttribute("ddmStructureFieldName");
    Serializable ddmStructureFieldValue = searchContext.getAttribute("ddmStructureFieldValue");

    if (Validator.isNotNull(ddmStructureFieldName) && Validator.isNotNull(ddmStructureFieldValue)) {

      String[] ddmStructureFieldNameParts =
          StringUtil.split(ddmStructureFieldName, DDMIndexer.DDM_FIELD_SEPARATOR);

      DDMStructure structure =
          DDMStructureLocalServiceUtil.getStructure(
              GetterUtil.getLong(ddmStructureFieldNameParts[1]));

      String fieldName =
          StringUtil.replaceLast(
              ddmStructureFieldNameParts[2],
              StringPool.UNDERLINE.concat(LocaleUtil.toLanguageId(searchContext.getLocale())),
              StringPool.BLANK);

      try {
        ddmStructureFieldValue =
            DDMUtil.getIndexedFieldValue(ddmStructureFieldValue, structure.getFieldType(fieldName));
      } catch (StructureFieldException sfe) {
        if (_log.isDebugEnabled()) {
          _log.debug(sfe, sfe);
        }
      }

      BooleanQuery booleanQuery = new BooleanQueryImpl();

      booleanQuery.addRequiredTerm(
          ddmStructureFieldName, StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);

      contextBooleanFilter.add(new QueryFilter(booleanQuery));
    }

    String[] mimeTypes = (String[]) searchContext.getAttribute("mimeTypes");

    if (ArrayUtil.isNotEmpty(mimeTypes)) {
      BooleanFilter mimeTypesBooleanFilter = new BooleanFilter();

      for (String mimeType : mimeTypes) {
        mimeTypesBooleanFilter.addTerm(
            "mimeType", StringUtil.replace(mimeType, CharPool.FORWARD_SLASH, CharPool.UNDERLINE));
      }

      contextBooleanFilter.add(mimeTypesBooleanFilter, BooleanClauseOccur.MUST);
    }
  }
  public void postProcessContextQuery(BooleanQuery contextQuery, SearchContext searchContext)
      throws Exception {

    if (_log.isDebugEnabled()) {
      _log.debug(" postProcessContextQuery()");
    }

    Map<String, Object> params = (Map<String, Object>) searchContext.getAttribute("params");

    if (params == null) {
      return;
    }

    Map<String, Serializable> searchByFields =
        (Map<String, Serializable>) params.get(SampleConstants.STRUCTURE_FIELDS);

    String structureKey = (String) params.get(SampleConstants.STRUCTURE_KEY);

    Long groupId = (Long) params.get(SampleConstants.STRUCTURE_GROUP_ID);

    Locale locale = searchContext.getLocale();

    DDMStructure structure =
        DDMStructureLocalServiceUtil.fetchStructure(
            groupId, PortalUtil.getClassNameId(JournalArticle.class), structureKey, true);

    if (!isCustomSearch(structure, groupId, searchByFields)) {
      return;
    }

    for (String fieldName : searchByFields.keySet()) {
      try {
        Serializable value = searchByFields.get(fieldName);

        String indexType = structure.getFieldProperty(fieldName, "indexType");

        if (Validator.isNull(indexType)) {
          if (_log.isDebugEnabled()) {
            _log.debug("fieldName " + fieldName + " is not indexable");
          }

          continue;
        }

        // some types are stored in a special way

        try {
          value = DDMUtil.getIndexedFieldValue(value, structure.getFieldType(fieldName));
        } catch (StructureFieldException sfe) {
          _log.error(sfe);

          continue;
        }

        contextQuery.addRequiredTerm(
            DDMIndexerUtil.encodeName(structure.getStructureId(), fieldName, locale),
            StringPool.QUOTE + value + StringPool.QUOTE);
      } catch (Exception e) {
        _log.error("Error processing custom search", e);
      }
    }

    _log.debug(contextQuery);
  }
  @Override
  public void postProcessContextBooleanFilter(
      BooleanFilter contextBooleanFilter, SearchContext searchContext) throws Exception {

    Long classNameId = (Long) searchContext.getAttribute(Field.CLASS_NAME_ID);

    if ((classNameId != null) && (classNameId != 0)) {
      contextBooleanFilter.addRequiredTerm(Field.CLASS_NAME_ID, classNameId.toString());
    }

    addStatus(contextBooleanFilter, searchContext);

    addSearchClassTypeIds(contextBooleanFilter, searchContext);

    String ddmStructureFieldName = (String) searchContext.getAttribute("ddmStructureFieldName");
    Serializable ddmStructureFieldValue = searchContext.getAttribute("ddmStructureFieldValue");

    if (Validator.isNotNull(ddmStructureFieldName) && Validator.isNotNull(ddmStructureFieldValue)) {

      String[] ddmStructureFieldNameParts =
          StringUtil.split(ddmStructureFieldName, DDMIndexer.DDM_FIELD_SEPARATOR);

      DDMStructure structure =
          _ddmStructureLocalService.getStructure(GetterUtil.getLong(ddmStructureFieldNameParts[1]));

      String fieldName =
          StringUtil.replaceLast(
              ddmStructureFieldNameParts[2],
              StringPool.UNDERLINE.concat(LocaleUtil.toLanguageId(searchContext.getLocale())),
              StringPool.BLANK);

      if (structure.hasField(fieldName)) {
        ddmStructureFieldValue =
            DDMUtil.getIndexedFieldValue(ddmStructureFieldValue, structure.getFieldType(fieldName));
      }

      BooleanQuery booleanQuery = new BooleanQueryImpl();

      booleanQuery.addRequiredTerm(
          ddmStructureFieldName, StringPool.QUOTE + ddmStructureFieldValue + StringPool.QUOTE);

      contextBooleanFilter.add(new QueryFilter(booleanQuery));
    }

    String articleType = (String) searchContext.getAttribute("articleType");

    if (Validator.isNotNull(articleType)) {
      contextBooleanFilter.addRequiredTerm(Field.TYPE, articleType);
    }

    String ddmStructureKey = (String) searchContext.getAttribute("ddmStructureKey");

    if (Validator.isNotNull(ddmStructureKey)) {
      contextBooleanFilter.addRequiredTerm("ddmStructureKey", ddmStructureKey);
    }

    String ddmTemplateKey = (String) searchContext.getAttribute("ddmTemplateKey");

    if (Validator.isNotNull(ddmTemplateKey)) {
      contextBooleanFilter.addRequiredTerm("ddmTemplateKey", ddmTemplateKey);
    }

    boolean head = GetterUtil.getBoolean(searchContext.getAttribute("head"), Boolean.TRUE);
    boolean relatedClassName =
        GetterUtil.getBoolean(searchContext.getAttribute("relatedClassName"));

    if (head && !relatedClassName) {
      contextBooleanFilter.addRequiredTerm("head", Boolean.TRUE);
    }
  }