/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.commands.catalog; import java.util.List; import org.apache.karaf.shell.api.action.Argument; import org.apache.karaf.shell.api.action.Command; import org.apache.karaf.shell.api.action.Option; import org.apache.karaf.shell.api.action.lifecycle.Service; import org.codice.ddf.commands.catalog.facade.CatalogFacade; import org.fusesource.jansi.Ansi; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.opengis.filter.Filter; import ddf.catalog.data.Metacard; import ddf.catalog.data.Result; import ddf.catalog.operation.SourceResponse; import ddf.catalog.operation.impl.QueryImpl; import ddf.catalog.operation.impl.QueryRequestImpl; import ddf.util.XPathHelper; @Service @Command(scope = CatalogCommands.NAMESPACE, name = "search", description = "Searches records in the Catalog Provider.") public class SearchCommand extends CqlCommands { private static final String ID = "ID "; private static final String TITLE = "Title "; private static final String DATE = "Modified "; private static final String EXCERPT = "Excerpt "; private static final int TITLE_MAX_LENGTH = 30; private static final int EXCERPT_MAX_LENGTH = 50; @Argument(name = "NUMBER_OF_ITEMS", description = "Number of maximum records to display.", index = 1, multiValued = false, required = false) int numberOfItems = -1; @Argument(name = "SEARCH_PHRASE", index = 0, multiValued = false, required = false, description = "Phrase to query the Catalog Provider. Will take precedence over --searchPhrase option.") String searchPhraseArgument = WILDCARD; @Option(name = "--cache", required = false, multiValued = false, aliases = {}, description = "Only search cached entries.") boolean cache = false; @Override protected Object executeWithSubject() throws Exception { searchPhrase = searchPhraseArgument; final Filter filter = getFilter(); if (this.cache) { return executeSearchCache(filter); } else { return executeSearchStore(filter); } } private Object executeSearchStore(Filter filter) throws Exception { String formatString = "%1$-33s %2$-26s %3$-" + TITLE_MAX_LENGTH + "s %4$-" + EXCERPT_MAX_LENGTH + "s%n"; CatalogFacade catalogProvider = getCatalog(); QueryImpl query = new QueryImpl(filter); query.setRequestsTotalResultsCount(true); if (numberOfItems > -1) { query.setPageSize(numberOfItems); } long start = System.currentTimeMillis(); SourceResponse response = catalogProvider.query(new QueryRequestImpl(query)); long end = System.currentTimeMillis(); int size = 0; if (response.getResults() != null) { size = response.getResults() .size(); } console.println(); console.printf(" %d result(s) out of %s%d%s in %3.3f seconds", (size), Ansi.ansi() .fg(Ansi.Color.CYAN) .toString(), response.getHits(), Ansi.ansi() .reset() .toString(), (end - start) / MS_PER_SECOND); console.printf(formatString, "", "", "", ""); printHeaderMessage(String.format(formatString, ID, DATE, TITLE, EXCERPT)); for (Result result : response.getResults()) { Metacard metacard = result.getMetacard(); String title = (metacard.getTitle() != null ? metacard.getTitle() : "N/A"); String excerpt = "N/A"; String modifiedDate = ""; if (searchPhrase != null) { if (metacard.getMetadata() != null) { XPathHelper helper = new XPathHelper(metacard.getMetadata()); String indexedText = helper.getDocument() .getDocumentElement() .getTextContent(); indexedText = indexedText.replaceAll("\\r\\n|\\r|\\n", " "); String normalizedSearchPhrase = searchPhrase.replaceAll("\\*", ""); int index = -1; if (caseSensitive) { index = indexedText.indexOf(normalizedSearchPhrase); } else { index = indexedText.toLowerCase() .indexOf(normalizedSearchPhrase.toLowerCase()); } if (index != -1) { int contextLength = (EXCERPT_MAX_LENGTH - normalizedSearchPhrase.length() - 8) / 2; excerpt = "..." + indexedText.substring(Math.max(index - contextLength, 0), index); excerpt = excerpt + Ansi.ansi() .fg(Ansi.Color.GREEN) .toString(); excerpt = excerpt + indexedText.substring(index, index + normalizedSearchPhrase.length()); excerpt = excerpt + Ansi.ansi() .reset() .toString(); excerpt = excerpt + indexedText.substring( index + normalizedSearchPhrase.length(), Math.min(indexedText.length(), index + normalizedSearchPhrase.length() + contextLength)) + "..."; } } } if (metacard.getModifiedDate() != null) { modifiedDate = new DateTime(metacard.getModifiedDate() .getTime()).toString(DATETIME_FORMATTER); } console.printf(formatString, metacard.getId(), modifiedDate, title.substring(0, Math.min(title.length(), TITLE_MAX_LENGTH)), excerpt); } return null; } private Object executeSearchCache(Filter filter) throws Exception { String formatString = "%1$-33s %2$-26s %3$-" + TITLE_MAX_LENGTH + "s %n"; long start = System.currentTimeMillis(); List<Metacard> results = getCacheProxy().query(filter); long end = System.currentTimeMillis(); console.println(); console.printf(" %d result(s) in %3.3f seconds", (results.size()), (end - start) / MS_PER_SECOND); console.printf(formatString, "", "", ""); printHeaderMessage(String.format(formatString, ID, DATE, TITLE)); for (Metacard metacard : results) { String title = (metacard.getTitle() != null ? metacard.getTitle() : "N/A"); String modifiedDate = ""; if (metacard.getModifiedDate() != null) { DateTime dt = new DateTime(DateTimeZone.UTC); modifiedDate = dt.toString(DATETIME_FORMATTER); } console.printf(formatString, metacard.getId(), modifiedDate, title.substring(0, Math.min(title.length(), TITLE_MAX_LENGTH))); } return null; } }