Motivation

GraphDatabasesBook: Robinson I., Webber J., Eifrem E. Graph Databases. 2013.
这本该是入门概念性质的书,但是没有讲清楚最重要的索引。我相信绝大部分人不会去看600页+的Neo4j手册的。

Neo4jInActionBook: Vukotic A., Watt N. et al. Neo4j in Action. 2015.
这本就不同了,虽然“旧”了点(其实是Neo4j变化太快,有些API不支持了),将索引讲的很清楚。

我很懒,真的很懒。就以可以工作的代码(测试开发用)来说明吧。

Talk is cheap, show me the code.

代码托管见SpringDataBasedNoSQLTutorials.

0 测试环境配置

Maven

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-neo4j</artifactId>
        <version>3.3.2.RELEASE</version>
    </dependency>

    <!-- with production Neo4j Server -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-neo4j-rest</artifactId>
        <version>3.3.2.RELEASE</version>
    </dependency>

    <!-- validation -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.1.3.Final</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>

    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>2.2.4</version>
    </dependency>

    <!-- log -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Configuration

package com.spike.springdata.neo4j;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;

import com.spike.springdata.neo4j.anno.Neo4jInActionBook;

@Configuration
@EnableNeo4jRepositories
public class Neo4jAppDevConfig extends Neo4jConfiguration {

    /**
     * local file directory of embedded database of Neo4j
     */
    public static final String Embedded_DB_DIR = "springdataneo4j.db";

    /**
     * Must use {@link #setBasePackage(String...)}!!!
     */
    public Neo4jAppDevConfig() {
        setBasePackage("com.spike.springdata.neo4j");
    }

    @Neo4jInActionBook(chapter = { "5" })
    @Bean
    public GraphDatabaseService graphDatabaseService() {
        // GraphDatabaseService result = new
        // GraphDatabaseFactory().newEmbeddedDatabase(Embedded_DB_DIR);

        // see manual-v2.2.3 37.12. Automatic Indexing
        GraphDatabaseService result = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(Embedded_DB_DIR)
                .setConfig(GraphDatabaseSettings.node_auto_indexing, "true")
                .setConfig(GraphDatabaseSettings.relationship_auto_indexing, "true")
                .setConfig(GraphDatabaseSettings.node_keys_indexable, "name, dateOfBirth")
                .setConfig(GraphDatabaseSettings.relationship_keys_indexable, "type, name").newGraphDatabase();

        return result;

    }
}

1 手工创建索引

package com.spike.springdata.neo4j.nativeAPI;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;

import com.spike.springdata.neo4j.Neo4jAppDevConfig;
import com.spike.springdata.neo4j.Neo4jAppUtils;
import com.spike.springdata.neo4j.anno.Neo4jInActionBook;

/**
 * DemonStration of Neo4j manual indexing using APIs
 *
 * @author zhoujiagen<br/>
 *         Aug 15, 2015 11:15:32 PM
 */
@Neo4jInActionBook(chapter = { "5" })
public class Neo4jManualIndexingDemonstration {
    private static final Logger logger = Logger.getLogger(Neo4jManualIndexingDemonstration.class);

    /**
     * it's not a good practice!
     */
    private static final String NEWLINE = System.getProperty("line.separator");

    private static final String names[] = { "Joanne Smith", "Kate Smith", "John Johnson" };
    private static final String emails[] = { "jsmith@example.org", "ksmith@exmaple.org", "jjohnson@exmaple.org" };
    private static final Integer ages[] = { 34, 35, 35 };

    private static Long[] person_ids = new Long[3];

    private static final String NODE_INDEX_NAME = "users";

