Search code examples
apache-kafkamemgraphdbmemgraph

Continuous Query Evaluation with Memgraph


I am looking for a way to evalute continuous queries with memgraph. My goal is to write an application, that notifies a user as soon as an event occurs (e.g. a specific pattern is found). The notification should be a message in a kafka topic. Something like the Neo4J-Kafka-Connector. Currently I found nothing better, than to write a python script that periodically queries the graph. My problem with that is, that I don't want to query the whole graph every time for performance reasons. I could define a timestamp in python and increment it with every query, but this seems very hacky. Is there a better way to solve this issue?


Solution

  • I just tried to connect to Memgraph using Neo4j Kafka Connector.

    I’ve created docker-compose.yml:

    ---
    version: "3"
    services:
      memgraph:
        image: "memgraph/memgraph"
        hostname: memgraph
        ports:
          - "7687:7687"
          - "7444:7444"
        volumes:
          - mg_lib:/var/lib/memgraph
          - mg_log:/var/log/memgraph
          - mg_etc:/etc/memgraph
    
      zookeeper:
        image: confluentinc/cp-zookeeper:7.3.0
        hostname: zookeeper
        container_name: zookeeper
        ports:
          - "2181:2181"
        environment:
          ZOOKEEPER_CLIENT_PORT: 2181
          ZOOKEEPER_TICK_TIME: 2000
    
      broker:
        image: confluentinc/cp-server:7.3.0
        hostname: broker
        container_name: broker
        depends_on:
          - zookeeper
        ports:
          - "9092:9092"
          - "9101:9101"
        environment:
          KAFKA_BROKER_ID: 1
          KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
          KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
          KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092
          KAFKA_METRIC_REPORTERS: io.confluent.metrics.reporter.ConfluentMetricsReporter
          KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
          KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
          KAFKA_CONFLUENT_LICENSE_TOPIC_REPLICATION_FACTOR: 1
          KAFKA_CONFLUENT_BALANCER_TOPIC_REPLICATION_FACTOR: 1
          KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
          KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
          KAFKA_JMX_PORT: 9101
          KAFKA_JMX_HOSTNAME: localhost
          KAFKA_CONFLUENT_SCHEMA_REGISTRY_URL: http://schema-registry:8081
          CONFLUENT_METRICS_REPORTER_BOOTSTRAP_SERVERS: broker:29092
          CONFLUENT_METRICS_REPORTER_TOPIC_REPLICAS: 1
          CONFLUENT_METRICS_ENABLE: 'true'
          CONFLUENT_SUPPORT_CUSTOMER_ID: 'anonymous'
    
      schema-registry:
        image: confluentinc/cp-schema-registry:7.3.0
        hostname: schema-registry
        container_name: schema-registry
        depends_on:
          - broker
        ports:
          - "8081:8081"
        environment:
          SCHEMA_REGISTRY_HOST_NAME: schema-registry
          SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: 'broker:29092'
          SCHEMA_REGISTRY_LISTENERS: http://0.0.0.0:8081
    
      connect:
        image: cnfldemos/cp-server-connect-datagen:0.6.0-7.3.0
        hostname: connect
        container_name: connect
        depends_on:
          - broker
          - schema-registry
        ports:
          - "8083:8083"
        volumes:
          - ./plugins:/tmp/connect-plugins
        environment:
          CONNECT_BOOTSTRAP_SERVERS: 'broker:29092'
          CONNECT_REST_ADVERTISED_HOST_NAME: connect
          CONNECT_GROUP_ID: compose-connect-group
          CONNECT_CONFIG_STORAGE_TOPIC: docker-connect-configs
          CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
          CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000
          CONNECT_OFFSET_STORAGE_TOPIC: docker-connect-offsets
          CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
          CONNECT_STATUS_STORAGE_TOPIC: docker-connect-status
          CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
          CONNECT_KEY_CONVERTER: org.apache.kafka.connect.storage.StringConverter
          CONNECT_VALUE_CONVERTER: io.confluent.connect.avro.AvroConverter
          CONNECT_VALUE_CONVERTER_SCHEMA_REGISTRY_URL: http://schema-registry:8081
          # CLASSPATH required due to CC-2422
          CLASSPATH: /usr/share/java/monitoring-interceptors/monitoring-interceptors-7.3.0.jar
          CONNECT_PRODUCER_INTERCEPTOR_CLASSES: "io.confluent.monitoring.clients.interceptor.MonitoringProducerInterceptor"
          CONNECT_CONSUMER_INTERCEPTOR_CLASSES: "io.confluent.monitoring.clients.interceptor.MonitoringConsumerInterceptor"
          CONNECT_PLUGIN_PATH: "/usr/share/java,/usr/share/confluent-hub-components,/tmp/connect-plugins"
          CONNECT_LOG4J_LOGGERS: org.apache.zookeeper=ERROR,org.I0Itec.zkclient=ERROR,org.reflections=ERROR
        command:
          - bash
          - -c
          - |
            confluent-hub install --no-prompt neo4j/kafka-connect-neo4j:latest
            /etc/confluent/docker/run
    
      control-center:
        image: confluentinc/cp-enterprise-control-center:7.3.0
        hostname: control-center
        container_name: control-center
        depends_on:
          - broker
          - schema-registry
          - connect
        ports:
          - "9021:9021"
        environment:
          CONTROL_CENTER_BOOTSTRAP_SERVERS: 'broker:29092'
          CONTROL_CENTER_CONNECT_CONNECT-DEFAULT_CLUSTER: 'connect:8083'
          CONTROL_CENTER_KSQL_KSQLDB1_URL: "http://ksqldb-server:8088"
          CONTROL_CENTER_KSQL_KSQLDB1_ADVERTISED_URL: "http://localhost:8088"
          CONTROL_CENTER_SCHEMA_REGISTRY_URL: "http://schema-registry:8081"
          CONTROL_CENTER_REPLICATION_FACTOR: 1
          CONTROL_CENTER_INTERNAL_TOPICS_PARTITIONS: 1
          CONTROL_CENTER_MONITORING_INTERCEPTOR_TOPIC_PARTITIONS: 1
          CONFLUENT_METRICS_TOPIC_REPLICATION: 1
          PORT: 9021
          
    volumes:
      mg_lib:
      mg_log:
      mg_etc:
    

    Created nodes with:

    CREATE (:TestSource {name: 'john', surname: 'doe', timestamp: localDateTime()});
    CREATE (:TestSource {name: 'mary', surname: 'doe', timestamp: localDateTime()});
    CREATE (:TestSource {name: 'jack', surname: 'small', timestamp: localDateTime()});
    

    Created source.neo4j.json:

    {
      "name": "Neo4jSourceConnectorAVRO",
      "config": {
        "topic": "my-topic",
        "connector.class": "streams.kafka.connect.source.Neo4jSourceConnector",
        "key.converter": "io.confluent.connect.avro.AvroConverter",
        "key.converter.schema.registry.url": "http://schema-registry:8081",
        "value.converter": "io.confluent.connect.avro.AvroConverter",
        "value.converter.schema.registry.url": "http://schema-registry:8081",
        "neo4j.server.uri": "bolt://memgraph:7687",
        "neo4j.authentication.basic.username": "",
        "neo4j.authentication.basic.password": "",
        "neo4j.streaming.poll.interval.msecs": 5000,
        "neo4j.streaming.property": "timestamp",
        "neo4j.streaming.from": "LAST_COMMITTED",
        "neo4j.enforce.schema": true,
        "neo4j.source.query": "MATCH (ts:TestSource) RETURN ts.name AS name, ts.surname AS surname, ts.timestamp AS timestamp"
      }
    }
    

    Created sink.neo4j.json:

    {
      "name": "Neo4jSinkConnectorAVRO",
      "config": {
        "topics": "my-topic",
        "connector.class": "streams.kafka.connect.sink.Neo4jSinkConnector",
        "key.converter": "io.confluent.connect.avro.AvroConverter",
        "key.converter.schema.registry.url": "http://schema-registry:8081",
        "value.converter": "io.confluent.connect.avro.AvroConverter",
        "value.converter.schema.registry.url": "http://schema-registry:8081",
        "errors.retry.timeout": "-1",
        "errors.retry.delay.max.ms": "1000",
        "errors.tolerance": "all",
        "errors.log.enable": true,
        "errors.log.include.messages": true,
        "neo4j.server.uri": "bolt://memgraph:7687",
        "neo4j.authentication.basic.username": "",
        "neo4j.authentication.basic.password": "",
        "neo4j.topic.cypher.my-topic": "MERGE (p:Person{name: event.name, surname: event.surname}) MERGE (f:Family{name: event.surname}) MERGE (p)-[:BELONGS_TO]->(f)"
      }
    }
    

    Tested with: MATCH (node) RETURN node;

    Added new node using CREATE (:TestSource {name: 'Ann', surname: 'Bolin', timestamp: localDateTime()});

    Does this help you?