What is Helixir?
Helixir is a Rustlings-style tutorial for HelixDB. It is a collection of exercises that will help you learn how to create a simple hierarchical HelixDB database. After completing the tutorial, you will be able to translate the concepts in this guide to your own use cases.Getting Started
Installing HelixirInstall Helixir using the following command:
Install the HelixDB CLI using the following command:
Defining Nodes
We will be using HelixDB to model the relationships between continents, countries, and cities as a graph. First, we have to define what kind of entities/nodes will be in our graph. From the schema, we can see that we have 3 types of nodes: continents, countries, and cities. Node Definitions- The
Continentnode will have anameproperty (String) - The
Countrynode will have:name(String),currency(String),population(U64), andgdp(F64) - The
Citynode will have:name(String),description(String), andzip_codes(array of strings)
- Create a
Continent,Country, andCitynode with their respective properties inschema.hx.
Answer
Answer
schema.hx
Defining Relationships
Now that we know what type of nodes are in our schema, we will define the relationships between those nodes. For this example, there is a hierarchical pattern where a city is in a country and a country is in a continent. Instructions:- Create a
Continent_to_CountryandCountry_to_Cityedge connecting their respective nodes with no properties inschema.hx.
Answer
Answer
schema.hx
Meta Relationships
In addition to the structural relationships between the nodes, you can also define relationships based on metadata. For example, a country must have a capital city. Instructions:- Create a
Country_to_Capitaledge connectingCountrytoCityinschema.hx.
Answer
Answer
schema.hx
Defining Vectors
Vectors in HelixDB allow us to create vector-based searches for semantic similarity. A vector is an array of floating-point numbers that represents the semantic meaning of data. In this case, we’ll create a vector for city descriptions to enable semantic search capabilities. Instructions:- Create a
CityDescriptionvector withvectorproperty that takes an array ofF64.
Answer
Answer
schema.hx
Basic Node Creation
Now that we have our schema, we need to write queries to insert the data. The best way to go about this given the structure of our data is to go from top (broad) to bottom (narrow) of the hierarchy. First, we will start with a basic query to create a continent. Usually, creation queries almost always include all the properties of the node in the arguments. In this case, we only need to know the continent’s name. Then we will useAddN to add a Continent node with property name. For best practices, make sure to return the continent that was added in your query.
Instructions:
- Write a query to create a
Continentnode inqueries.hx.
createContinentname: String
Answer
Answer
queries.hx
Relational Node Creation
Most of the nodes in our schema are related to other nodes, which means that we have to also create edges between them. However, we can optimize this process by creating both the node and the edge connecting it to other existing nodes in one query. In this exercise, we will create aCountry node and connect it to its corresponding Continent node. First we will first create a new Country node using AddN. Then we will get the Continent node via the node’s ID so that we can create a Continent_to_Country edge going from the created Continent to Country node using AddE. We will also do the same thing for creating a City node.
Instructions:
-
Write a query to create a
Countrynode and connect it to its respectiveContinentnode by continent ID. -
Write a query to create a
Citynode and connect it to its respectiveCountrynode by country ID.
createCountrycontinent_id: IDname: Stringcurrency: Stringpopulation: I64gdp: F64
createCitycountry_id: IDname: Stringdescription: String
Answer
Answer
queries.hx
Creating Meta Relationships
In order to add meta relationships into our graph, we will connect nodes together with the edges that define the meta relationships. For this example, we will create aCountry_to_Capital edge from a Country node to a City node.
Instructions:
- Write a query to set a
Citynode as the capital city of aCountrynode using their IDs.
setCapitalcountry_id: IDcity_id: ID
Answer
Answer
queries.hx
Creating Vector Embeddings
Vector embeddings allow us to perform similarity-based searches on our data. For city descriptions, this means we can find cities with similar characteristics even if they don’t share exact properties. We will create a vector embedding for each city’s description. Instructions: Write a query to create aCityDescription vector and connect it to its respective City node by city ID.
Query Parameters:
embedDescriptioncity_id: IDvector: [F64]
Answer
Answer
queries.hx
Get Nodes by ID
Now that we know how to create nodes and their relationships, we need to be able to retrieve nodes from our graph. The simplest way is to retrieve nodes when we know their ID. Instructions:- Write 3 queries to get
Continent,Country, andCityby node ID.
getContinentcontinent_id: ID
getCountrycountry_id: ID
getCitycity_id: ID
Answer
Answer
queries.hx
Get All Nodes of Type
In addition to retrieving nodes by ID, we often want to retrieve all nodes of a certain type. Since we have a hierarchical structure, we will also want to get all countries within a continent and all cities within a country. Instructions:-
Write 3 queries to get all
Continent,Country, andCitynodes. -
Write 2 queries to get all
CountryandCitynodes by their parent IDs.
getAllContinentsgetAllCountriesgetAllCitiesgetCountriesInContinentcontinent_id: ID
getCitiesInCountrycountry_id: ID
Answer
Answer
queries.hx
Get Nodes by Meta Relationship
Similar to getting nodes by their hierarchical relationships, we can also get nodes via their meta relationships. For this example, we will retrieve the capital city of a country. We’ll do this by traversing theCountry_to_Capital edge from a Country node to find its capital City node.
Instructions:
- Write a query to get a country’s capital
Citynode by the country’s ID.
getCapitalcountry_id: ID
Answer
Answer
queries.hx
Get Node Properties
Sometimes we don’t need the full node, just a few specific properties. For example, we can display only the names and populations of countries without pulling in the entire node. In this case, we can use property selection syntax to retrieve just the fields we care about. This allows for more efficient querying and cleaner data handling when building visualizations or summaries. Instructions:- Write a query to get each country’s
nameandpopulation.
getCountryNames
Answer
Answer
queries.hx
Get Nodes by Property
In addition to retrieving nodes by their ID or relationship, we often need to find nodes based on their properties. This allows for more flexible querying of our graph database. We will write queries to retrieve nodes by specific properties they contain. Instructions:- Write 3 queries that get the
Continent,Country, andCitynodes by their names.
getContinentByNamecontinent_name: String
getCountryByNamecountry_name: String
getCityByNamecity_name: String
Answer
Answer
queries.hx
Get Nodes by Property Cont.
Building on property-based queries, you can also filter nodes using comparison operators. This allows you to find nodes that meet specific criteria rather than exact matches. You’ll practice with different comparison operators to filter countries by various attributes. Instructions:-
Write a query to get
Countrynodes by their currency. -
Write a query to get
Countrynodes with population less thanmax_population. -
Write a query to get
Countrynodes with GDP greater than or equal tomin_gdp.
getCountriesByCurrencycurrency: String
getCountriesByPopulationmax_population: I64
getCountriesByGdpmin_gdp: F64
Answer
Answer
queries.hx
Get Nodes by Many Properties
Now that we’ve seen how to get nodes by individual properties, we can also combine multiple conditions to perform more advanced filtering. For this example, we’ll write queries that retrieveCountry nodes based on a combination of property values. This includes filtering countries with a population greater than a minimum and a GDP less than or equal to a maximum, as well as retrieving countries that either use a specific currency or have a population below a certain threshold. These types of queries allow us to refine our searches and extract more targeted subsets of data from our graph.
Instructions:
-
Write a query to find
Countrynodes with both population greater thanmin_populationand GDP less than or equal tomax_gdp. -
Write a query to find
Countrynodes with either a specificcurrencyor a population less than or equal tomax_population.
getCountriesByPopGdpmin_population: I64max_gdp: F64
getCitiesByCurrPopcurrency: Stringmax_population: I64
Answer
Answer
queries.hx
Get Nodes by Meta Relationships
In addition to traversing structural relationships, we can also query nodes based on meta relationships. For example, we can retrieve allCountry nodes that have a capital city assigned. This involves checking for the existence of an outgoing Country_to_Capital edge from each Country node. Meta relationship queries like this are useful for identifying nodes with specific contextual connections beyond hierarchical structures.
Instructions:
Write a query to get Country nodes that have capital cities.
Query Parameters:
getCountriesWithCapitals
Answer
Answer
queries.hx
Get Range of Nodes
When working with large datasets, it’s often useful to limit the number of results returned from a query. TheRANGE operator allows you to implement pagination and control result set size efficiently. This is particularly important for performance when dealing with queries that might return many nodes. The RANGE operator takes two parameters: the starting index (0-based) and the number of items to return.
Instructions:
- Write a query to get the first
k(I64)Citynodes in a continent given the continent’s name.
getContinentCitiescontinent_name: Stringk: I64
Answer
Answer
queries.hx
Get Count of Nodes
In some cases, we want to gather basic statistics about our graph. For example, we can count the number of capital cities by checking how manyCity nodes have an incoming Country_to_Capital edge. Using the COUNT operation, we can quickly compute aggregate statistics like this to better understand the structure and distribution of data across our graph.
Instructions:
- Write a query to get the number of capital cities.
countCapitals
Answer
Answer
queries.hx
Get Nodes with Anonymous Traversals
Sometimes we want to filter nodes based other node’s properties. For example, we can get all countries that have more than a certain number of cities. To do this, we’ll count the number of outgoingCountry_to_City edges from each Country node and filter by num_cities. This pattern of anonymous traversal is useful when we care about the structure or degree of connectivity in the graph, rather than the specific linked nodes themselves.
Instructions:
- Write a query to get
Countrynodes that has more cities thannum_cities.
getCountryByCityCntnum_cities: I64
Answer
Answer
queries.hx
Semantic Search Vectors
Semantic search allows us to go beyond exact matches by comparing the meaning of data. For example, we can find cities with similar descriptions using vector embeddings. By searching againstCityDescription vectors, we can retrieve the top-k most semantically similar City nodes to a given input vector. This is especially useful when we want to find cities that share common characteristics or themes, even if their properties don’t match exactly.
For Helixir, we will be using fake embeddings to test the semantic search functionality. In real applications, you would use proper embeddings from providers like OpenAI, Gemini, etc.
- Write a query to semantically search a
vectoragainstCityDescriptionvectors and returning the topkCitynodes.
searchDescriptionsvector: [F64]k: I64
Answer
Answer
queries.hx
Updating Nodes
Updating nodes allows us to modify the properties of existing entities in our graph without needing to recreate them. To update a node, we use theUPDATE operation followed by the fields we want to modify. For example, we can update a country’s currency by its ID, or simultaneously update both its population and GDP. Keeping node data up-to-date ensures our graph remains accurate and relevant for queries, visualizations, and downstream analytics.
Instructions:
-
Write a query to update a country’s
currencyby a country’s ID. -
Write a query to update a country’s
populationandgdpby a country’s ID.
updateCurrencycountry_id: IDcurrency: String
updatePopGdpcountry_id: IDpopulation: I64gdp: F64
Answer
Answer
queries.hx
Deleting Nodes
Deleting nodes is useful when we want to clean up outdated or incorrect data from our graph. However, this can get tricky in a graph database because not only do we have to drop the node but also the relationships connected to that node. Additionally, the order in which we drop them is very important. For example, if a city is no longer relevant or a country needs to be removed entirely, we can drop the node and its relationship to the country as a city and also potentially as a capital city. In cases where the node is linked through specific edges, like a capital city connection, it’s important to remove those edges first to maintain the graph structure and allowing us to drop the other edges later. This ensures that dependent edges don’t linger in the system, avoiding potential inconsistencies during traversal or analytics. Instructions:-
Write a query to delete a
Citynode given its ID. -
Write a query to delete a capital
Citynode given its country’s ID. -
Write a query to delete a
Countrynode given its ID.
deleteCitycity_id: ID
deleteCapitalcountry_id: ID
deleteCountrycountry_id: ID
Answer
Answer
queries.hx
Updating Meta Relationships
Sometimes you need to update the meta relationships between nodes rather than creating new ones. For example, you might want to change which city serves as a country’s capital. This involves removing the existing capital relationship and creating a new one with a different city. When updating meta relationships, it’s important to properly manage the edge connections to maintain graph consistency. Instructions:- Write a query to update the capital
Citynode of aCoutrynode given the country’s ID and the new capital city’s ID.
updateCapitalcountry_id: IDcity_id: ID
Answer
Answer
queries.hx
Updating Embeddings
When working with vector embeddings, you often need to update both the node properties and their associated vector embeddings. For example, when a city’s description changes, you need to update the description property and also update the corresponding vector embedding to reflect the new semantic meaning. This ensures that semantic searches remain accurate and relevant. Instructions:- Write a query to update the
descriptionof aCitynode given its ID and also update theCityDescriptionvector embedding given a newvector.
updateDescriptioncity_id: IDdescription: Stringvector: [F64]
Answer
Answer
queries.hx
Congratulations!
You’ve completed the Helixir guide!
In this guide, we covered the basics of querying a graph database using Helixir. We learned how to create nodes, relationships, and properties. We also learned how to filter, update, and delete nodes and their relationships. This is just the beginning of what you can do with Helixir. There are many more features and capabilities that you can explore. If you have any questions or feedback, please feel free to reach out to us!Next Steps
Skip the Infrastructure HassleGoing from local testing to production? Helix Cloud makes it effortless. We handle servers, scaling, and maintenance so you can focus on building your application. Explore Use Cases
Ready to dive deeper? Check out our guides and tutorials for real-wold use cases and advanced scenarios. Learn the Language
Get to know HelixQL, our fast, efficient query language built for traversing and manipulating graph and vector data. Work with the SDK
Build, query, and embed entirely in your language of choice using our Python SDK, TypeScript SDK, Rust SDK, and Go SDK. Unlock More Features
Discover everything HelixDB has to offer with our cutting-edge features.