    public static void main(String[] args) {
        Neo4jAppUtils.clean(Neo4jAppDevConfig.Embedded_DB_DIR);

        // create graph database service
        GraphDatabaseService gds = new GraphDatabaseFactory().newEmbeddedDatabase(Neo4jAppDevConfig.Embedded_DB_DIR);

        populateGraphData(gds);

        List<Node> persons = getNodesByIds(gds, person_ids);
        createNodeIndex(gds, NODE_INDEX_NAME, persons.get(0), PropEnum.EMAIL.name(), emails[0]);
        searchWithNodeIndex(gds, NODE_INDEX_NAME, PropEnum.EMAIL.name(), emails[0]);

        createNodeIndex(gds, NODE_INDEX_NAME, persons.get(0), PropEnum.AGE.name(), ages[0]);
        createNodeIndex(gds, NODE_INDEX_NAME, persons.get(1), PropEnum.AGE.name(), ages[1]);
        createNodeIndex(gds, NODE_INDEX_NAME, persons.get(2), PropEnum.AGE.name(), ages[2]);
        searchWithIndexReturnMultipleResults(gds, PropEnum.AGE.name(), ages[1]);

        // dealing with changed indexed entry
        String newEmail = "jsmith_new@example.org";
        changeIndexValue(gds, NodeIndexEnum.USERS.name(), PropEnum.EMAIL.name(), emails[0], newEmail);
        searchWithNodeIndex(gds, NODE_INDEX_NAME, PropEnum.EMAIL.name(), emails[0]);
        searchWithNodeIndex(gds, NODE_INDEX_NAME, PropEnum.EMAIL.name(), newEmail);
    }

    static void populateGraphData(GraphDatabaseService gds) {
        try (Transaction tx = gds.beginTx();) {
            Node person1 = gds.createNode();
            person_ids[0] = person1.getId();
            person1.setProperty(PropEnum.NAME.name(), names[0]);
            person1.setProperty(PropEnum.EMAIL.name(), emails[0]);
            person1.setProperty(PropEnum.AGE.name(), ages[0]);

            Node person2 = gds.createNode();
            person_ids[1] = person2.getId();
            person2.setProperty(PropEnum.NAME.name(), names[1]);
            person2.setProperty(PropEnum.EMAIL.name(), emails[1]);
            person2.setProperty(PropEnum.AGE.name(), ages[1]);

            Node person3 = gds.createNode();
            person_ids[2] = person3.getId();
            person3.setProperty(PropEnum.NAME.name(), names[2]);
            person3.setProperty(PropEnum.EMAIL.name(), emails[2]);
            person3.setProperty(PropEnum.AGE.name(), ages[2]);

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when populate graph data, refer", e);
        }
    }

    static Node getNodeById(GraphDatabaseService gds, Long nodeId) {
        Node result = null;

        try (Transaction tx = gds.beginTx();) {
            result = gds.getNodeById(nodeId);

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when get node by its id, refer", e);
        }

        return result;
    }

    static List<Node> getNodesByIds(GraphDatabaseService gds, Long... nodeIds) {
        List<Node> result = new ArrayList<Node>();

        try (Transaction tx = gds.beginTx();) {
            for (Long nodeId : nodeIds) {
                result.add(gds.getNodeById(nodeId));
            }

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when get nodes by their ids, refer", e);
        }

        return result;
    }

    static void createNodeIndex(GraphDatabaseService gds, String indexName, Node node, String indexKey,
            Object indexValue) {

        try (Transaction tx = gds.beginTx();) {
            // obtain a reference to IndexManager
            IndexManager indexManager = gds.index();
            // find or create an named index
            Index<Node> index = indexManager.forNodes(indexName);
            // 3 parameters: the indexed node, the index key, the indexed value
            index.add(node, indexKey, indexValue);

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when create index, refer", e);
        }

    }

    static void searchWithNodeIndex(GraphDatabaseService gds, String indexName, String indexKey, String indexValue) {
        logger.info(NEWLINE + "searchWithIndex(" + indexKey + "," + indexValue + ")");

        try (Transaction tx = gds.beginTx();) {
            Index<Node> index = gds.index().forNodes(indexName);

            // search with index
            IndexHits<Node> indexHits = index.get(indexKey, indexValue);
            Node resultNode = indexHits.getSingle();// single match

            if (resultNode != null) {
                logger.info(NEWLINE + renderNode(resultNode));
            }

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when search with index, refer", e);
        }
    }

