diff --git a/next.config.mjs b/next.config.mjs
index 58ddca1d2..d88c84826 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -299,6 +299,11 @@ export default withNextra({
destination: '/advanced-algorithms/available-algorithms/migrate',
permanent: true
},
+ {
+ source: '/memgraph/query-modules/mgps.py',
+ destination: '/advanced-algorithms/available-algorithms/mgps',
+ permanent: true
+ },
{
source: '/mage/algorithms/traditional-graph-analytics/node-similarity-algorithm',
destination: '/advanced-algorithms/available-algorithms/node_similarity',
diff --git a/pages/advanced-algorithms/available-algorithms.mdx b/pages/advanced-algorithms/available-algorithms.mdx
index 80912f8c9..1518e04e2 100644
--- a/pages/advanced-algorithms/available-algorithms.mdx
+++ b/pages/advanced-algorithms/available-algorithms.mdx
@@ -86,7 +86,7 @@ If you want to know more and learn how this affects you, read our [announcement]
| [collections](/advanced-algorithms/available-algorithms/collections) | C++ | The collections module is a collection manipulation module that offers functions to work with lists in Cypher queries, allowing operations like filtering, sorting, and modification for efficient data handling. |
| [create](/advanced-algorithms/available-algorithms/create) | C++ | The create module is a set of powerful tools that provide additional capabilities for creating nodes and relationships in the Memgraph graph database. |
| [csv_utils](/advanced-algorithms/available-algorithms/csv_utils) | C++ | An utility module for creating and deleting CSV files. |
-| [date](/advanced-algorithms/available-algorithms/date) | C++ | The `date` module provides various utilities to handle date and time operations within the Cypher query language. |
+| [date](/advanced-algorithms/available-algorithms/date) | Python | The `date` module provides various utilities to handle date and time operations within the Cypher query language. |
| [do](/advanced-algorithms/available-algorithms/do) | C++ | A module that is used to define conditions not expressible in Cypher to control query execution. |
| [export_util](/advanced-algorithms/available-algorithms/export_util) | Python | A module for exporting the graph database in different formats (JSON). |
| [graph_analyzer](/advanced-algorithms/available-algorithms/graph_analyzer) | Python | This Graph Analyzer query module offers insights about the stored graph or a subgraph. |
@@ -100,6 +100,7 @@ If you want to know more and learn how this affects you, read our [announcement]
| [meta](/advanced-algorithms/available-algorithms/meta) | C++ | Provides information about graph nodes and relationships. |
| [meta_util](/advanced-algorithms/available-algorithms/meta_util) | Python | A module that contains procedures describing graphs on a meta-level. |
| [migrate](/advanced-algorithms/available-algorithms/migrate) | Python | A module that can access data from a MySQL, SQL Server or Oracle database. |
+| [mgps](/advanced-algorithms/available-algorithms/mgps) | Python | A module containing various utility functions. |
| [neighbors](/advanced-algorithms/available-algorithms/neighbors) | C++ | The neighbors module allows users to interact with and query nodes that have direct relationships to a specified node. |
| [node](/advanced-algorithms/available-algorithms/node) | C++ | A module that provides a comprehensive toolkit for managing graph nodes, enabling creation, deletion, updating, merging, and more. |
| [nodes](/advanced-algorithms/available-algorithms/nodes) | C++ | A module that provides a comprehensive toolkit for managing multiple graph nodes, enabling linking, updating, type deduction and more. |
diff --git a/pages/advanced-algorithms/available-algorithms/_meta.ts b/pages/advanced-algorithms/available-algorithms/_meta.ts
index b56fae365..f2ec94d59 100644
--- a/pages/advanced-algorithms/available-algorithms/_meta.ts
+++ b/pages/advanced-algorithms/available-algorithms/_meta.ts
@@ -38,6 +38,7 @@ export default {
"meta_util": "meta_util",
"meta": "meta",
"migrate": "migrate",
+ "mgps": "mgps",
"neighbors": "neighbors",
"node_similarity": "node_similarity",
"node": "node",
diff --git a/pages/advanced-algorithms/available-algorithms/date.mdx b/pages/advanced-algorithms/available-algorithms/date.mdx
index 082354115..f99a7dfc1 100644
--- a/pages/advanced-algorithms/available-algorithms/date.mdx
+++ b/pages/advanced-algorithms/available-algorithms/date.mdx
@@ -27,9 +27,7 @@ Memgraph for managing time-based data.
| Trait | Value |
| ------------------- | ------------------- |
| **Module type** | util |
-| **Implementation** | C++ |
-| **Graph direction** | directed/undirected |
-| **Edge weights** | weighted/unweighted |
+| **Implementation** | Python |
| **Parallelism** | sequential |
## Procedures
@@ -152,6 +150,8 @@ YIELD parsed RETURN parsed;
+---------------------+
```
+## Functions
+
### `add()`
Adds two numeric values representing quantities of time in specific units.
@@ -187,4 +187,49 @@ RETURN date.add(100, "day", 24, "h") AS sum;
+---------------------+
| 101 |
+---------------------+
+```
+
+### `convert_format()`
+
+Converts a string containing a date or time in one specified format into a
+string of another specified date or time format.
+
+{
Input:
}
+
+- `temporal: string` ➡ The date to be converted.
+- `currentFormat: string` ➡ The format of the input `temporal` string date.
+- `convertTo: string` ➡ The required output date string format.
+
+`conert_format` supports the following formats for both `currentFormat` and
+`convertTo`:
+
+- `basic_iso_date` - `YYYYMMDD` (e.g., `20111203`)
+- `iso_local_date` - `YYYY-MM-DD` (e.g., `2011-12-03`)
+- `iso_offset_date` - `YYYY-MM-DD±hh:mm` (e.g., `2011-12-03+01:00`)
+- `iso_date` - `YYYY-MM-DD` or `YYYY-MM-DD±hh:mm` (e.g., `2011-12-03+01:00`)
+- `iso_local_time` - `HH:MM:SS` (e.g., `10:15:30`)
+- `iso_offset_time` - `HH:MM:SS±hh:mm` (e.g., `10:15:30+01:00`)
+- `iso_time` - `HH:MM:SS` or `HH:MM:SS±hh:mm` (e.g, `10:15:30+01:00`)
+- `iso_local_date_time` - `YYYY-MM-DDTHH:MM:SS` (e.g., `2011-12-03T10:15:30`)
+- `iso_offset_date_time` - `YYYY-MM-DDTHH:MM:SS±hh:mm` (e.g., `2011-12-03T10:15:30+01:00`)
+- `iso_zoned_date_time` - `YYYY-MM-DDTHH:MM:SS±hh:mm[ZoneName]` (e.g., `2011-12-03T10:15:30+01:00[Europe/Paris]`)
+- `iso_date_time` - `YYYY-MM-DDTHH:MM:SS±hh:mm[ZoneName]` (e.g, `2011-12-03T10:15:30+01:00[Europe/Paris]`)
+
+{ Output:
}
+
+The output of this function is a `string` value representing the `temporal` input
+expressed in the required `convertTo` format.
+
+{ Usage:
}
+
+```cypher
+RETURN date.convert_format('2011-12-03T10:15:30+01:00[Europe/Paris]', 'iso_zoned_date_time', 'iso_offset_date_time') AS dt;
+```
+
+```plaintext
++-----------------------------+
+| dt |
++-----------------------------+
+| "2011-12-03T10:15:30+01:00" |
++-----------------------------+
```
\ No newline at end of file
diff --git a/pages/advanced-algorithms/available-algorithms/mgps.mdx b/pages/advanced-algorithms/available-algorithms/mgps.mdx
new file mode 100644
index 000000000..7143d01c8
--- /dev/null
+++ b/pages/advanced-algorithms/available-algorithms/mgps.mdx
@@ -0,0 +1,60 @@
+---
+title: mgps
+description: Memgraph's mgps module contains various utility functions for general-purpose tasks.
+---
+
+import { Steps } from 'nextra/components'
+import { Callout } from 'nextra/components'
+import { Cards } from 'nextra/components'
+import GitHub from '/components/icons/GitHub'
+
+# mgps
+
+A module containing utility functions for general purpose tasks.
+
+
+ }
+ title="Source code"
+ href="https://github.com/memgraph/memgraph/blob/master/query_modules/mgps.py"
+ />
+
+
+| Trait | Value |
+| ------------------- | ---------- |
+| **Module type** | util |
+| **Implementation** | Python |
+| **Parallelism** | sequential |
+
+## Functions
+
+### `validate_predicate()`
+
+Raises an exception if the given predicate yields `true`; otherwise returns
+`true`. This is useful when checking assertions in `WHERE` clauses, because it
+means you can filter out data that doesn't meet your validation criteria, and
+provide an error if the validation fails for a specific reason you want to
+highlight.
+
+{ Input:
}
+- `predicate: boolean` ➡ Predicate condition to evaluate.
+- `message: string` ➡ The text of the error message raised if the predicate
+yields `true`. This can contain placeholder, such as `%s`, which are
+interpolated from the `params` argument.
+- `params: list` ➡ The list of parameters to substitute into placeholders in `message`.
+
+{ Output:
}
+
+- `true: boolean` ➡ This function either returns `true`, or aborts the query by
+raising an exception.
+
+{ Usage:
}
+
+Use the following query to validate that `age` is never negative, and to raise
+an exception if the predicate is `true`.
+
+```cypher
+MATCH (n:User)
+WHERE mgps.validate_predicate(n.age < 0, "Invalid age: %i", [n.age])
+RETURN n;
+```
diff --git a/pages/advanced-algorithms/available-algorithms/refactor.mdx b/pages/advanced-algorithms/available-algorithms/refactor.mdx
index 291dd981f..b4f65d71e 100644
--- a/pages/advanced-algorithms/available-algorithms/refactor.mdx
+++ b/pages/advanced-algorithms/available-algorithms/refactor.mdx
@@ -965,4 +965,93 @@ CALL refactor.rename_type_property("distance_in_km","distance",[r]) YIELD relati
+---------------------------------------+
| 1 |
+---------------------------------------+
-```
\ No newline at end of file
+```
+
+### `mergeNodes()`
+
+Merges the properties, labels and relationships for the source nodes to the target node.
+
+{ Input:
}
+
+- `subgraph: Graph` (**OPTIONAL**) ➡ A specific subgraph, which is an [object of type Graph](/advanced-algorithms/run-algorithms#run-procedures-on-subgraph) returned by the `project()` function, on which the algorithm is run.
+If subgraph is not specified, the algorithm is computed on the entire graph by default.
+
+- `nodes: List[Node]` ➡ List of nodes that will be merged into the first node of the list. Exception is thrown if the node list is empty.
+- `config: Map` ➡ Configuration parameters:
+ - `properties: string` ➡ values: `combine`, `override/overwrite`, `discard`:
+ - `combine` ➡ if there are multiple values of the properties, they will be merged into a list of values. if there is only one property, then it will be the same value
+ - `override` ➡ last property in the list wins
+ - `overwrite` ➡ same as `override`
+ - `discard` ➡ discard the properties of the nodes - value will be null
+ - `.*: string` ➡ equivalent to properties
+ - `mergeRels: boolean` ➡ specify whether the relationships will be merged into the target node or discarded.
+
+{ Output:
}
+
+- `node: Node` ➡ Merged node with updated properties, labels and relationships.
+
+{ Usage:
}
+
+Create a graph using the queries below:
+
+```cypher
+CREATE (n1:Person {name: 'Alice', age: 30, city: 'New York'})
+CREATE (n2:Person {name: 'Bob', age: 25, country: 'USA'});
+```
+
+Use the procedure to merge the nodes:
+
+```cypher
+MATCH (n1:Person {name: 'Alice'}), (n2:Person {name: 'Bob'})
+CALL refactor.merge_nodes([n1, n2], {properties: 'combine'}) YIELD node
+RETURN node.name as name, node.age as age, node.city as city, node.country as country;
+```
+
+or
+
+```cypher
+MATCH (n1:Person {name: 'Alice'}), (n2:Person {name: 'Bob'})
+CALL refactor.merge_nodes([n1, n2], {`.*`: 'combine'}) YIELD node
+RETURN node.name as name, node.age as age, node.city as city, node.country as country;
+```
+
+The procedure returns the merged node properties:
+
+```plaintext
++-----------------------------------------------------+
+| name | age | city | country |
++-----------------------------------------------------+
+| ["Alice", "Bob"] | [30, 25] | "New York" | "USA" |
++-----------------------------------------------------+
+```
+
+The following scenario will also merge the relationships:
+
+Create a graph using the queries below:
+
+```cypher
+CREATE (n1:Person {name: 'Alice', age: 30, city: 'New York'})
+CREATE (n2:Person {name: 'Bob', age: 25, country: 'USA'})
+CREATE (n3:Person {name: 'Charlie', age: 35, city: 'London'})
+CREATE (n1)-[:KNOWS {since: 2020}]->(n2)
+CREATE (n2)-[:WORKS_WITH {project: 'Project X'}]->(n3)
+CREATE (n3)-[:FRIENDS_WITH {since: 2019}]->(n1);
+```
+
+Use the procedure to merge the nodes and relationships:
+
+```cypher
+ MATCH (n1:Person {name: 'Alice'}), (n2:Person {name: 'Bob'}), (n3:Person {name: 'Charlie'})
+ CALL refactor.merge_nodes([n1, n2, n3], {properties: 'combine', mergeRels: true}) YIELD node
+ RETURN node.name as name, outDegree(node) as out_degree, inDegree(node) as in_degree;
+```
+
+The procedure returns the merged node properties:
+
+```plaintext
++----------------------------------------------------+
+| name | inDegree | outDegree |
++----------------------------------------------------+
+| ["Alice", "Bob", "Charlie"] | 3 | 3 |
++----------------------------------------------------+
+```
diff --git a/pages/advanced-algorithms/available-algorithms/text.mdx b/pages/advanced-algorithms/available-algorithms/text.mdx
index fa0cbe23a..cdacb0e7b 100644
--- a/pages/advanced-algorithms/available-algorithms/text.mdx
+++ b/pages/advanced-algorithms/available-algorithms/text.mdx
@@ -120,7 +120,7 @@ If subgraph is not specified, the algorithm is computed on the entire graph by d
{ Usage:
}
-Use the following query to insert the parameters to the placeholders in the sentence:
+Use the following queries to insert the parameters to the placeholders in the sentence:
```cypher
CALL text.format("Memgraph is the number {} {} in the world.", [1, "graph database"])
@@ -137,3 +137,106 @@ Result:
| "Memgraph is the number 1 graph database in the world. "|
+---------------------------------------------------------+
```
+
+### `replace()`
+
+Replace each substring of the given string that matches the given regular expression with the given replacement.
+
+{ Input:
}
+
+- `subgraph: Graph` (**OPTIONAL**) ➡ A specific subgraph, which is an [object of type Graph](/advanced-algorithms/run-algorithms#run-procedures-on-subgraph) returned by the `project()` function, on which the algorithm is run.
+If subgraph is not specified, the algorithm is computed on the entire graph by default.
+- `text: string` ➡ Text that needs to be replaced.
+- `regex: string` ➡ Regular expression by which to replace the string.
+- `replacement: string` ➡ Target string to replace the matched string.
+
+{ Usage:
}
+
+Use the following queries to do text replacement:
+
+```cypher
+RETURN text.replace('Hello World!', '[^a-zA-Z]', '') AS result;
+```
+
+Result:
+
+```plaintext
++--------------+
+| result |
++--------------+
+| "HelloWorld" |
++--------------+
+```
+
+```cypher
+RETURN text.replace('MAGE is a Memgraph Product', 'MAGE', 'GQLAlchemy') AS result;
+```
+
+Result:
+
+```plaintext
++------------------------------------+
+| result |
++---------- -------------------------+
+| "GQLAlchemy is a Memgraph Product" |
++------------------------------------+
+```
+
+### `regReplace()`
+
+Replace each substring of the given string that matches the given regular expression with the given replacement.
+
+{ Input:
}
+
+- `subgraph: Graph` (**OPTIONAL**) ➡ A specific subgraph, which is an [object of type Graph](/advanced-algorithms/run-algorithms#run-procedures-on-subgraph) returned by the `project()` function, on which the algorithm is run.
+If subgraph is not specified, the algorithm is computed on the entire graph by default.
+- `text: string` ➡ Text that needs to be replaced.
+- `regex: string` ➡ Regular expression by which to replace the string.
+- `replacement: string` ➡ Target string to replace the matched string.
+
+{ Usage:
}
+
+Use the following query to do text replacement:
+
+```cypher
+RETURN text.regreplace("Memgraph MAGE Memgraph MAGE", "MAGE", "GQLAlchemy") AS output;
+```
+
+Result:
+
+```plaintext
++---------------------------------------+
+| result |
++---------------------------------------+
+| "GQLAlchemy MAGE Memgraph GQLAlchemy" |
++---------------------------------------+
+```
+
+### `distance()`
+
+Compare the given strings with the Levenshtein distance algorithm.
+
+{ Input:
}
+
+- `subgraph: Graph` (**OPTIONAL**) ➡ A specific subgraph, which is an [object of type Graph](/advanced-algorithms/run-algorithms#run-procedures-on-subgraph) returned by the `project()` function, on which the algorithm is run.
+If subgraph is not specified, the algorithm is computed on the entire graph by default.
+- `text1: string` ➡ Source string.
+- `text2: string` ➡ Destination string for comparison.
+
+{ Usage:
}
+
+Use the following query to calculate distance between texts:
+
+```cypher
+RETURN text.distance("Levenshtein", "Levenstein") AS result;
+```
+
+Result:
+
+```plaintext
++--------+
+| result |
++--------+
+| 1 |
++--------+
+```
diff --git a/pages/client-libraries/graphql.mdx b/pages/client-libraries/graphql.mdx
index 25e6ad94e..2efc50db5 100644
--- a/pages/client-libraries/graphql.mdx
+++ b/pages/client-libraries/graphql.mdx
@@ -15,19 +15,18 @@ queries against it.
## Quickstart
-We'll be using a simple Node.js application using the Neo4j GraphQL Library to connect
-to a running Memgraph instance and execute GraphQL queries against the database.
+We'll be using a simple Node.js application using the Neo4j GraphQL Library to
+connect to a running Memgraph instance and execute GraphQL queries against the
+database.
Necessary prerequisites that should be installed in your local environment are:
-- [The Neo4j GraphQL Library](https://neo4j.com/docs/graphql/current/).
- The [npm](https://www.npmjs.com/) package manager.
- The [Node.js](https://nodejs.org/en) runtime environment.
-- A utility that can be used to host the GraphQL schema, e.g. [Apollo Server](https://www.apollographql.com/docs/apollo-server/).
-### Run Memgraph
+### Run Memgraph
If you're new to Memgraph or you're in a developing stage, we
recommend using the Memgraph Platform. Besides the database, it also
@@ -58,7 +57,7 @@ communicate with the client using the exposed 7687 port. Memgraph Lab is a web
application you can use to visualize the data. It's accessible at
[http://localhost:3000](http://localhost:3000) if Memgraph Platform is running
correctly. The 7444 port enables Memgraph Lab to access and preview the logs,
-which is why both of these ports need to be exposed.
+which is why both of these ports need to be exposed.
For more information visit the getting started guide on [how to run Memgraph
with Docker](/getting-started/install-memgraph/docker).
@@ -66,7 +65,7 @@ with Docker](/getting-started/install-memgraph/docker).
### Create a work directory
-Next, create a directory for your project and positioning yourself in it:
+Next, create a directory for your project and position yourself in it:
```
mkdir graphql-example
@@ -83,36 +82,98 @@ npm init es6 --yes
### Setup the Neo4j GraphQL Library
-Install the library and its dependencies by running the following command:
+Install the library, its dependencies, and a GraphQL server (such as [Apollo
+Server](https://www.apollographql.com/docs/apollo-server/)) by running the
+following command:
```
-npm install @neo4j/graphql graphql neo4j-driver @apollo/server
+npm install @neo4j/graphql@7.2.0 graphql neo4j-driver @apollo/server
```
+
+Memgraph is currently compatible with `@neo4j/graphql@7.2.0`. Other versions of
+the GraphQL middleware may not generate Memgraph-compatible queries.
+
+
+### Create the Schema
+
+GraphQL APIs need to be aware of the shape of the data they are working with,
+which is abstracted into the concept of GraphQL schemas. This must be explicitly
+specified in the Node.js application file. In terms of a property graph, the
+schema must contain the labels and properties of nodes and relationships.
+(Whilst the Neo4j GraphQL Library has support for automatic graph introspection,
+Memgraph currently does not support this feature.)
+
+More information on defining a GraphQL schema can be found in the
+[GraphQL documentation](https://graphql.org/learn/schema/).
+
+Given the following dataset:
+
+```cypher
+CREATE (u1:User {id: "user1", name: "Alice"})
+CREATE (u2:User {id: "user2", name: "Bob"})
+CREATE (p1:Post {id: "post1", content: "Alice's first post!"})
+CREATE (p2:Post {id: "post2", content: "Bob's great idea."})
+CREATE (p3:Post {id: "post3", content: "Another thought from Alice."})
+CREATE (u1)-[:HAS_POST]->(p1)
+CREATE (u2)-[:HAS_POST]->(p2)
+CREATE (u1)-[:HAS_POST]->(p3);
+```
+
+We could create the following schema, where `type Post @node` corresponds to a
+vertex with a `:Post` label, `content: String` to a `content` property on the
+vertex, and `:HAS_POST` edges are notated using the `@relationship` directive
+with type `HAS_POST`.
+
+```graphql
+type Post @node {
+ id: ID! @id
+ content: String!
+ creator: [User!]! @relationship(type: "HAS_POST", direction: IN)
+}
+
+type User @node {
+ id: ID! @id
+ name: String
+ posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT)
+}
+```
+
+
+Note that relationships in the schema must always be expressed as many-to-many
+(i.e, as array `[ ]` types), even if they model one-to-one, one-to-many, or
+many-to-one relationships. This is a restriction of the GraphQL middleware.
+
+
### Create the application file
-The application file will be used to setup the Neo4j GraphQL Library and connect to the running Memgraph instance.
+The application file will be used to setup the Neo4j GraphQL Library and connect
+to the running Memgraph instance.
-Create the file and add the content below.
+Create the file:
```
touch index.js
```
+Add the content below. Note that the schema we created above is used as a
+string literal for `typeDefs`:
+
+
```js
import { Neo4jGraphQL } from "@neo4j/graphql";
import { ApolloServer } from '@apollo/server'
import { startStandaloneServer } from '@apollo/server/standalone';
import neo4j from 'neo4j-driver'
-const typeDefs = `#graphql
-type Post {
+const typeDefs = `
+type Post @node {
id: ID! @id
content: String!
- creator: User! @relationship(type: "HAS_POST", direction: IN)
+ creator: [User!]! @relationship(type: "HAS_POST", direction: IN)
}
-type User {
+type User @node {
id: ID! @id
name: String
posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT)
@@ -131,16 +192,24 @@ const server = new ApolloServer({
});
const { url } = await startStandaloneServer(server, {
- context: async ({ req }) => ({ req, sessionConfig: {database: "memgraph"}}),
+ context: async ({ req }) => ({
+ req,
+ sessionConfig: { database: "memgraph" },
+ cypherQueryOptions: { addVersionPrefix: false }
+ }),
listen: { port: 4000 },
});
console.log(`🚀 Server ready at ${url}`);
-```
-The example above shows that the data schema must be explicitly defined. The GraphQL server can only process queries that operate on data conforming to the specified schemas. In this example, the schema defines a graph, where the nodes labeled as `User` or `Post` may be connected with a `HAS_POST` relationship.
+```
-To set up a GraphQL client against a running Memgraph instance, you have to specify the GraphQL schema, set up the used drivers, and specify Memgraph as the used database. Specifying the used database is especially important because the default database name in the `driverConfig` is not compatible with Memgraph.
+To set up a GraphQL client against a running Memgraph instance, you have to
+specify the GraphQL schema, set up the used drivers, and specify Memgraph as the
+used database. Specifying the used database is especially important because the
+default database name in the `driverConfig` is not compatible with Memgraph.
+You must also use `cypherQueryOptions: { addVersionPrefix: false }` to ensure
+that any Cypher queries are compatible with Memgraph.
### Run the application
@@ -152,27 +221,69 @@ node index.js
### Interact with Memgraph
-You are now ready to interact with Memgraph using GraphQL queries through **localhost(127.0.0.1):4000**.
+You are now ready to interact with Memgraph using GraphQL queries through the
+GraphQL server on `localhost:4000` or `127.0.0.1:4000`.
+
+Running a GraphQL query on the example dataset, such as:
+
+```graphql
+query Users {
+ users {
+ name
+ posts {
+ content
+ }
+ }
+}
+```
+
+will yield the following results:
+
+```json
+{
+ "data": {
+ "users": [
+ {
+ "name": "Alice",
+ "posts": [
+ {
+ "content": "Alice's first post!"
+ },
+ {
+ "content": "Another thought from Alice."
+ }
+ ]
+ },
+ {
+ "name": "Bob",
+ "posts": [
+ {
+ "content": "Bob's great idea."
+ }
+ ]
+ }
+ ]
+ }
+}
+```
-## Current limitations
+## Features
-Memgraph currently supports CRUD operations using GraphQL. That means that the entities inside
-the database can be updated and basic traversals can be done, but more complicated operations
-are currently not supported through GraphQL.
+Memgraph support includes the following GraphQL features:
-GraphQL APIs need to be aware of the shape of data they are working with which is abstracted
-into the concept of GraphQL schemas. Currently this has to be specified in the Node.js
-application file explicitly. In terms of a property graph, this means the labels and properties
-of nodes and relationships. The Neo4j GraphQL Library has support for automatic graph
-introspection, Memgraph currently does not support this feature.
+- [Read queries based on a defined schema.](https://neo4j.com/docs/graphql/current/queries-aggregations/queries/)
+- [Aggregations on nodes and edges.](https://neo4j.com/docs/graphql/current/queries-aggregations/aggregations/)
+- [Sorting results on any object type defined in the schema.](https://neo4j.com/docs/graphql/current/queries-aggregations/sorting/)
+- [Offset and cursor based pagination.](https://neo4j.com/docs/graphql/current/queries-aggregations/pagination/)
+- [Create, update, and delete mutations.](https://neo4j.com/docs/graphql/current/mutations/)
+- [Custom Cypher statements with the `@cypher` directive.](https://neo4j.com/docs/graphql/current/directives/custom-logic/#_cypher)
If you encounter serialization errors while using GraphQL client, we recommend
referring to our [Serialization errors](/help-center/errors/serialization) page
-for detailed guidance on troubleshooting and best practices.
-
+for detailed guidance on troubleshooting and best practices.
diff --git a/pages/database-management/monitoring.mdx b/pages/database-management/monitoring.mdx
index 167343d63..63671ecef 100644
--- a/pages/database-management/monitoring.mdx
+++ b/pages/database-management/monitoring.mdx
@@ -496,6 +496,9 @@ and describes a particular operation.
| SwapMainUUIDRpcSuccess | Counter | The number of times coordinators received successful response to SwapMainUUIDRpc. |
| GetDatabaseHistoriesRpcFail | Counter | The number of times coordinators received unsuccessful or no response to GetDatabaseHistoriesRpc. |
| GetDatabaseHistoriesRpcSuccess | Counter | The number of times coordinators received successful response to GetDatabaseHistoriesRpc. |
+ | ReplicaRecoverySuccess | Counter | The number of times the replica recovery process finished successfully. |
+ | ReplicaRecoveryFail | Counter | The number of times the replica recovery process finished unsuccessfully. |
+ | ReplicaRecoverySkip | Counter | The number of times the replica recovery task was skipped. |
All HA metrics with type `Counter` are aggregated for all coordinators. That makes it easier for users to track what is going on since they don't need to aggregate
diff --git a/pages/fundamentals/indexes.mdx b/pages/fundamentals/indexes.mdx
index c9a7af220..9f60dc098 100644
--- a/pages/fundamentals/indexes.mdx
+++ b/pages/fundamentals/indexes.mdx
@@ -84,6 +84,14 @@ label-property combination using the `CREATE INDEX ON` syntax or `CREATE EDGE IN
### Label index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
To optimize queries that fetch nodes by label, you need to create a label index:
```cypher
@@ -103,6 +111,14 @@ DROP INDEX ON :Label;
### Label-property index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
To optimize queries that fetch nodes with a certain label and property
combination, you need to create a label-property index. For the best
performance, create an index on properties containing unique integer values.
@@ -208,6 +224,14 @@ DROP INDEX ON :Label(property);
### Label-property composite index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
An index on a label and two or more properties is called a **composite index**.
Composite indices optimize queries where multiple properties are commonly used
together in filtering clauses, or when uniqueness exists in a combination of
@@ -355,6 +379,14 @@ DROP INDEX ON :Label(property1, property2);
### Edge-type index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
To optimize queries that fetch only the edges by specific edge-types, you need
to create an edge-type index. Creating an edge-type index requires the
`--storage-properties-on-edges` flag to be set to true.
@@ -385,6 +417,14 @@ DROP EDGE INDEX ON :EDGE_TYPE;
### Edge-type property index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
To optimize queries that fetch only the edges by specific edge types and
properties, you need to create an edge-type property index. Creating an
edge-type property index requires the `--storage-properties-on-edges` flag to be
@@ -415,6 +455,14 @@ DROP EDGE INDEX ON :EDGE_TYPE(property_name);
### Global edge property index
+
+
+This index supports non-blocking creation: reads continue without interruption,
+while writes are briefly paused. For more information, see the [concurrent index
+creation](#concurrent-index-creation).
+
+
+
Since edges can have only one edge type, Memgraph provides a way to index all the edges that contain
a certain property via the global edge index. The syntax to index all the edges that have a certain property is
the following:
@@ -548,7 +596,7 @@ memgraph> MATCH (n:Person) WHERE n.name =~ ".*an$" RETURN n.name;
## Show created indexes
To see all the information on the label, label-property, edge-type, edge-type
-property and point indexes, run the following query:
+property, point indexes and vector indexes, run the following query:
```cypher
SHOW INDEX INFO;
@@ -821,3 +869,35 @@ based on the type, and secondly for values within that type.
In the case of composite indices, the list is lexicographically ordered, which
means that the pair $(a_1, a_2)$ is ordered before $(b_1, b_2)$ if either $a_1 <
b_1$, or $a_1 = b_1$ and $a_2 < b_2$.
+
+### Concurrent index creation
+
+Memgraph supports **(almost) fully concurrent index creation** for all
+skiplist-based indices, including label, label-property, composite, edge-type,
+edge-type property, and global edge property, with minimal impact on
+performance.
+
+The three-phase implementation begins with a brief **registration phase** that
+requires `READ ONLY` access. This ensures that all pending write transactions
+complete before the index creation begins. During this phase, the database waits
+for ongoing writes to finish (typically milliseconds) while blocking new write
+transactions. Read queries are not affected by this.
+
+Once registered:
+- Read queries continue uninterrupted throughout index creation
+- Write operations can proceed concurrently after registration
+- Existing data is indexed in the background
+
+If you encounter write transaction failures during registration, you can
+increase the wait time using the `--storage-access-timeout-sec` flag, which
+extends how long write queries will retry before failing.
+
+The system maintains **full MVCC consistency** throughout the process, ensuring
+transactional integrity. Long-running index operations can be safely cancelled
+if needed. Currently, some features like replication synchronization and TTL
+indices still use blocking mode during operations, though these limitations will
+be addressed in future releases.
+
+For complete technical details about the implementation, consistency guarantees,
+and current limitations, please refer to the [Concurrent Index Creation
+ADR](https://github.com/memgraph/memgraph/blob/master/ADRs/004_concurrent_index_creation.md).
diff --git a/pages/querying/functions.mdx b/pages/querying/functions.mdx
index 1c0d71ec7..a1ad8c0cb 100644
--- a/pages/querying/functions.mdx
+++ b/pages/querying/functions.mdx
@@ -44,6 +44,7 @@ This section contains the list of supported functions.
| `outDegree` | `outDegree(node: Node) -> (integer)` | Returns the number of outgoing relationships of a node. |
| `inDegree` | `inDegree(node: Node) -> (integer)` | Returns the number of incoming relationships of a node. |
| `endNode` | `endNode(relationship: Relationship) -> (Node)` | Returns the destination node of a relationship. |
+ | `gethopsCounter` | `getHopsCounter() -> (integer)` | Returns the current number of hops traversed during the query execution. |
| `head` | `head(list: List[any]) -> (any)` | Returns the first element of a list. |
| `id` | `id(value: Node\|Relationship) -> (integer)` | Returns identifier for a given node or relationship. The identifier is generated during the initialization of a node or a relationship and will be persisted through the durability mechanism. |
| `last` | `last(list: List[any]) -> (any)` | Returns the last element of a list. |
diff --git a/pages/querying/vector-search.mdx b/pages/querying/vector-search.mdx
index 68139f14a..8440920b7 100644
--- a/pages/querying/vector-search.mdx
+++ b/pages/querying/vector-search.mdx
@@ -19,6 +19,7 @@ specifically operates at `READ_UNCOMMITTED`. This design maintains all
transactional guarantees at the database level. Only the vector index
operations use this relaxed isolation level, ensuring the database's ACID
properties remain intact for all other operations.
+Memgraph supports both vector index on nodes and on edges.
To configure vector search as described in the example, please use the latest Memgraph version.
@@ -47,6 +48,11 @@ CREATE VECTOR INDEX vector_index_name ON :Label(embedding) WITH CONFIG {"dimensi
Note that `dimension` and `capacity` are mandatory configuration required to create a vector index.
+To create vector index on edges, next syntax should be used:
+```shell
+CREATE VECTOR EDGE INDEX vector_index_name ON :EDGE_TYPE(embedding) WITH CONFIG {"dimension": 256, "capacity": 1000};
+```
+
### Configuration parameters
Below is a list of all configuration options:
@@ -56,6 +62,7 @@ Below is a list of all configuration options:
- `metric: string (default=l2sq)` ➡ The metric used for the vector search. The default value is `l2sq` (squared Euclidean distance).
- `resize_coefficient: int (default=2)` ➡ When the index reaches its capacity, it resizes by multiplying the current capacity by this coefficient, if sufficient memory is available.
If resizing fails due to memory limitations, an exception will be thrown. Default value is `2`.
+- `scalar_kind: string (default=f32)` ➡ The scalar type used to store each vector component. Smaller types reduce memory usage but may decrease precision.
## Run vector search
@@ -81,6 +88,9 @@ Additionally, the same information can be retrieved with the `SHOW VECTOR INDEX
- `capacity: int` ➡ The capacity of the vector index.
- `metric: string` ➡ Metric used for vector search similarity.
- `size: int` ➡ The number of entries in the vector index.
+- `scalar_kind: string` ➡ The scalar type used for each vector element.
+- `index_type: string` ➡ The type of the index. If the vector index is created on nodes, the output will be `label+property_vector`;
+if created on edges, it will be `edge-type+property_vector`.
{ Usage:
}
@@ -96,8 +106,8 @@ SHOW VECTOR INDEX INFO;
### Query vector index
-Use the `vector_search.search()` procedure to search for similar vectors within
-a vector index. This procedure allows you to find the closest vectors to a query
+Use the `vector_search.search()` and `vector_search.search_edges()` procedures to search for similar vectors within
+a vector index. This procedures allows you to find the closest vectors to a query
vector based on the selected similarity metric.
{ Input:
}
@@ -108,16 +118,29 @@ vector based on the selected similarity metric.
{ Output:
}
+When the index is defined on nodes:
+
- `distance: double` ➡ The distance from the node to the query.
- `node: Vertex` ➡ A node in the vector index matching the given query.
- `similarity: double` ➡ The similarity of the node and the query.
+When the index is defined on edges:
+
+- `distance: double` ➡ The distance from the node to the query.
+- `edges: Relationship` ➡ A node in the vector index matching the given query.
+- `similarity: double` ➡ The similarity of the node and the query.
+
{ Usage:
}
```shell
CALL vector_search.search("index_name", 1, [2.0, 2.0]) YIELD * RETURN *;
```
+To query an index on edges, use:
+```shell
+CALL vector_search.search_edges("index_name", 1, [2.0, 2.0]) YIELD * RETURN *;
+```
+
### Similarity metrics
The following table lists the supported similarity metrics for vector search. These
@@ -139,9 +162,28 @@ for the metric is `l2sq` (squared Euclidean distance).
### Scalar type
-Properties are stored as 64-bit values in the property store and as 32-bit values in the vector index.
-Scalar type define the data type of each vector element. Default type for the
-metric is `f32`.
+Properties are stored as 64-bit values in the property store. However, for efficiency, vector elements in the vector index are stored using 32-bit values by default.
+The `scalar_kind` setting determines the data type used for each vector element in the index. By default, the scalar type is set to `f32` (32-bit floating point),
+which provides a good balance between precision and memory usage. Alternative options, such as `f16` for lower memory usage or `f64` for higher precision, allow you to fine-tune this tradeoff based on your specific needs.
+
+| Scalar | Description |
+|-----------|------------------------------------------------------------|
+| `b1x8` | Binary format (1 bit per element, stored in 8-bit chunks). |
+| `u40` | Unsigned 40-bit integer. |
+| `uuid` | Universally unique identifier (UUID). |
+| `bf16` | 16-bit floating point (bfloat16). |
+| `f64` | 64-bit floating point (double). |
+| `f32` | 32-bit floating point (float). |
+| `f16` | 16-bit floating point. |
+| `f8` | 8-bit floating point. |
+| `u64` | 64-bit unsigned integer. |
+| `u32` | 32-bit unsigned integer. |
+| `u16` | 16-bit unsigned integer. |
+| `u8` | 8-bit unsigned integer. |
+| `i64` | 64-bit signed integer. |
+| `i32` | 32-bit signed integer. |
+| `i16` | 16-bit signed integer. |
+| `i8` | 8-bit signed integer. |
## Drop vector index
@@ -179,7 +221,7 @@ After Memgraph MAGE and Lab have been started, head over to the Query execution
tab in Memgraph Lab and run the following query to create vector index:
```cypher
-CREATE VECTOR INDEX index_name ON :Node(vector) WITH CONFIG {"dimension": 2, "capacity": 1000, "metric": "cos","resize_coefficient": 2};
+CREATE VECTOR INDEX index_name ON :Node(vector) WITH CONFIG {"dimension": 2, "capacity": 1000, "metric": "cos", "resize_coefficient": 2, "scalar_kind": "f16"};
```
Then, run the following query to inspect vector index:
@@ -196,11 +238,11 @@ SHOW VECTOR INDEX INFO;
The above query will result with:
```
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| capacity | dimension | index_name | label | property | size |
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| 2048 | 2 | "index_name" | "Node" | "vector" | 0 |
-+--------------+--------------+--------------+--------------+--------------+--------------+
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| capacity | dimension | index_name | label | property | size | scalar_kind | index_type |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| 2048 | 2 | "index_name"| "Node" | "vector" | 0 | "f16" | "label+property_vector" |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
```
{ Create a node
}
@@ -221,11 +263,11 @@ CALL vector_search.show_index_info() YIELD * RETURN *;
The above query results in:
```
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| capacity | dimension | index_name | label | property | size |
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| 2048 | 2 | "index_name" | "Node" | "vector" | 1 |
-+--------------+--------------+--------------+--------------+--------------+--------------+
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| capacity | dimension | index_name | label | property | size | scalar_kind | index_type |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| 2048 | 2 | "index_name"| "Node" | "vector" | 1 | "f16" | "label+property_vector" |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
```
We can see the size of the index changed, due to one new node.
@@ -269,11 +311,11 @@ CALL vector_search.show_index_info() YIELD * RETURN *;
The size is now 5, due to 4 additional nodes:
```
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| capacity | dimension | index_name | label | property | size |
-+--------------+--------------+--------------+--------------+--------------+--------------+
-| 2048 | 2 | "index_name" | "Node" | "vector" | 5 |
-+--------------+--------------+--------------+--------------+--------------+--------------+
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| capacity | dimension | index_name | label | property | size | scalar_kind | index_type |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
+| 2048 | 2 | "index_name"| "Node" | "vector" | 5 | "f16" | "label+property_vector" |
++----------+-----------+-------------+--------+----------+------+-------------+-------------------------+
```
Let's again search for the top five similar nodes to the vector [2.0, 2.0] (to compare it to all nodes we have):
diff --git a/pages/release-notes.mdx b/pages/release-notes.mdx
index dee153020..2e9da7507 100644
--- a/pages/release-notes.mdx
+++ b/pages/release-notes.mdx
@@ -66,6 +66,90 @@ updated.
## 🚀 Latest release
+### Memgraph v3.4.0 - July 10th, 2025
+
+{⚠️ Breaking changes
}
+
+- Introduced a small breaking change under `SHOW INDEX INFO` output.
+ Previously, all vector indexes returned the hardcoded string `"vector"` as
+ their type. Now, the output reflects the actual indexed entity more
+ precisely: for node indexes the type is `label+property_vector`, for edge
+ indexes: `edge-type+property_vector`.
+ [#3059](https://github.com/memgraph/memgraph/pull/3059)
+
+{✨ New features
}
+
+- Quantization options have been added to the vector index. Users can now
+ reduce memory consumption by enabling quantization in the config map, using
+ the `scalar_kind` field to define the scalar type for vectors, at the cost of
+ some precision. [#3037](https://github.com/memgraph/memgraph/pull/3037)
+- Added function `getHopsCounter()` to return the current number of hops
+ traversed in a given query.
+ [#3048](https://github.com/memgraph/memgraph/pull/3048)
+- Added monitoring of replica recovery process. Users will now be able to
+ monitor how often an instance gets into recovery replication state and how
+ often instance gets successfully recovered once in that state.
+ [#3002](https://github.com/memgraph/memgraph/pull/3002)
+- Added support for vector indexing on edges (edge_type & property
+ combination). Users can now create and query vector indexes on edge
+ embeddings. The syntax for creating an edge-based vector index is: `CREATE
+ VECTOR EDGE INDEX vector_index_name ON :EDGE_TYPE(embedding) WITH CONFIG
+ config_map;`. [#3059](https://github.com/memgraph/memgraph/pull/3059)
+
+{🛠️ Improvements
}
+
+- Added support for `@neo4j/graphql` middleware v7.2.0. Users can now leverage
+ better Neo4j / GraphQL compatibility and use Memgraph with the latest GraphQL
+ ecosystem. [#3027](https://github.com/memgraph/memgraph/pull/3027)
+ [#3083](https://github.com/memgraph/memgraph/pull/3083)
+- Added non-blocking index creation. Until this point, index creation was a
+ "stop the world" operation on a given database. With this change, at the time
+ of `CREATE INDEX` queries, memgraph will exhaust all in-flight write
+ operations and pause/queue all new write operations. When all in-flight
+ writes are done, Memgraph will make the index usable and allow writing. Read
+ queries are possible at any time. From the user perspective, `CREATE INDEX`
+ operations should feel completely non-blocking. Only in extreme cases under
+ heavy-write load might there be some pausing of write queries, but none of
+ the writes should be lost.
+ [#2923](https://github.com/memgraph/memgraph/pull/2923)
+ [#3064](https://github.com/memgraph/memgraph/pull/3064)
+ [#3073](https://github.com/memgraph/memgraph/pull/3073)
+ [#3091](https://github.com/memgraph/memgraph/pull/3091)
+
+{🐞 Bug fixes
}
+
+- Queries using regex clauses on label+property indices no longer return an
+ empty results set. Users can now use label+property indices to improve
+ performance of regex queries (although note that, unlike exact text match
+ queries, regex queries require an additional filtering step).
+ [#3025](https://github.com/memgraph/memgraph/pull/3025)
+- Queries now support directly accessing properties of maps nested inside
+ lists, such as `RETURN [{name: 'Alice'}, {name: 'Bob'}][1].name;`. This
+ allows users to more easily work with Memgraph if their schema contains
+ nested data. [#3058](https://github.com/memgraph/memgraph/pull/3058)
+- Fixed HA upgrade. Upgrade of coordinators from 3.2.1 to 3.3 was failing due
+ to a bug in the serialisation code. With this bug fix, users can expect
+ smooth upgrade of coordinators.
+ [#3078](https://github.com/memgraph/memgraph/pull/3078)
+- Executing `RECOVER SNAPSHOT` on `MAIN` will propagate the snapshot to
+ replicas as well. This simplifies the recovery of the whole cluster. Use
+ `RECOVER SNAPSHOT` to easily overwrite the data stored in a database.
+ [3031](https://github.com/memgraph/memgraph/pull/3031)
+
+### MAGE v3.4.0 - July 10th, 2025
+
+{✨ New features
}
+
+- Added `text.replace`, `text.regreplace` and `text.distance` functions.
+ [#621](https://github.com/memgraph/mage/pull/621)
+- Added `refactor.mergeNodes` procedure.
+ [#623](https://github.com/memgraph/mage/pull/623)
+- Added `date.convert_format()` function, which converts temporal strings in
+ one format to another temporal string format.
+ [#631](https://github.com/memgraph/mage/pull/631)
+
+## Previous releases
+
### Memgraph v3.3.0 - June 4th, 2025
{✨ New features
}
@@ -74,7 +158,7 @@ updated.
high priority queries. Users should never see a blocked database due to heavy
load. Users can now run specific high priority queries even under heavy loads
(example: SHOW TRANSACTIONS).
- [2567](https://github.com/memgraph/memgraph/pull/2567)
+ [#2567](https://github.com/memgraph/memgraph/pull/2567)
- Added support for creating indices on nested properties within maps. Users
can now create efficient indices on deeply nested data structures using dot
notation, such as `CREATE INDEX ON :User(preferences.notifications.email)`,
@@ -163,8 +247,6 @@ updated.
-## Previous releases
-
### Memgraph v3.2.1 - May 9th, 2025
{🛠️ Improvements
}