/* * Licensed to "Neo Technology," Network Engine for Objects in Lund AB * (http://neotechnology.com) under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional information * regarding copyright ownership. Neo Technology 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.neo4j.neoclipse.search; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.search.ui.ISearchQuery; import org.eclipse.search.ui.ISearchResult; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.neoclipse.Activator; import org.neo4j.neoclipse.graphdb.GraphCallable; import org.neo4j.neoclipse.graphdb.GraphDbServiceManager; import org.neo4j.neoclipse.view.ErrorMessage; import org.neo4j.neoclipse.view.NeoGraphViewPart; import org.neo4j.neoclipse.view.UiHelper; /** * This class represents a search query for Neo objects. * * @author Peter Hänsgen * @author Anders Nawroth */ public class NeoSearchQuery implements ISearchQuery { /** * The search expression. */ private final NeoSearchExpression expression; /** * The found matches. */ private final NeoSearchResult result; /** * The constructor. * * @param graphView the current graph view */ public NeoSearchQuery( final NeoSearchExpression expression, final NeoGraphViewPart graphView ) { this.expression = expression; // initialize an empty result result = new NeoSearchResult( this ); } /** * Returns a String form of the search expression. */ public String getExpression() { return expression.getExpression(); } /** * Returns true. */ public boolean canRerun() { return true; } /** * Returns true. */ public boolean canRunInBackground() { return true; } /** * Returns a label. */ public String getLabel() { return "Neo4j Search"; } /** * Returns the search result. */ public ISearchResult getSearchResult() { return result; } /** * Executes the search. */ public IStatus run( final IProgressMonitor monitor ) throws OperationCanceledException { final GraphDbServiceManager gsm = Activator.getDefault().getGraphDbServiceManager(); if ( !gsm.isRunning() ) { return new Status( IStatus.ERROR, Activator.PLUGIN_ID, "There is no active Neo4j service." ); } try { gsm.submitTask( new GraphCallable<Boolean>() { public Boolean call( final GraphDatabaseService graphDb ) { final Iterable<Node> matches = getMatchingNodes( monitor, graphDb ); UiHelper.asyncExec( new Runnable() { public void run() { result.setMatches( matches ); } } ); return true; } }, "run search" ).get(); if ( monitor.isCanceled() ) { return new Status( IStatus.CANCEL, Activator.PLUGIN_ID, "Cancelled." ); } else { return new Status( IStatus.OK, Activator.PLUGIN_ID, "OK" ); } } catch ( Exception e ) { ErrorMessage.showDialog( "Search error", e ); } return null; } /** * Finds all nodes matching the search criteria. * * @param graphDb TODO */ protected Iterable<Node> getMatchingNodes( final IProgressMonitor monitor, final GraphDatabaseService graphDb ) { // monitor.beginTask( "Neo4j search operation started.", // IProgressMonitor.UNKNOWN ); List<Node> matches = new LinkedList<Node>(); Node nodeFromId = null; if ( expression.isPossibleId() ) { try { long id = Long.parseLong( expression.getExpression() ); nodeFromId = graphDb.getNodeById( id ); matches.add( nodeFromId ); } catch ( RuntimeException e ) // NumberFormatException included { // do nothing } } for ( Node node : graphDb.getAllNodes() ) { if ( expression.matches( node.getId() ) ) { matches.add( node ); } else { // find at least one property whose value matches the // given expression for ( String key : node.getPropertyKeys() ) { Object value = node.getProperty( key ); if ( expression.matches( value ) ) { matches.add( node ); break; } } } } return matches; } /** * Finds all nodes matching the search criteria. */ // protected Iterable<Node> getMatchingNodesByRecursion( final Node node, // final IProgressMonitor monitor ) // { // // TODO the Neo traverser API is not sufficient as it does not allow to // // find ALL connected // // nodes regardless of their relationship types // // we have to implement a similar functionality ourselves... // Set<Node> visitedNodes = new HashSet<Node>(); // List<Node> matches = new ArrayList<Node>(); // // try using as id, if possible // if ( expression.isPossibleId() ) // { // try // { // long id = Long.parseLong( expression.getExpression() ); // Node nodeFromId = neoService.getNodeById( id ); // matches.add( nodeFromId ); // visitedNodes.add( nodeFromId ); // } // catch ( RuntimeException e ) // this also covers // // NumberFormatException // { // // do nothing // } // } // checkNode( node, visitedNodes, matches, monitor ); // return matches; // } /** * Checks if a node matches the search criteria and visits all connected * nodes. */ protected void checkNode( final Node node, final Set<Node> visitedNodes, final List<Node> matches, final IProgressMonitor monitor ) { if ( monitor.isCanceled() ) { return; } if ( !visitedNodes.add( node ) ) { // we have already been here return; } // for completeness, also check the id of the node if ( expression.matches( node.getId() ) ) { matches.add( node ); } else { // find at least one property whose value matches the given // expression for ( String key : node.getPropertyKeys() ) { Object value = node.getProperty( key ); if ( expression.matches( value ) ) { matches.add( node ); break; } } } // recursively follow all connections for ( Relationship r : node.getRelationships( Direction.BOTH ) ) { Node end = r.getOtherNode( node ); checkNode( end, visitedNodes, matches, monitor ); } } }