    static void searchWithIndexReturnMultipleResults(GraphDatabaseService gds, String indexKey, Integer indexValue) {

        try (Transaction tx = gds.beginTx();) {
            Index<Node> index = gds.index().forNodes(indexKey);

            // search with index
            IndexHits<Node> indexHits = index.get(indexKey, indexValue);
            for (Node node : indexHits) {
                logger.info(NEWLINE + renderNode(node));
            }

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happeded when search with index, and multiple results expected, refer", e);
        }

    }

    static void changeIndexValue(GraphDatabaseService gds, String indexName, String indexKey, String sourceIndexValue,
            String targetIndexValue) {
        try (Transaction tx = gds.beginTx();) {
            Index<Node> index = gds.index().forNodes(indexName);
            IndexHits<Node> hits = index.get(indexKey, sourceIndexValue);
            Node targetNode = hits.getSingle();
            if (targetNode != null) {
                // remove source indexed entry
                index.remove(targetNode, indexKey, sourceIndexValue);
                // create the new indexed entry
                index.add(targetNode, indexKey, targetIndexValue);
            }

        } catch (Exception e) {
            logger.error("Strange things happeded when changing index values, refer", e);
        }

    }

    /**
     * How lovely is Scala's `sealed`
     */
    static final PropEnum[] ALL_PropEnum = { PropEnum.NAME, PropEnum.EMAIL, PropEnum.AGE };

    static String renderNode(Node node) {
        StringBuilder sb = new StringBuilder();
        for (PropEnum propEnum : ALL_PropEnum) {
            sb.append(propEnum.name() + ": " + node.getProperty(propEnum.name()) + ",");
        }
        String result = sb.toString();
        return result.substring(0, result.length() - 1);
    }

    private static enum PropEnum {
        NAME, EMAIL, AGE
    }

    /**
     * the indexes used for nodes
     */
    private static enum NodeIndexEnum {
        USERS
    }

}

2 自动创建索引

    @Bean
    public GraphDatabaseService graphDatabaseService() {
        // GraphDatabaseService result = new
        // GraphDatabaseFactory().newEmbeddedDatabase(Embedded_DB_DIR);

        // see manual-v2.2.3 37.12. Automatic Indexing
        GraphDatabaseService result = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(Embedded_DB_DIR)
                .setConfig(GraphDatabaseSettings.node_auto_indexing, "true")
                .setConfig(GraphDatabaseSettings.relationship_auto_indexing, "true")
                .setConfig(GraphDatabaseSettings.node_keys_indexable, "name, dateOfBirth")
                .setConfig(GraphDatabaseSettings.relationship_keys_indexable, "type, name").newGraphDatabase();

        return result;

    }

3 schema创建索引

Graph database不是声称无schema的吗?那是骗鬼的!

package com.spike.springdata.neo4j.nativeAPI;

import org.apache.log4j.Logger;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.helpers.collection.IteratorUtil;

import com.spike.springdata.neo4j.Neo4jAppDevConfig;
import com.spike.springdata.neo4j.Neo4jAppUtils;
import com.spike.springdata.neo4j.anno.Neo4jInActionBook;

/**
 * Demonstration of Neo4j Schema Indexing<br/>
 * a feature was introduced in 2.0+
 *
 * @author zhoujiagen<br/>
 *         Aug 17, 2015 11:07:27 PM
 */
@Neo4jInActionBook(chapter = { "5" })
public class Neo4jSchemaIndexingDemonstration {
    private static final Logger logger = Logger.getLogger(Neo4jSchemaIndexingDemonstration.class);

    /**
     * it's not a good practice!
     */
    private static final String NEWLINE = System.getProperty("line.separator");

