Relatude Help

30 Pitfalls and gotchas

Modelling

  1. Reference/References yield nothing from foreach unless preloaded. Use .Get(), .TryGet(out …) or .Preload(...). This makes accidental N+1 loads impossible.
  2. Never construct or assign a NodeMeta. Default it to NodeMeta.Empty and write meta with db.UpdateMeta (§4).
  3. Prefer interfaces. No boilerplate, nothing to initialise, and multiple inheritance.
  4. Put property attributes on the interface that declares the property. Attributes anywhere else are ignored.
  5. Two parent interfaces may not declare the same property name. Move a shared member into a common base interface.
  6. A subtype cannot redeclare a property. To change a default, a boost or text indexing per type, use [PropertyOverride] (§3.1).
  7. Nullable value types (int?, DateTime?) are not supported. Use the type's empty value, such as GeoCoordinate.Empty or FileValue.Empty.
  8. A class node type needs a parameterless constructor, and must initialise every reference-type property: string.Empty, FileValue.Empty, NodeMeta.Empty, [], new().
  9. Relations are not foreign keys. Do not add a VenueId property: declare the relation class and expose its ends.
  10. Relation lists are ordered, per side. AddRelation appends, and throws for a pair that is already related; SetRelation and ClearRelation do not throw. Reorder with MoveRelation… or SetRelationOrder, never by removing and adding again.
  11. 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.
  12. Embedded objects are owned by their parent. To share one, make it a node type and relate to it.
  13. [EmbeddedMapProperty(KeyProperty = …)] must name a property of the value type with the map's key type.
  14. You cannot read an embedded map by key before its parent is saved. Until then, only Add is safe.
  15. 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

  1. GeoCoordinate.Empty never matches IsWithin, and its distance to anything is infinite.
  2. OrderBy on a GeoCoordinate throws. Filter by radius, then OrderBy(v => v.Location.DistanceTo(center)).
  3. Coordinates snap to a grid of about 1 cm, so a stored value is not bit for bit the input.

Querying

  1. Use the query an operator returns: q = q.Where(...). Queries are immutable.
  2. Filter, sort and facet on Indexed = true properties. Anything else is checked node by node.
  3. Use builder methods, not LINQ on the result. Use Count() and SelectId() rather than loading nodes to count them or read their ids.
  4. The last OrderBy is the main sort key. Earlier ones only break ties.
  5. Query<T>() includes subtypes. Narrow it with WhereTypes when you want one type only.
  6. Name the node type when you go through a relation end: Traverse<IVenue>(v => v.Halls, …) and Include<IEvent>(v => v.Events).ThenInclude(…).
  7. Include filters never shrink the main result. Parents with no matching children still come back.
  8. Traverse and ShortestPath work only over relations. If you need traversal, model the link as a relation.
  9. Get and TryGet ignore read access. To check what a user may read, use a query.