Enhancements in Graph Model: Dynamic Entities & Full-Text Search

As I continued work on BrainExpanded and its MCP service, I came to realize that I needed support for dynamic entities and full text search in the Graph Model API. I just published a new release of the Cvoya Graph Model packages with many changes. Here’s a summary:

  • Dynamic Entities. It’s no longer required to use strong types to interact with the graph model. The DynamicNode and DynamicRelationship types provide the necessary support.
  • Full text search. It’s not possible to search the graph using full text search. The PropertyAttribute has been updated to support options related to full text search (e.g. IncludeInFullTextSearch) or to schema-related requirements (e.g. IsRequired, IsUnique).
  • One package dependency. You can now get started easily by just adding the Cvoya.Graph.Model.Neo4j package to your project. The necessary package dependencies will be included and the code generator will be automatically activated at build time of your project. The Cvoya.Graph.Model.Analyzers package is still optional but recommended.
  • All the examples now work. The code for some of the example projects on github was commented out for the last release. They all build and run to completion now. There is an example project for full-text search as well.

Example – Dynamic Entities

You can now use DynamicNode and DynamicRelationship for nodes and relationships:

// Create a node
var node = new DynamicNode(
   labels: new[] { "Person" },
   properties: new Dictionary<string, object?>
   {
      ["name"] = "John Doe",
      ["age"] = 40,
      ["email"] = "john@example.com",
      ["active"] = true
   }
);

await Graph.CreateNodeAsync(node);

It’s similar for relationships.

You can configure the properties using the introduced PropertyConfiguration-related API. Refer to the source code on github for more details. When I get some time, I will write an example and update the documentation.

Of course, it is possible to interact with the created node using a strong type:

public record Person : Node
{
  [Property(Label = "name")]
  public string FullName { get; set; }

  [Property(Label = "age")]
  public int Age { get; set; }

  [Property(Label = "email")]
  public string Email { get; set; }

  [Property(Label = "active")]
  public bool IsActive { get; set; }
}

var person = await graph.Nodes<Person>()
   .Where(p => p.Id = "...")
   .FirstOrDefaultAsync();

Example – Full Text Search

Every entity (node or relationship) with a string property is automatically added to the text index unless otherwise configured. There is a text index per property and a global one. You can search the global index using Search(), node/relationship indexes using SearchNodes() and SearchRelationships, or type-specific indexes using SearchNodes<Person>() and SearchRelationships<FriendsWith>(). You can then compose the search operators with other LINQ queries:

var johnsOver30 = await graph
   .SearchNode<Person>("John")
   .Where(p => p.Age > 30)
   .ToListAsync()

Example – Get started

And now, it’s much easier to get started:

> dotnet new console
> dotnet add package Cvoya.Graph.Model.Neo4j -v 1.0.0-alpha.20250716.4

# optionally but recommended
> dotnet add package Cvoya.Graph.Model.Analyzers -v 1.0.0-alpha.20250716.4

Happy coding!