    static final Label movieLabel = DynamicLabel.label(LabelEnum.MOVIE.name());
    static final Label userLabel = DynamicLabel.label(LabelEnum.USER.name());
    static Node movie, user;
    static final String name = "Michael Collins";

    public static void main(String[] args) {
        Neo4jAppUtils.clean(Neo4jAppDevConfig.Embedded_DB_DIR);

        Label movieLabel = DynamicLabel.label(LabelEnum.MOVIE.name());
        Label userLabel = DynamicLabel.label(LabelEnum.USER.name());

        // create graph database service
        GraphDatabaseService gds = new GraphDatabaseFactory().newEmbeddedDatabase(Neo4jAppDevConfig.Embedded_DB_DIR);

        createSchemaWithIndex(gds);

        populateGraphDataWithLabel(gds);

        searchWithIndex(gds, movieLabel, PropEnum.NAME.name(), name);
        searchWithIndex(gds, userLabel, PropEnum.NAME.name(), name);

    }

    static void searchWithIndex(GraphDatabaseService gds, Label label, String indexKey, String indexValue) {
        try (Transaction tx = gds.beginTx();) {

            ResourceIterable<Node> result = gds.findNodesByLabelAndProperty(label, indexKey, indexValue);
            logger.info(NEWLINE + "result's size=" + IteratorUtil.count(result));

            logger.info(NEWLINE + "movie[" + movie.getId() + "], user[" + user.getId() + "], and result's id="
                    + result.iterator().next().getId());

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happend when search with indexes, refer", e);
        }
    }

    static void populateGraphDataWithLabel(GraphDatabaseService gds) {
        try (Transaction tx = gds.beginTx();) {

            /**
             * note {@link GraphDatabaseService#createNode(Label... labels)}<br/>
             * so we can create a node with multiple labels, this example is
             * omitted
             */
            movie = gds.createNode(movieLabel);
            movie.setProperty(PropEnum.NAME.name(), name);

            user = gds.createNode(userLabel);
            user.setProperty(PropEnum.NAME.name(), name);

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happend when populateing data with labels, refer", e);
        }
    }

    static void createSchemaWithIndex(GraphDatabaseService gds) {

        // create schema
        try (Transaction tx = gds.beginTx();) {
            // define indexes
            gds.schema().indexFor(movieLabel).on(PropEnum.NAME.name()).create();
            gds.schema().indexFor(userLabel).on(PropEnum.NAME.name()).create();

            tx.success();
        } catch (Exception e) {
            logger.error("Strange things happend when creating schema, refer", e);
        }
    }

    private static enum LabelEnum {
        MOVIE, USER
    }

    private static enum PropEnum {
        NAME
    }
}

