/** * Copyright (c) 2002-2011 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.index.impl.lucene; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexProvider; import org.neo4j.graphdb.index.RelationshipIndex; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.Config; import org.neo4j.kernel.impl.index.IndexConnectionBroker; import org.neo4j.kernel.impl.index.ReadOnlyIndexConnectionBroker; import org.neo4j.kernel.impl.transaction.TxModule; public class LuceneIndexProvider extends IndexProvider { static final String KEY_PROVIDER = "provider"; static final String KEY_TYPE = "type"; static final String KEY_ANALYZER = "analyzer"; static final String KEY_TO_LOWER_CASE = "to_lower_case"; static final String KEY_SIMILARITY = "similarity"; public static final String SERVICE_NAME = "lucene"; public static final Map<String, String> EXACT_CONFIG = Collections.unmodifiableMap( MapUtil.stringMap( KEY_PROVIDER, SERVICE_NAME, KEY_TYPE, "exact" ) ); public static final Map<String, String> FULLTEXT_CONFIG = Collections.unmodifiableMap( MapUtil.stringMap( KEY_PROVIDER, SERVICE_NAME, KEY_TYPE, "fulltext", KEY_TO_LOWER_CASE, "true" ) ); public static final int DEFAULT_LAZY_THRESHOLD = 100; private static final String DATA_SOURCE_NAME = "lucene-index"; private IndexConnectionBroker<LuceneXaConnection> broker; private LuceneDataSource dataSource; private GraphDatabaseService graphDb; final int lazynessThreshold = DEFAULT_LAZY_THRESHOLD; public LuceneIndexProvider() { super( SERVICE_NAME ); } IndexConnectionBroker<LuceneXaConnection> broker() { return this.broker; } LuceneDataSource dataSource() { return this.dataSource; } GraphDatabaseService graphDb() { return this.graphDb; } @Override protected void load( KernelData kernel ) { } @Override protected void init( KernelData kernel ) { try { load( kernel.graphDatabase(), kernel.getConfig() ); } catch ( RuntimeException e ) { e.printStackTrace(); throw e; } } private void load( GraphDatabaseService db, Config config ) { this.graphDb = db; TxModule txModule = config.getTxModule(); boolean isReadOnly = config.isReadOnly(); Map<Object, Object> params = new HashMap<Object, Object>( config.getParams() ); params.put( "read_only", isReadOnly ); dataSource = (LuceneDataSource) txModule.registerDataSource( DATA_SOURCE_NAME, LuceneDataSource.class.getName(), LuceneDataSource.DEFAULT_BRANCH_ID, params, true ); broker = isReadOnly ? new ReadOnlyIndexConnectionBroker<LuceneXaConnection>( txModule.getTxManager() ) : new ConnectionBroker( txModule.getTxManager(), dataSource ); } public Index<Node> nodeIndex( String indexName, Map<String, String> config ) { IndexIdentifier identifier = new IndexIdentifier( LuceneCommand.NODE, dataSource.nodeEntityType, indexName ); synchronized ( dataSource.indexes ) { LuceneIndex index = dataSource.indexes.get( identifier ); if ( index == null ) { index = new LuceneIndex.NodeIndex( this, identifier ); dataSource.indexes.put( identifier, index ); } return index; } } public RelationshipIndex relationshipIndex( String indexName, Map<String, String> config ) { IndexIdentifier identifier = new IndexIdentifier( LuceneCommand.RELATIONSHIP, dataSource.relationshipEntityType, indexName ); synchronized ( dataSource.indexes ) { LuceneIndex index = dataSource.indexes.get( identifier ); if ( index == null ) { index = new LuceneIndex.RelationshipIndex( this, identifier ); dataSource.indexes.put( identifier, index ); } return (RelationshipIndex) index; } } public Map<String, String> fillInDefaults( Map<String, String> source ) { Map<String, String> result = source != null ? new HashMap<String, String>( source ) : new HashMap<String, String>(); String type = result.get( KEY_TYPE ); if ( type == null ) { type = "exact"; result.put( KEY_TYPE, type ); } if ( type.equals( "fulltext" ) ) { if ( !result.containsKey( LuceneIndexProvider.KEY_TO_LOWER_CASE ) ) { result.put( KEY_TO_LOWER_CASE, "true" ); } } return result; } @Override public boolean configMatches( Map<String, String> storedConfig, Map<String, String> config ) { return match( storedConfig, config, KEY_TYPE, null ) && match( storedConfig, config, KEY_TO_LOWER_CASE, "true" ) && match( storedConfig, config, KEY_ANALYZER, null ) && match( storedConfig, config, KEY_SIMILARITY, null ); } private boolean match( Map<String, String> storedConfig, Map<String, String> config, String key, String defaultValue ) { String value1 = storedConfig.get( key ); String value2 = config.get( key ); if ( value1 == null || value2 == null ) { if ( value1 == value2 ) { return true; } if ( defaultValue != null ) { value1 = value1 != null ? value1 : defaultValue; value2 = value2 != null ? value2 : defaultValue; return value1.equals( value2 ); } } else { return value1.equals( value2 ); } return false; } @Override public String getDataSourceName() { return DATA_SOURCE_NAME; } }