30 Pitfalls and gotchas
Modelling
Reference/Referencesyield nothing fromforeachunless preloaded. Use.Get(),.TryGet(out …)or.Preload(...). This makes accidental N+1 loads impossible.- Never construct or assign a
NodeMeta. Default it toNodeMeta.Emptyand write meta withdb.UpdateMeta(§4). - Prefer interfaces. No boilerplate, nothing to initialise, and multiple inheritance.
- Put property attributes on the interface that declares the property. Attributes anywhere else are ignored.
- Two parent interfaces may not declare the same property name. Move a shared member into a common base interface.
- A subtype cannot redeclare a property. To change a default, a boost or text indexing per
type, use
[PropertyOverride](§3.1). - Nullable value types (
int?,DateTime?) are not supported. Use the type's empty value, such asGeoCoordinate.EmptyorFileValue.Empty. - A class node type needs a parameterless constructor, and must initialise every reference-type
property:
string.Empty,FileValue.Empty,NodeMeta.Empty,[],new(). - Relations are not foreign keys. Do not add a
VenueIdproperty: declare the relation class and expose its ends. - Relation lists are ordered, per side.
AddRelationappends, and throws for a pair that is already related;SetRelationandClearRelationdo not throw. Reorder withMoveRelation…orSetRelationOrder, never by removing and adding again. - Order is no reason to choose
References<T>over a relation. Relations keep their order too. References differ in allowing duplicates and having no reverse lookup. - Embedded objects are owned by their parent. To share one, make it a node type and relate to it.
[EmbeddedMapProperty(KeyProperty = …)]must name a property of the value type with the map's key type.- You cannot read an embedded map by key before its parent is saved. Until then, only
Addis safe. - Pin
[Node(Id = …)]and[Relation(Id = …)]early. Without them the id is a hash of the full type name, so a rename creates a new, empty type.
Geo
GeoCoordinate.Emptynever matchesIsWithin, and its distance to anything is infinite.OrderByon aGeoCoordinatethrows. Filter by radius, thenOrderBy(v => v.Location.DistanceTo(center)).- Coordinates snap to a grid of about 1 cm, so a stored value is not bit for bit the input.
Querying
- Use the query an operator returns:
q = q.Where(...). Queries are immutable. - Filter, sort and facet on
Indexed = trueproperties. Anything else is checked node by node. - Use builder methods, not LINQ on the result. Use
Count()andSelectId()rather than loading nodes to count them or read their ids. - The last
OrderByis the main sort key. Earlier ones only break ties. Query<T>()includes subtypes. Narrow it withWhereTypeswhen you want one type only.- Name the node type when you go through a relation end:
Traverse<IVenue>(v => v.Halls, …)andInclude<IEvent>(v => v.Events).ThenInclude(…). - Include filters never shrink the main result. Parents with no matching children still come back.
TraverseandShortestPathwork only over relations. If you need traversal, model the link as a relation.GetandTryGetignore read access. To check what a user may read, use a query.