Neo4j Index Notes的更多相关文章

  1. Neo4j:Index索引

    Indexing in Neo4j: An Overview by Stefan Armbruster · Jan. 06, 14 · Java Zone Neo4j是一个图数据库,在做图的检索时,用 ...

  2. Solution for automatic update of Chinese word segmentation full-text index in NEO4J

    Solution for automatic update of Chinese word segmentation full-text index in NEO4J 1. Sample data 2 ...

  3. 基于neo4j图数据库,实现人口关系大图的基本思路及实现方案。

    近期由于工作需要,需要做一个人口关系大图的存储及检索方案,我们主要的数据对象有:人口(年龄,身份证号码,性别..) :学校信息(学校地址,学校名称,学校级别,学校下边的年级班级..):就职信息(公司名 ...

  4. Neo4j中實現自定義中文全文索引

    資料庫檢索效率時,一般首要優化途徑是從索引入手,然後根據需求再考慮更復雜的負載均衡.讀寫分離和分散式水平/垂直分庫/表等手段:索引通過資訊冗餘來提高檢索效率,其以空間換時間並會降低資料寫入的效率,因此 ...

  5. NEO4J中文分词全文索引自动更新解决方案

    NEO4J中文分词全文索引自动更新解决方案 一.样例数据 二.英文与中文全文索引差别 1.创建NEO4J默认索引 2.删除索引 3.创建支持中文分词的索引 三.APOC自带英文全文索引过程(可自动更新 ...

  6. NEO4J亿级数据全文索引构建优化

    NEO4J亿级数据全文索引构建优化 一.数据量规模(亿级) 二.构建索引的方式 三.构建索引发生的异常 四.全文索引代码优化 1.Java.lang.OutOfMemoryError 2.访问数据库时 ...

  7. Neo4j中实现自定义中文全文索引

    数据库检索效率时,一般首要优化途径是从索引入手,然后根据需求再考虑更复杂的负载均衡.读写分离和分布式水平/垂直分库/表等手段:索引通过信息冗余来提高检索效率,其以空间换时间并会降低数据写入的效率:因此 ...

  8. Java基础学习总结--对象容器

    目录: ArrayList 顺序泛型容器 HashSet 集合容器 HashMap<Key,Value>容器 要用Java实现记事本的功能.首先列出记事本所需功能: 可以添加记录(字符串) ...

  9. MVC中Controller控制器相关技术

    第6章Controller相关技术 Controller(控制器)在ASP.NET MVC中负责控制所有客户端与服务器端的交互,并 且负责协调Model与View之间的数椐传递,是ASP.NET MV ...

随机推荐

  1. 【Unity3D游戏开发】之常用代码 (十二)

    //创建一个名为"Player"的游戏物体 //并给他添加刚体和立方体碰撞器. player=new GameObject("Player"); player. ...

  2. 安装DotNetCore.1.0.0-VS2015Tools.Preview2失败解决方案

    1.把安装文件放入非系统盘 2.命令行带参数运行: DotNetCore.1.0.0-VS2015Tools.Preview2.0.1.exe SKIP_VSU_CHECK=1 或 DotNetCor ...

  3. Java开发中经典的小实例-(字符串倒序输出)

    public class Test12 {    public static void main(String[] args) {        // TODO Auto-generated meth ...

  4. PHP : Reflection API

    PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类.方法.属性.参数等的详细信息,包括注释. PHP Reflection API有: class Reflecti ...

  5. 16s及宏基因组测序公司资源--20161104

    锐羿基因: http://www.realbio.cn/index.php?c=list&cs=keyandongtai& 科研动态更新及时 青岛过程能源所: http://www.c ...

  6. K-邻近算法

    K-邻近算法 采用测量不同特征值之间的距离来进行分类 Ad:精度高,对异常值不敏感,无数据输入假定 Na:计算复杂度高,空间复杂度高 KNN原理 存在样本集,每个数据都存在标签,输入无标签的新数据后, ...

  7. 2014-07-29 Asp.Net 中级工程师 笔试题

    一.选择题    1.下列描述错误的是() A  类不可以被多重继承而接口可以: B  抽象类自身可以定义成员而接口不可以: C  抽象类和接口都不能被实例化: D   一个类可以继承多个基类和多个基 ...

  8. 电源相关知识—S0、S1(POS)、S2、S3(STR)、 S4、S5、睡眠、休眠、待机

    转 http://blog.sina.com.cn/s/blog_52f28dde0100l3ci.html APM https://en.wikipedia.org/wiki/Advanced_Po ...

  9. iOS开发UI篇—多控制器和导航控制器简单介绍

    iOS开发UI篇—多控制器和导航控制器简单介绍 一.多控制器 一个iOS的app很少只由一个控制器组成,除非这个app极其简单.当app中有多个控制器的时候,我们就需要对这些控制器进行管理 有多个vi ...

  10. [html5]placeholder默认颜色

    :-moz-placeholder { /* Mozilla Firefox 4 to 18 */ color: #f00; } ::-moz-placeholder { /* Mozilla Fir ...