/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.usergrid.utils; import java.io.IOException; import java.io.StringReader; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.util.Version; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import static org.apache.usergrid.utils.ClassUtils.cast; import static org.apache.usergrid.utils.ClassUtils.isBasicType; import static org.apache.usergrid.utils.JsonUtils.quoteString; import static org.apache.usergrid.utils.JsonUtils.toJsonNode; public class IndexUtils { private static final Logger logger = LoggerFactory.getLogger( IndexUtils.class ); static Analyzer analyzer = new StandardAnalyzer( Version.LUCENE_30 ); private static void buildKeyValueSet( Object node, Map<String, List<Object>> keyValues, String path, boolean fulltextIndex, Object... history ) { if ( node == null ) { return; } if ( node instanceof Collection ) { Object[] newHistory = Arrays.copyOf( history, history.length + 1 ); newHistory[history.length] = node; @SuppressWarnings("unchecked") Collection<Object> c = ( Collection<Object> ) node; for ( Object o : c ) { buildKeyValueSet( o, keyValues, path, fulltextIndex, newHistory ); } } else if ( node instanceof Map ) { Object[] newHistory = Arrays.copyOf( history, history.length + 1 ); newHistory[history.length] = node; @SuppressWarnings("unchecked") Map<Object, Object> m = ( Map<Object, Object> ) node; for ( Entry<Object, Object> e : m.entrySet() ) { String newPath; String key = e.getKey().toString(); key = quoteString( key ); if ( key.indexOf( '\\' ) == -1 ) { if ( path != null ) { newPath = path + "." + key; } else { newPath = "" + key; } } else { if ( path != null ) { newPath = path + "[\"" + key + "\"]"; } else { newPath = "" + "[\"" + key + "\"]"; } } buildKeyValueSet( e.getValue(), keyValues, newPath, fulltextIndex, newHistory ); } } else if ( node instanceof ArrayNode ) { Object[] newHistory = Arrays.copyOf( history, history.length + 1 ); newHistory[history.length] = node; ArrayNode c = ( ArrayNode ) node; for ( JsonNode o : c ) { buildKeyValueSet( o, keyValues, path, fulltextIndex, newHistory ); } } else if ( node instanceof ObjectNode ) { Object[] newHistory = Arrays.copyOf( history, history.length + 1 ); newHistory[history.length] = node; ObjectNode c = ( ObjectNode ) node; Iterator<Entry<String, JsonNode>> i = c.fields(); while ( i.hasNext() ) { Entry<String, JsonNode> e = i.next(); String newPath; String key = e.getKey(); key = quoteString( key ); if ( key.indexOf( '\\' ) == -1 ) { if ( path != null ) { newPath = path + "." + key; } else { newPath = "" + key; } } else { if ( path != null ) { newPath = path + "[\"" + key + "\"]"; } else { newPath = "" + "[\"" + key + "\"]"; } } buildKeyValueSet( e.getValue(), keyValues, newPath, fulltextIndex, newHistory ); } } else if ( !isBasicType( node.getClass() ) && ( !( node instanceof JsonNode ) ) ) { buildKeyValueSet( toJsonNode( node ), keyValues, path, fulltextIndex, history ); } else { if ( node instanceof JsonNode ) { if ( ( ( JsonNode ) node ).isTextual() ) { node = ( ( JsonNode ) node ).asText(); UUID uuid = UUIDUtils.tryGetUUID( ( String ) node ); if ( uuid != null ) { node = uuid; } } else if ( ( ( JsonNode ) node ).isNumber() ) { node = ( ( JsonNode ) node ).asInt(); } else { return; } } if ( path == null ) { path = ""; } List<Object> l = keyValues.get( path ); if ( l == null ) { l = new ArrayList<Object>(); keyValues.put( path, l ); } l.add( node ); if ( ( node instanceof String ) && fulltextIndex ) { String keywordsPath = ( path.length() > 0 ) ? path + ".keywords" : "keywords"; List<Object> keywords = cast( keywords( ( String ) node ) ); if ( keywords.size() > 0 ) { keyValues.put( keywordsPath, keywords ); } } } } public static Map<String, List<Object>> getKeyValues( String path, Object obj, boolean fulltextIndex ) { Map<String, List<Object>> keys = new LinkedHashMap<String, List<Object>>(); buildKeyValueSet( obj, keys, path, fulltextIndex ); return keys; } public static List<Map.Entry<String, Object>> getKeyValueList( String path, Object obj, boolean fulltextIndex ) { Map<String, List<Object>> map = getKeyValues( path, obj, fulltextIndex ); List<Map.Entry<String, Object>> list = new ArrayList<Map.Entry<String, Object>>(); for ( Entry<String, List<Object>> entry : map.entrySet() ) { for ( Object value : entry.getValue() ) { list.add( new AbstractMap.SimpleEntry<String, Object>( entry.getKey(), value ) ); } } return list; } public static List<Entry<String, Object>> getKeyValueList( Object obj, boolean fulltextIndex ) { return getKeyValueList( null, obj, fulltextIndex ); } public static List<String> keywords( String source ) { List<String> keywords = new ArrayList<String>(); TokenStream ts = null; try { ts = analyzer.tokenStream( "keywords", new StringReader( source ) ); ts.reset(); while ( ts.incrementToken() ) { keywords.add( ts.getAttribute( CharTermAttribute.class ).toString() ); } ts.end(); } catch ( IOException e ) { logger.error( "Error getting keywords ", e ); } finally { try { ts.close(); } catch (IOException ignored) {} } return keywords; } }