Simple to advanced sample applications using VelocityDB and VelocityGraph

Expand/Contract each of the following samples by clicking on the + or - signs. All of these samples are also available in a sample solution included in the download. Open the sample solution by using the shortcut in the VelocityDB start menu or by going to your Documents\VelocityDB folder and opening VelocityDb.sln.

VelocityDB Sample Applications

Person.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VelocityDb;

namespace VelocityDbSchema.Samples.Sample1
{
  public class Person : OptimizedPersistable
  {
    string firstName;
    string lastName;
    UInt16 age;

    public Person(string firstName, string lastName, UInt16 age)
    {
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
    }
  }
}

Program.cs

  class Program
  {
    static readonly string systemDir = 
      System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "VelocityDB", "Databases", "Sample1");

    static void Main(string[] args)
    {
      SessionNoServer session = new SessionNoServer(systemDir);
      session.BeginUpdate();
      Person person = new Person("Robin", "Hood", 30);
      session.Persist(person);
      person = new Person("Bill", "Gates", 56);
      session.Persist(person);
      person = new Person("Steve", "Jobs", 56);
      session.Persist(person);
      session.Commit();
    }
  }

Looking at the Person objects through the VelocityDbBrowser

Person.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VelocityDb;

namespace VelocityDbSchema.Samples.Sample2
{
  public class Person : OptimizedPersistable
  {
    string firstName;
    string lastName;
    UInt16 age;
    Person bestFriend;

    public Person(string firstName, string lastName, UInt16 age, Person bestFriend = null)
    {
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
      this.bestFriend = bestFriend;
    }

    public Person BestFriend
    {
      get
      {
        return bestFriend;
      }
      set
      {
        Update();
        bestFriend = value;
      }
    }
  }
}

Program.cs

  class Program
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "Sample2");

    static void Main(string[] args)
    {
      SessionNoServer session = new SessionNoServer(systemDir);
      session.BeginUpdate();
      Person robinHood = new Person("Robin", "Hood", 30);
      Person billGates = new Person("Bill", "Gates", 56, robinHood);
      Person steveJobs = new Person("Steve", "Jobs", 56, billGates);
      robinHood.BestFriend = billGates;
      session.Persist(steveJobs); // the other persons will be persisted implicetly by reachability from "Steve Jobs" person object
      session.Commit();
    }
  }

Looking at the Sample 2 Person objects through the VelocityDbBrowser

When using VelocityDbList instreade of List, the list is assigned an Oid instead of being directly embedded in the parent object. If a list is to be shared between multiple objects then VelocityDbList should be used.

Person.cs

 public class Person : OptimizedPersistable
  {
    string firstName;
    string lastName;
    UInt16 age;
    Person bestFriend;
    VelocityDbList<Person> friends;

    public Person(string firstName, string lastName, UInt16 age, Person bestFriend = null)
    {
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
      this.bestFriend = bestFriend;
      friends = new VelocityDbList<Person>();
    }

    public Person BestFriend
    {
      get
      {
        return bestFriend;
      }
      set
      {
        Update();
        bestFriend = value;
      }
    }

    public string FirstName
    {
      get
      {
        return firstName;
      }
      set
      {
        Update();
        firstName = value;
      }
    }

    public VelocityDbList<Person> Friends
    {
      get
      {
        return friends;
      }
    }
  }

Looking at the objects through the VelocityDbBrowser

Main program of Sample4. It also shows how to unpersist (delete from database) objects.

  class Program
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "Sample4");

    static void Main(string[] args)
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginUpdate();
          Database db = session.OpenDatabase(session.DatabaseNumberOf(typeof(Person)), true, false);
          if (db != null)
          { // delete (unpersist) all Person objects created in prior run
            foreach (Person p in db.AllObjects<Person>())
              p.Unpersist(session);
            // delete (unpersist) all VelocityDbList<Person> objects created in prior run
            foreach (VelocityDbList<Person> l in db.AllObjects<VelocityDbList<Person>>())
              l.Unpersist(session);
          }
          else
          {
            Person robinHood = new Person("Robin", "Hood", 30);
            Person billGates = new Person("Bill", "Gates", 56, robinHood);
            Person steveJobs = new Person("Steve", "Jobs", 56, billGates);
            robinHood.BestFriend = billGates;
            session.Persist(steveJobs);
            steveJobs.Friends.Add(billGates);
            steveJobs.Friends.Add(robinHood);
            billGates.Friends.Add(billGates);
            robinHood.Friends.Add(steveJobs);
          }
          session.Commit();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }
  }
  class OneDbPerClass
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "OneDbPerClass");

    public void AddRaceCar()
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginUpdate();
          RaceCar raceCar = new RaceCar();
          raceCar.Speed = 1976.7;
          session.Persist(raceCar);
          session.Commit();
        }      
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }    
    
    public void AddSomeBicycles()
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginUpdate();
          for (int i = 0; i < 1000000; i++)
          {
            Bicycle bicycle = new Bicycle();
            bicycle.Color = "red";
            session.Persist(bicycle);
          }
          for (int i = 0; i < 10; i++)
          {
            Bicycle bicycle = new Bicycle();
            bicycle.Color = "blue";
            session.Persist(bicycle);
          }
          for (int i = 0; i < 10; i++)
          {
            Bicycle bicycle = new Bicycle();
            bicycle.Color = "yellow";
            session.Persist(bicycle);
          }
          session.Commit();
        }      
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }    
    
    public void QuerySomeBicycles()
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginRead();
          Database db = session.OpenDatabase(session.DatabaseNumberOf(typeof(Bicycle)));
          var src = from Bicycle bike in db.AllObjects<Bicycle>() where bike.Color == "blue" select bike;
          foreach (Bicycle bike in src)
            Console.WriteLine(bike.ToStringDetails(session));
          session.Commit();
        }      
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }

    static void Main(string[] args)
    {
      OneDbPerClass oneDbPerClass = new OneDbPerClass();
      oneDbPerClass.AddRaceCar();
      oneDbPerClass.AddSomeBicycles();
      oneDbPerClass.QuerySomeBicycles();
    }
  }

The following code shows how high availabilty is achieved using VelocityDB. Test first connects to one server with a requested backup (like replica) to another server. Then we delete all the database files from the original server and quickly make a restore to another server and continue operating with no data loss.

// This application shows how to accomplish High Availability with VelocityDB. 
// It can be done with in memory data or persisted data replicated to a backup server that can take over as a master server on demand.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using VelocityDb;
using VelocityDb.Session;
using VelocityDbSchema;

namespace BackupRestore
{
  class HighAvailability
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "HighAvailability");
    static string systemHost = Dns.GetHostName();
    static string backupHost = "FindPriceBuy"; // modify to second server name that you are using (make sure VelocityDB is installed on that server first)
    static readonly string backupDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases" + Path.DirectorySeparatorChar + "HighAvailabilityBackup");
    static bool inMemoryOnly = true;
    public void CreateDataWithBackupServer()
    {
      int loops = 30000;
      int j;
      using (ServerClientSession session = new ServerClientSession(systemDir, systemHost, 1000, true, inMemoryOnly))
      {
        Man aMan = null;
        Woman aWoman = null;
        const bool isBackupLocation = true;
        session.BeginUpdate();
        // we need to have backup locations special since server is not supposed to do encryption or compression
        DatabaseLocation backupLocation = new DatabaseLocation(backupHost, backupDir, (uint)Math.Pow(2, 24), UInt32.MaxValue, session,
          false, PageInfo.encryptionKind.noEncryption, isBackupLocation, session.DatabaseLocations.Default());
        session.NewLocation(backupLocation);
        for (j = 1; j <= loops; j++)
        {
          aMan = new Man(null, aMan, j, DateTime.Now);
          session.Persist(aMan);
          aWoman = new Woman(aMan, aWoman, j);
          session.Persist(aWoman);
          aMan.spouse = new VelocityDb.WeakIOptimizedPersistableReference<VelocityDbSchema.Person>(aWoman);
          if (j % 1000000 == 0)
            Console.WriteLine("Loop # " + j);
        }
        UInt64 id = aWoman.Id;
        Console.WriteLine("Commit, done Loop # " + j);
        session.Commit();
      }
    }

    public void CreateMoreDataWithBackupServer()
    {
      int loops = 1000;
      int j;
      using (ServerClientSession session = new ServerClientSession(systemDir, systemHost, 1000, true, inMemoryOnly))
      {
        Man aMan = null;
        Woman aWoman = null;
        session.BeginUpdate();
        for (j = 1; j <= loops; j++)
        {
          aMan = new Man(null, aMan, j, DateTime.Now);
          session.Persist(aMan);
          aWoman = new Woman(aMan, aWoman, j);
          session.Persist(aWoman);
          aMan.spouse = new VelocityDb.WeakIOptimizedPersistableReference<VelocityDbSchema.Person>(aWoman);
          if (j % 1000000 == 0)
            Console.WriteLine("Loop # " + j);
        }
        UInt64 id = aWoman.Id;
        Console.WriteLine("Commit, done Loop # " + j);
        session.Commit();
      }
    }

    public void ReadSomeData()
    {
      int ct = 0;
      using (ServerClientSession session = new ServerClientSession(systemDir, systemHost, 1000, true, inMemoryOnly))
      {
        session.BeginRead();
        foreach (Man man in session.AllObjects<Man>())
        {
          ct++;
        }
        Console.WriteLine("Commit, number of Men found: " + ct);
        session.Commit();
      }
    }

    public void RestoreToBackupServer()
    {
      using (ServerClientSession session = new ServerClientSession(systemDir, systemHost))
      {
        session.ClearServerCache(); // normally don't use this function but use it here to simulate a server going down and restarting
      }

      using (ServerClientSession session = new ServerClientSession(systemDir, backupHost, 1000, true, inMemoryOnly))
      {
        session.BeginUpdate();
        DatabaseLocation backupLocation = new DatabaseLocation(backupHost, backupDir, (uint)Math.Pow(2, 24), UInt32.MaxValue, session,
    false, PageInfo.encryptionKind.noEncryption, true, session.DatabaseLocations.Default());
        session.RestoreFrom(backupLocation, DateTime.Now);
        session.Commit(false, true);
      }
    }

    static void Main(string[] args)
    {
      if (args.Length > 0) // pass any argument to to command line to force use of persisted data.
        inMemoryOnly = false;
      HighAvailability ha = new HighAvailability();
      ha.CreateDataWithBackupServer();
      ha.ReadSomeData();
      ha.CreateMoreDataWithBackupServer();
      ha.ReadSomeData();
      if (Directory.Exists(systemDir))
        Directory.Delete(systemDir, true); // remove our current systemDir and all its databases.
      ha.RestoreToBackupServer();
      string t = systemHost; // swap backupHost and systemHost
      systemHost = backupHost;
      backupHost = t;
      ha.ReadSomeData();
    }
  }
}

Here are some classes with defined indexes

   1:    [UniqueConstraint]
   2:    [Index("registrationState,registrationPlate")]
   3:    public class Car : Vehicle
   4:    {
   5:      string registrationState;
   6:      string registrationPlate;
   7:      [Index]
   8:      InsuranceCompany insuranceCompany;
   9:      string insurancePolicy;
  10:   
  11:      public Car(string color, int maxPassengers, int fuelCapacity, double litresPer100Kilometers, DateTime modelYear,
  12:        string brandName, string modelName, int maxSpeed, int odometer, string registrationState, string registrationPlate,
  13:        InsuranceCompany insuranceCompany, string insurancePolicy)
  14:        : base(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed, odometer)
  15:      {
  16:        this.registrationState = registrationState;
  17:        this.registrationPlate = registrationPlate;
  18:        this.insuranceCompany = insuranceCompany;
  19:        this.insurancePolicy = insurancePolicy;
  20:      }
  21:    }
  22:   
  23:    [Index("stateOrCountry,licenseNumber")]
  24:    public class DriversLicense : OptimizedPersistable
  25:    {
  26:      string stateOrCountry;
  27:      string licenseNumber;
  28:      DateTime dateIssued;
  29:   
  30:      [Index]
  31:      DateTime validUntil;
  32:   
  33:      public DriversLicense(string stateOrCountry, string licenseNumber, DateTime validUntil)
  34:      {
  35:        this.stateOrCountry = stateOrCountry;
  36:        this.licenseNumber = licenseNumber;
  37:        this.dateIssued = DateTime.Now;
  38:        this.validUntil = validUntil;
  39:      }
  40:   
  41:      [FieldAccessor("validUntil")]
  42:      public DateTime ValidUntil
  43:      {
  44:        get
  45:        {
  46:          return validUntil;
  47:        }
  48:      }
  49:    }
  50:   
  51:    public class InsuranceCompany : OptimizedPersistable
  52:    {
  53:      [Index]
  54:      [UniqueConstraint]
  55:      [OnePerDatabase]
  56:      string name;
  57:      string phoneNumber;
  58:   
  59:      public InsuranceCompany(string name, string phoneNumber)
  60:      {
  61:        this.name = name;
  62:        this.phoneNumber = phoneNumber;
  63:      }
  64:   
  65:      [FieldAccessor("name")]
  66:      public string Name
  67:      {
  68:        get
  69:        {
  70:          return name;
  71:        }
  72:      }
  73:    }
  74:   
  75:    [Index]
  76:    public class Person : OptimizedPersistable
  77:    {
  78:      string name;
  79:      DriversLicense license;
  80:   
  81:      public Person(string name, DriversLicense license)
  82:      {
  83:        this.name = name;
  84:        this.license = license;
  85:      }
  86:    }
  87:   
  88:    public class Truck : Vehicle
  89:    {
  90:      string registrationState;
  91:      string registrationPlate;
  92:      InsuranceCompany insuranceCompany;
  93:      string insurancePolicy;
  94:      int cargoCapacity; // in Kg
  95:   
  96:      public Truck(string color, int maxPassengers, int fuelCapacity, double litresPer100Kilometers, DateTime modelYear,
  97:        string brandName, string modelName, int maxSpeed, int odometer, string registrationState, string registrationPlate,
  98:        InsuranceCompany insuranceCompany, string insurancePolicy, int cargoCapacity)
  99:        : base(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed, odometer)
 100:      {
 101:        this.registrationState = registrationState;
 102:        this.registrationPlate = registrationPlate;
 103:        this.insuranceCompany = insuranceCompany;
 104:        this.insurancePolicy = insurancePolicy;
 105:        this.cargoCapacity = cargoCapacity;
 106:      }
 107:    }
 108:    [Index("modelYear,brandName,modelName,color")]
 109:    public abstract class Vehicle : OptimizedPersistable
 110:    {
 111:      [Index]
 112:      string color;
 113:      int maxPassengers;
 114:      int fuelCapacity; // fuel capacity in liters   
 115:      [Index]
 116:      double litresPer100Kilometers; // fuel cunsumption 
 117:      DateTime modelYear;
 118:      [Index]
 119:      [IndexStringByHashCode]
 120:      string brandName;
 121:      string modelName;
 122:      int maxSpeed; // km/h
 123:      int odometer; // km
 124:   
 125:      protected Vehicle(string color, int maxPassengers, int fuelCapacity, double litresPer100Kilometers, DateTime modelYear, string brandName, string modelName, int maxSpeed, int odometer)
 126:      {
 127:        this.color = color;
 128:        this.maxPassengers = maxPassengers;
 129:        this.fuelCapacity = fuelCapacity;
 130:        this.litresPer100Kilometers = litresPer100Kilometers;
 131:        this.modelYear = modelYear;
 132:        this.brandName = brandName;
 133:        this.modelName = modelName;
 134:        this.maxSpeed = maxSpeed;
 135:        this.odometer = odometer;
 136:      }
 137:   
 138:      [FieldAccessor("color")]
 139:      public string Color
 140:      {
 141:        get
 142:        {
 143:          return color;
 144:        }
 145:        set
 146:        {
 147:          Update();
 148:          color = value;
 149:        }
 150:      }
 151:   
 152:      [FieldAccessor("litresPer100Kilometers")]
 153:      public double LitresPer100Kilometers
 154:      {
 155:        get
 156:        {
 157:          return litresPer100Kilometers;
 158:        }
 159:      }
 160:    }

Using these indexes

   1:    class Indexes
   2:    {
   3:      static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
   4:        "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "Indexes");
   5:   
   6:      static void Main(string[] args)
   7:      {
   8:        try
   9:        {
  10:          string brandName = "Toyota";
  11:          string color = "Blue";
  12:          int maxPassengers = 5;
  13:          int fuelCapacity = 40;
  14:          double litresPer100Kilometers = 5;
  15:          DateTime modelYear = new DateTime(2003, 1, 1);
  16:          string modelName = "Highlander";
  17:          int maxSpeed = 200;
  18:          int odometer = 100000;
  19:          string registrationState = "Texas";
  20:          string registrationPlate = "TX343434";
  21:          string insurancePolicy = "CAA7878787";
  22:          DriversLicense license = new DriversLicense("California", "B7788888", DateTime.Now + new TimeSpan(1825, 0, 0, 0));
  23:          Person person = new Person("Mats Persson", license);
  24:          InsuranceCompany insuranceCompany = new InsuranceCompany("Allstate", "858727878");
  25:          Car car = new Car(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed,
  26:            odometer, registrationState, registrationPlate, insuranceCompany, insurancePolicy);
  27:          using (SessionNoServer session = new SessionNoServer(systemDir))
  28:          { // cleanup data from a possible prior run
  29:            session.BeginUpdate();
  30:            foreach (Database db in session.OpenAllDatabases(true))
  31:              if (db.DatabaseNumber >= 10 || db.DatabaseNumber == SessionBase.IndexDescriptorDatabaseNumber)
  32:                session.DeleteDatabase(db);
  33:            session.Commit();
  34:          }
  35:          using (SessionNoServer session = new SessionNoServer(systemDir))
  36:          {
  37:            session.BeginUpdate();
  38:            session.Persist(car);
  39:            registrationState = "Maine";
  40:            car = new Car(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed,
  41:            odometer, registrationState, registrationPlate, insuranceCompany, insurancePolicy);
  42:            session.Persist(car);
  43:            color = "Red";
  44:            maxPassengers = 5;
  45:            fuelCapacity = 50;
  46:            litresPer100Kilometers = 8;
  47:            modelYear = new DateTime(2006, 1, 1);
  48:            brandName = "Toyota";
  49:            modelName = "Tacoma";
  50:            maxSpeed = 210;
  51:            odometer = 50000;
  52:            registrationState = "Texas";
  53:            registrationPlate = "TX343433";
  54:            insurancePolicy = "CAA7878777";
  55:            car = new Car(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed,
  56:              odometer, registrationState, registrationPlate, insuranceCompany, insurancePolicy);
  57:            session.Persist(car);
  58:            color = "Black";
  59:            maxPassengers = 5;
  60:            fuelCapacity = 60;
  61:            litresPer100Kilometers = 3;
  62:            modelYear = new DateTime(2001, 1, 1);
  63:            brandName = "Lincoln";
  64:            modelName = "Town Car";
  65:            maxSpeed = 220;
  66:            odometer = 250000;
  67:            registrationState = "Texas";
  68:            registrationPlate = "TX543433";
  69:            insurancePolicy = "CAA7878775";
  70:            car = new Car(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, maxSpeed,
  71:              odometer, registrationState, registrationPlate, insuranceCompany, insurancePolicy);
  72:            session.Persist(car);
  73:            session.Commit();
  74:          }
  75:          using (SessionNoServer session = new SessionNoServer(systemDir))
  76:          {
  77:            session.BeginRead();
  78:            Console.WriteLine("Blue Cars");
  79:            BTreeSet<Car> bTree = session.Index<Car>("color");
  80:            foreach (Car c in (from aCar in bTree where aCar.Color == "Blue" select aCar))
  81:              Console.WriteLine(c.ToStringDetails(session));
  82:            Console.WriteLine("Cars in fuel efficierncy order");
  83:            foreach (Car c in session.Index<Car>("litresPer100Kilometers"))
  84:              Console.WriteLine(c.ToStringDetails(session));
  85:            Console.WriteLine("Vehicles ordered modelYear, brandName, modelName, color");
  86:            foreach (Vehicle v in session.Index<Vehicle>())
  87:              Console.WriteLine(v.ToStringDetails(session));
  88:            session.Commit();
  89:          }
  90:          using (SessionNoServer session = new SessionNoServer(systemDir))
  91:          {
  92:            session.BeginUpdate();
  93:            // these LINQ statements will trigger a binary search lookup (not a linear serach) of the matching Car objects in the BTreeSet
  94:            Car c = (from aCar in session.Index<Car>("color") where aCar.Color == "Blue" select aCar).First();
  95:            c.Color = "Green";
  96:            session.Commit();
  97:          }
  98:          using (SessionNoServer session = new SessionNoServer(systemDir))
  99:          {
 100:            session.BeginUpdate();
 101:            // these LINQ statements will trigger a binary search lookup (not a linear serach) of the matching Car objects in the BTreeSet
 102:            Car c = (from aCar in session.Index<Car>("color") where aCar.Color == "Green" select aCar).First();
 103:            UInt64 id = c.Id;
 104:            session.DeleteObject(id);
 105:            session.Abort();
 106:            session.BeginUpdate();
 107:            session.DeleteObject(id);
 108:            session.Commit();
 109:          }
 110:          using (SessionNoServer session = new SessionNoServer(systemDir))
 111:          {
 112:            Stopwatch sw = new Stopwatch();
 113:            sw.Start();
 114:            session.BeginRead();
 115:            // these LINQ statements will trigger a binary search lookup (not a linear serach) of the matching Car objects in the BTreeSet
 116:            Console.WriteLine("Blue Cars");
 117:            foreach (Car c in (from aCar in session.Index<Car>("color") where aCar.Color == "Blue" select aCar))
 118:              Console.WriteLine(c.ToStringDetails(session));
 119:            session.Commit();
 120:            sw.Stop();
 121:            Console.WriteLine(sw.Elapsed);
 122:          }
 123:          using (SessionNoServer session = new SessionNoServer(systemDir))
 124:          {
 125:            Stopwatch sw = new Stopwatch();
 126:            sw.Start();
 127:            session.BeginUpdate();
 128:            for (int i = 0; i < 10000; i++)
 129:            { // add some junk to make search harder
 130:              car = new Car(color, maxPassengers, fuelCapacity, litresPer100Kilometers, modelYear, brandName, modelName, i,
 131:              odometer, registrationState, registrationPlate + i, insuranceCompany, insurancePolicy);
 132:              session.Persist(car);
 133:            }
 134:            session.Commit();
 135:            sw.Stop();
 136:            Console.WriteLine(sw.Elapsed);
 137:          }
 138:          using (SessionNoServer session = new SessionNoServer(systemDir))
 139:          {
 140:            Stopwatch sw = new Stopwatch();
 141:            sw.Start();
 142:            session.BeginRead();
 143:            // these LINQ statements will trigger a binary search lookup (not a linear serach) of the matching Car objects in the BTreeSet
 144:            Console.WriteLine("Blue Cars");
 145:            foreach (Car c in (from aCar in session.Index<Car>("color") where aCar.Color == "Blue" select aCar))
 146:              Console.WriteLine(c.ToStringDetails(session));
 147:            session.Commit();
 148:            sw.Stop();
 149:            Console.WriteLine(sw.Elapsed);
 150:          }
 151:          using (SessionNoServer session = new SessionNoServer(systemDir))
 152:          {
 153:            Stopwatch sw = new Stopwatch();
 154:            sw.Start();
 155:            session.BeginUpdate();
 156:            Car c = (from aCar in session.Index<Car>("color") where aCar.Color == "Blue" select aCar).First();
 157:            c.Unpersist(session);
 158:            session.Commit();
 159:            sw.Stop();
 160:            Console.WriteLine(sw.Elapsed);
 161:          }
 162:          using (SessionNoServer session = new SessionNoServer(systemDir))
 163:          {
 164:            Stopwatch sw = new Stopwatch();
 165:            sw.Start();
 166:            session.BeginRead();
 167:            foreach (Car c in session.Index<Car>())
 168:              Console.WriteLine(c.ToStringDetails(session));
 169:            Console.WriteLine("Blue Cars");
 170:            foreach (Car c in (from aCar in session.Index<Car>() where aCar.Color == "Blue" select aCar))
 171:              Console.WriteLine(c.ToStringDetails(session));
 172:            session.Commit();
 173:            sw.Stop();
 174:            Console.WriteLine(sw.Elapsed);
 175:          }
 176:          using (SessionNoServer session = new SessionNoServer(systemDir))
 177:          {
 178:            Stopwatch sw = new Stopwatch();
 179:            sw.Start();
 180:            session.BeginUpdate();
 181:            InsuranceCompany prior = insuranceCompany;
 182:            try
 183:            {
 184:              for (int i = 0; i < 100000; i++)
 185:              {
 186:                insuranceCompany = new InsuranceCompany("AAA", "858787878");
 187:                session.Persist(insuranceCompany);
 188:              }
 189:              Debug.Assert(false); // should not get here
 190:            }
 191:            catch (UniqueConstraintException)
 192:            {
 193:            }
 194:            session.Commit();
 195:            sw.Stop();
 196:            Console.WriteLine(sw.Elapsed);
 197:          }
 198:          using (SessionNoServer session = new SessionNoServer(systemDir))
 199:          {
 200:            Stopwatch sw = new Stopwatch();
 201:            sw.Start();
 202:            session.BeginRead();
 203:            Database db = session.OpenDatabase(session.DatabaseNumberOf(typeof(InsuranceCompany)));
 204:            var q = from company in session.Index<InsuranceCompany>("name", db) where company.Name == "AAA" select company;
 205:            foreach (InsuranceCompany company in q)
 206:              Console.WriteLine(company.ToStringDetails(session)); // only one will match
 207:            session.Commit();
 208:            sw.Stop();
 209:            Console.WriteLine(sw.Elapsed);
 210:          }
 211:        }
 212:        catch (Exception ex)
 213:        {
 214:          Console.WriteLine(ex.ToString());
 215:        }
 216:      }
 217:    }
   1:  // This sample console application demonstrates the usage of the event subscription ServerClientSession api. Get notified when other sessions changes objects your session is intrested in.
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.IO;
   5:  using System.Linq;
   6:  using System.Text;
   7:  using System.Threading;
   8:  using System.Threading.Tasks;
   9:  using VelocityDb;
  10:  using VelocityDb.Session;
  11:  using VelocityDbSchema;
  12:   
  13:  namespace EventSubscriber
  14:  {
  15:    class EventSubscriber
  16:    {
  17:      static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
  18:        "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "EventSubscriber"); 
  19:   
  20:      static void Main(string[] args)
  21:      {
  22:        using (ServerClientSession session = new ServerClientSession(systemDir))
  23:        {
  24:          session.BeginUpdate();
  25:          session.SubscribeToChanges(typeof(Person));
  26:          session.SubscribeToChanges(typeof(Woman), "OlderThan50");
  27:          Person robinHood = new Person("Robin", "Hood", 30, 1234, null, null);
  28:          session.Persist(robinHood);
  29:          Person billGates = new Person("Bill", "Gates", 56, 234, robinHood, null);
  30:          session.Persist(billGates);
  31:          Person steveJobs = new Person("Steve", "Jobs", 56, 456, billGates, null);
  32:          session.Persist(steveJobs);
  33:          session.Commit();
  34:          Thread t = new Thread(UpdaterThread);
  35:          t.Start();
  36:          Thread.Sleep(600);
  37:          
  38:          for (int i = 0; i < 50; i++)
  39:          {
  40:            List<Oid> changes = session.BeginReadWithEvents();
  41:            if (changes.Count == 0)
  42:            {
  43:              Console.WriteLine("No changes events at: " + DateTime.Now.ToString("HH:mm:ss:ms"));
  44:              Thread.Sleep(250);
  45:            }
  46:            foreach (Oid id in changes)
  47:            {
  48:              object obj = session.Open(id);
  49:              Console.WriteLine("Received change event for: " + obj + " at: " + DateTime.Now.ToString("HH:mm:ss:ms"));;
  50:              //session.UnsubscribeToChanges(typeof(Person));
  51:            }
  52:            Console.WriteLine();
  53:            session.Commit();
  54:          }       
  55:          t.Join();
  56:        }
  57:      }
  58:   
  59:      static void UpdaterThread()
  60:      {
  61:        using (ServerClientSession session = new ServerClientSession(systemDir))
  62:        {
  63:          session.BeginUpdate();
  64:          Person Mats = new Person("Mats", "Persson", 22, 1234, null, null);
  65:          session.Persist(Mats);
  66:          Woman Kinga = new Woman("Kinga", "Persson", 56, 234, null, Mats);
  67:          foreach (Person p in session.AllObjects<Person>())
  68:          {
  69:            p.Age = (ushort) (p.Age + 1); 
  70:          }
  71:          session.Persist(Kinga);
  72:          session.Commit();
  73:  // 5 events
  74:          Thread.Sleep(5000);
  75:   
  76:          session.BeginUpdate();
  77:          Woman Lidia = new Woman("Lidia", "Persson", 22, 1234, null, null);
  78:          session.Persist(Lidia);
  79:          session.Commit();
  80:  // 0 events 
  81:          Thread.Sleep(500);
  82:   
  83:          session.BeginUpdate();
  84:          Woman Margareta = new Woman("Margareta", "Persson", 98, 1234, null, null);
  85:          session.Persist(Margareta);
  86:          session.Commit();
  87:  // 1 event
  88:          Thread.Sleep(500);
  89:   
  90:          session.BeginUpdate();
  91:          Woman Oliwia = new Woman("Oliwia", "Persson", 50, 1234, null, null);
  92:          session.Persist(Oliwia);
  93:          session.Commit();
  94:  // 0 events
  95:          Thread.Sleep(500);
  96:   
  97:          session.BeginUpdate();
  98:          foreach (Woman p in session.AllObjects<Woman>())
  99:          {
 100:            p.Age = (ushort) (p.Age + 1); 
 101:          }
 102:          session.Commit();
 103:  // 3 events
 104:          session.BeginUpdate();
 105:          foreach (Woman p in session.AllObjects<Woman>())
 106:          {
 107:            p.Age = (ushort)(p.Age + 1);
 108:          }
 109:          session.Commit();
 110:  // 3 events
 111:        }
 112:      }
 113:    }
 114:  }
  public class Person : OptimizedPersistable
  {
    static Random randGen = new Random(5);

    string firstName;
    string lastName;
    WeakReference<VelocityDbList<Person>> friendsRef;

    public Person(Person person = null) // creates a random Person object
    {
      int r = randGen.Next(99999);
      firstName = r.ToString();
      r = randGen.Next(99999999);
      lastName = r.ToString();
      VelocityDbList<Person> personList = new VelocityDbList<Person>();
      if (person != null && person.IsPersistent)
      {
        personList.Persist(person.Session, person);
        personList.Add(person);
        friendsRef = new WeakReference<VelocityDbList<Person>>(personList);
      }
    }

    public string FirstName
    {
      get
      {
        return firstName;
      }
      set
      {
        Update();
        firstName = value;
      }
    }

    public VelocityDbList<Person> Friends
    {
      get
      {
        return friendsRef.GetTarget(false, null);
      }
    }
  }

Usage

  class Program
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "WeakReferences");

    const long numberOfPersons = 10000000000; // a billion Person objects - let's say it is more than we can fit in memory
    static void Main(string[] args)
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginUpdate();
          Person person = new Person();
          for (long i = 0; i < numberOfPersons; i++)
          {
            // Each WeakReference require a persisted object (non null Oid) so that object reference can be substituted with an Oid.
            session.Persist(person);
            // create new Person and make prior Person his/her friend (see constructor of Person)
            person = new Person(person);
          }
          session.Commit();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }

The class AllSupported contains sample data fields of all (or most) kinds of types that have beed tested with VelocityDb. Let us know if we missed any kind of field type.

AllSupported.cs

  public class AllSupported : OptimizedPersistable
  {
    enum byteEnum : byte { a, b, c };
    enum int16Enum : short { a, b, c, d, e, f };
    enum int32Enum : int { a, b, c, d, e, f, g, h };
    enum int64Enum : long { a, b, c, dd, ee, ff };
    int[][] jaggedArray = new int[3][];
    Person person; 
    Pet aPet; // just a sample using a class that is not a subclass of OptimizedPersistable
    byteEnum enumByte;
    int16Enum enumInt16;
    int32Enum enumInt32;
    int64Enum enumInt64;
    PersistenceByInterfaceSnake aSnake;
    byte aByte;
    SByte sByte;
    string aString;
    char aChar;
    public float single;
    public double aDouble;
    public UInt16 uint16;
    public UInt32 uint32;
    public UInt64 uint64;
    public Int16 int16;
    public Int32 int32;
    public Int64 int64;
    public DateTime dateTime;
    public TimeSpan timeSpan;
    public Decimal aDecimal;
    byte? nullableByte;
    SByte? nullablesByte;
    char? nullableChar;
    public float? nullablesingle;
    public double? nullableaDouble;
    public UInt16? nullableuint16;
    public UInt32? nullableuint32;
    public UInt64? nullableuint64;
    public Int16? nullableint16;
    public Int32? nullableint32;
    public Int64? nullableint64;
    public DateTime? nullabledateTime;
    public TimeSpan? nullabletimeSpan;
    public Decimal? nullableDecimal;
    WeakReference<OptimizedPersistable> weakReference;
    byte?[] nullablebyteArray;
    char?[] nullablecharArray;
    UInt16?[] nullableuint16Array;
    UInt32?[] nullableuint32Array;
    UInt64?[] nullableuint64Array;
    Int16?[] nullableint16Array;
    Int32?[] nullableint32Array;
    Int64?[] nullableint64Array;
    float?[] nullablefloatArray;
    double?[] nullabledoubleArray;
    DateTime?[] nullableDateTimeArray;
    Decimal?[] nullableDecimalArray;
    Oid?[] nullableOidArray;
    byte[] byteArray;
    char[] charArray;
    UInt16[] uint16Array;
    UInt32[] uint32Array;
    UInt64[] uint64Array;
    Int16[] int16Array;
    Int32[] int32Array;
    Int64[] int64Array;
    float[] floatArray;
    double[] doubleArray;
    DateTime[] dateTimeArray;
    Oid[] oidArray;
    List<byte> listByte; // all List<> use wrapper OptimizedPersistable looked up from a parent collection
    List<Person> personList;
    List<WeakReference<OptimizedPersistable>> weakReferenceList;
    List<Int16> int16List;
    List<UInt16> uint16List;
    List<Int32> int32List;
    List<UInt32> uint32List;
    List<Int64> int64List;
    List<UInt64> uint64List;
    List<String> stringList;
    List<Oid> oidList;
    List<Oid?> nullableoidList;
    [UseOidShort]
    List<Pet> petListOidShort;
    List<Pet> petListLongOid;
    VelocityDbList<byte> odblistByte; // VelocityDbList is like List but is a subclass of OptimizedPersistable which makes it perform better by avoiding collection lookup
    VelocityDbList<Person> personOdbList;
    VelocityDbList<WeakReference<OptimizedPersistable>> weakReferenceOdbList;
    VelocityDbList<Int16> int16OdbList;
    VelocityDbList<UInt16> uint16OdbList;
    VelocityDbList<Int32> int32OdbList;
    VelocityDbList<UInt32> uint32OdbList;
    VelocityDbList<Int64> int64OdbList;
    VelocityDbList<UInt64> uint64OdbList;
    VelocityDbList<String> stringOdbList;
    VelocityDbList<Pet> petOdbList;
    ArrayList petList2;
    public Slot aSlot;
    public Slot[] m_slots;
    BTreeSet<Person> bTreePerson;
    SortedSetAny<Person> sortedSetPerson;
    SortedMap<byte, Person> sortedMapByteToPerson;
    VelocityDbHashSet<Person> personHashSet;
    public struct Slot
    {
      public int hashCode;
      public Person value;
      public int next;
    }
    public AllSupported(Int32 arraySize)
    {
      aSnake = new PersistenceByInterfaceSnake("Curly", 1, true, 58);
      jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 };
      jaggedArray[1] = new int[] { 0, 2, 4, 6 };
      jaggedArray[2] = new int[] { 11, 22 };
      nullabledateTime = null;
      nullableByte = null;
      enumByte = byteEnum.b;
      enumInt16 = int16Enum.c;
      enumInt32 = int32Enum.f;
      enumInt64 = int64Enum.ff;
      byteArray = new byte[arraySize];
      charArray = new char[arraySize];
      uint16Array = new UInt16[arraySize];
      uint32Array = new UInt32[arraySize];
      uint64Array = new UInt64[arraySize];
      int16Array = new Int16[arraySize];
      int32Array = new Int32[arraySize];
      int64Array = new Int64[arraySize];
      floatArray = new float[arraySize];
      doubleArray = new double[arraySize];
      dateTimeArray = new DateTime[arraySize];
      oidArray = new Oid[arraySize];
      nullablebyteArray = new byte?[arraySize];
      nullablecharArray = new char?[arraySize];
      nullableuint16Array = new UInt16?[arraySize];
      nullableuint32Array = new UInt32?[arraySize];
      nullableuint64Array = new UInt64?[arraySize];
      nullableint16Array = new Int16?[arraySize];
      nullableint32Array = new Int32?[arraySize];
      nullableint64Array = new Int64?[arraySize];
      nullablefloatArray = new float?[arraySize];
      nullabledoubleArray = new double?[arraySize];
      nullableDateTimeArray = new DateTime?[arraySize];
      nullableDecimalArray = new Decimal?[arraySize];
      nullableOidArray = new Oid?[arraySize];
      listByte = new List<byte>(arraySize); // just samples of what Key can be
      personList = new List<Person>(arraySize);
      petListOidShort = new List<Pet>(arraySize);
      petListLongOid = new List<Pet>(arraySize);
      petList2 = new ArrayList(arraySize);
      int32List = new List<Int32>(arraySize);
      uint32List = new List<UInt32>(arraySize);
      uint64List = new List<ulong>(arraySize);
      oidList = new List<Oid>(arraySize);
      nullableoidList = new List<Oid?>(arraySize);
      personHashSet = new VelocityDbHashSet<Person>();
      person = new Person();
      timeSpan = new TimeSpan(1, 0, 0);
      aPet = new Cat("Boze", 5);
      petListOidShort.Add(aPet);
      petListLongOid.Add(aPet);
      petList2.Add(aPet);
      uint32List.Add(5555);
      int32List.Add(-66666);
      uint64List.Add(8989898988989);
      doubleArray[0] = 0.2323232323232;
      aSlot = new Slot();
      aSlot.value = new Person();
      m_slots = new Slot[5];
      nullableoidList.Add(new Oid((ulong)4444));
      nullableoidList.Add(null);
      nullableoidList.Add(new Oid((ulong)8888));
      if (arraySize > 0)
      {
        oidArray[0] = new Oid((ulong)99999);
        nullableOidArray[0] = new Oid((ulong)99999);
        nullableint32Array[0] = 5;
      }
      if (arraySize > 2)
      {
        oidArray[2] = new Oid((ulong)66666);
        nullableOidArray[2] = new Oid((ulong)66666);
        nullableint32Array[2] = 6;
      }
      for (int i = 0; i < 5; i++)
      {
        m_slots[i].hashCode = i;
        m_slots[i].value = new Person();
        m_slots[i].next = i + 1;
      }
    }
  }

Pet.cs

  [Serializable]
  public class Pet
  {
    string name;
    short age;
    public List<Pet> friends;
    public Pet() { }
    public Pet(string aName, short anAge)
    {
      name = aName;
      age = anAge;
      friends = new List<Pet>(2);
    }

    public string Name
    {
      get
      {
        return name;
      }
      set
      {
        name = value;
      }
    }
  }

Cat.cs

  [Serializable]
  public class Cat : Pet
  {
    public string color;
    public Cat() { } // used by Activator.CreateInstance for non OptimizedPersistable subclasses and value types
    public Cat(string aName, short anAge, string color = "black") : base(aName, anAge)
    {
      this.color = color;
    }
  }

Main Program of AllSupported sample

  class Program
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "AllSupportedSample");
    static void Main(string[] args)
    {
      UInt64 id;
      AllSupported allSupported, allSupported2;
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginUpdate();
          allSupported = new AllSupported(3);
          session.Persist(allSupported);
          id = allSupported.Id;
         session.Commit();
        }
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginRead();
          allSupported2 = (AllSupported)session.Open(id);
          session.Commit();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }
  }

Looking at some of the objects created

Shows how to use a BTreeSet and a LINQ query taking advantage of the efficient BTreeSet lookup

  class Program
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "SortedObjects");

    static void Main(string[] args)
    {
      try
      {
        Oid bTreeId;
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          const UInt32 numberOfPersons = 100000;
          const ushort nodeMaxSize = 5000;
          const ushort comparisonByteArraySize = sizeof(UInt64); // enough room to hold entire idNumber of a Person
          const bool comparisonArrayIsCompleteKey = true;
          const bool addIdCompareIfEqual = false;
          Person person;
          session.BeginUpdate();
          //mySession.SetTraceAllDbActivity();
          CompareByField<Person> compareByField = new CompareByField<Person>("idNumber", session, addIdCompareIfEqual);
          BTreeSet<Person> bTree = new BTreeSet<Person>(compareByField, session, nodeMaxSize, comparisonByteArraySize, comparisonArrayIsCompleteKey);
          session.Persist(bTree); // Persist the root of the BTree so that we have something persisted that can be flushed to disk if memory available becomes low
          for (int i = 0; i < numberOfPersons; i++)
          {
            person = new Person();
            session.Persist(person);
            bTree.Add(person);
          }
          bTreeId = bTree.Oid;
          session.Commit();
        }
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          session.BeginRead();
          BTreeSet<Person> bTree = (BTreeSet<Person>)session.Open(bTreeId);
          foreach (Person person in (IEnumerable<Person>)bTree)
          {
            if (person.IdNumber > 196988888791402)
            {
              Console.WriteLine(person);
              break;
            }
          }
          session.Commit();
          session.BeginRead();
          // this LINQ statement will trigger a binary search lookup (not a linear serach) of the matching Person objects in the BTreeSet
          Console.WriteLine((from person in bTree where person.IdNumber > 196988888791402 select person).First());
          session.Commit();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }
  }

DesEncrypted

  class Program
  {
    const UInt32 desEncryptedStartDatabaseNumber = 10;
    const bool compressPages = true;
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "VelocityDB", "Databases", "DesEncrypted");
    static readonly string desEncryptedLocation = System.IO.Path.Combine(systemDir + "desEncryptedLocation");

    static void Main(string[] args)
    {
      try
      {
        using (SessionNoServer session = new SessionNoServer(systemDir))
        {
          DatabaseLocation localLocation = new DatabaseLocation(Dns.GetHostName(), desEncryptedLocation, desEncryptedStartDatabaseNumber, UInt32.MaxValue,
            session, compressPages, PageInfo.encryptionKind.desEncrypted);
          session.BeginUpdate();
          session.NewLocation(localLocation);          
          localLocation.DesKey = Page.StringToByteArray("5d9nndwy"); // Des keys are 8 bytes long
          Person robinHood = new Person("Robin", "Hood", 30);
          Person billGates = new Person("Bill", "Gates", 56, robinHood);
          Person steveJobs = new Person("Steve", "Jobs", 56, billGates);
          robinHood.BestFriend = billGates;
          session.Persist(steveJobs);
          steveJobs.Friends.Add(billGates);
          steveJobs.Friends.Add(robinHood);
          billGates.Friends.Add(billGates);
          robinHood.Friends.Add(steveJobs);
          session.Commit();
        }
        using (SessionNoServer session = new SessionNoServer(systemDir))
        { // Des keys are not persisted in DatabaseLocation (for safety). Instead they are stored as *.des files
          // in the users document directory of the user that created the DatabaseLocation. These files can be copied
          // to other user's document directory when acccess is desired for other users. 
          // Path to user document dir is given by C#: Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
          session.BeginRead();
          Database db = session.OpenDatabase(session.DatabaseNumberOf(typeof(Person)));
          foreach (Page page in db)
          {
            foreach (OptimizedPersistable obj in page)
            {
              Person person = obj as Person;
              if (person != null)
                Console.WriteLine(person.FirstName);
            }
          }
          session.Commit();
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.ToString());
      }
    }
  }

This sample illustrates the support for changing class definition of persistently stored objects

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using VelocityDb;
   6:  using VelocityDb.TypeInfo;
   7:  using VelocityDb.Session;
   8:  using VelocityDbSchema.Samples.UpdateClass;
   9:  using System.IO;
  10:   
  11:  namespace UpdateClass
  12:  {
  13:    /// <summary>
  14:    /// Test updating a class that already exist in the Database schema, rerun multiple times. Modify class in between runs. Check objects by browsing them with VelocityDbBrowser
  15:    /// </summary>
  16:    public class UpdateClass
  17:    {
  18:      static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
  19:        "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "UpdateClass");
  20:   
  21:      static void Main(string[] args)
  22:      {
  23:        try
  24:        {
  25:          UpdatedClass updatedClassObject;
  26:          using (SessionNoServer session = new SessionNoServer(systemDir))
  27:          {
  28:            session.BeginUpdate();
  29:            foreach (UpdatedClass obj in session.AllObjects<UpdatedClass>())
  30:            {
  31:              Console.Write(obj.ToString() + " has members: ");
  32:              foreach (DataMember member in obj.GetDataMembers())
  33:              {
  34:                Console.Write(member.ToString() + " ");
  35:              }
  36:              Console.WriteLine();
  37:              //obj.UpdateTypeVersion(); // uncomment if you want to migrate this object to the latest version of the class
  38:            }
  39:            updatedClassObject = new UpdatedClass();
  40:            session.UpdateClass(typeof(UpdatedClass)); // call this when you have updated the class since first storing instances of this type or since last call to UpdateClass
  41:            session.Persist(updatedClassObject);
  42:            session.Commit();
  43:          }
  44:        }
  45:        catch (Exception ex)
  46:        {
  47:          Console.WriteLine(ex.ToString());
  48:        }
  49:      }
  50:    }
  51:  }

Sometimes it is convenient to use LINQ to get data the way you want it. We use it in the IssueTracker that we have online at VelocityDB.com. As you see deploying an asp.net using VelocityDB is easy. The only issue is updating the host and path of DatabaseLocations used.

public ICollection<ProductVersion> AllVersions(string sortExpression)
    {
      try
      {
        string dataPath = HttpContext.Current.Server.MapPath("~/tracker");
        using (SessionNoServer session = new SessionNoServer(dataPath, false, 2000, true, false))
        {
          session.BeginRead();
          IssueTracker bugTracker = (IssueTracker)session.Open(IssueTracker.PlaceInDatabase, 1, 1, false);
          ICollection<ProductVersion> versionSet = bugTracker.VersionSet.Keys;
          if (sortExpression.Length > 0)
          {
            string[] selectAndDir = sortExpression.Split(' ');
            int sorting = int.Parse(selectAndDir[0]);
            bool reverse = selectAndDir.Length > 1;
            switch (sorting)
            {
              case 1: versionSet = (from version in versionSet orderby version.Name select version).ToList();
                break;
              case 2: versionSet = (from version in versionSet orderby version.Description select version).ToList();
                break;
              case 3: versionSet = (from version in versionSet orderby version.ReleaseDate select version).ToList();
                break;
              case 4: versionSet = (from version in versionSet orderby version.CreatedBy select version).ToList();
                break;
            }
            if (reverse)
            {
              List<ProductVersion> tempList = new List<ProductVersion>(versionSet.Count);
              IEnumerable<ProductVersion> temp = versionSet.Reverse<ProductVersion>();
              foreach (ProductVersion issue in temp)
              {
                tempList.Add(issue);
              }
              versionSet = tempList;
            }
          }
          session.Commit();
          return versionSet;
        }
      }
      catch (System.Exception ex)
      {
        this.errorLabel.Text = ex.ToString();
      }
      return null;
    }

Here we import the entire collection of Wikipedia articles/subjects. Wikipedia is available as one giant XML file, the file is 40 GB large (uncompressed). You can download the compressed file (~9 GB) from here. Uncompress it using 7-zip. The import to VelocityDB databases is very fast, it takes about 20 minutes to read, parse and store all 652,870,281 lines of text into 80 structured VelocityDB databases (from & to a spinning disk). The sample program then continues by creating an inverted index of all 12,772,300 Wikipedia articles, the index is created using parallell threads. All the C# source code for this sample is provided in our download.

Loads World Cities with population from a csv file (~about 2.7 million records) provided by MaxMind. After loading the city data, we attempt to mimic the behavior of QlikView. A ListBoxItem that is selected has a green background, an associated value has a white background and non-associated values have a light gray background. You can select single cells or multiple cells in each column and see how the associations (colors) change. QlikView is a very successful product with revenue around $260 million/year. The product was invented and mostly coded by my fellow Swede, Håkan Wolgé

ImdbImport imports from two compressed text files containing actors, actresses and movie data. The data comes from IMDB


The KeveinBaconNumers application computes the degree of seperation between Kevin Bacon and all other actors and actresses (~ 2 million) taking part in about 350,000 movies.

0 degrees means you are Kevin Bacon

1 degree, you acted in one of Kevin Bacon's movies

2 degrees, you acted in a movie with someone who acted in the same movie as Kevin Bacon

and so on...


The output looks similar to Oracle of Bacon (which by the way is not using an Oracle database!)


[c:\VelocityDb\Release]timer& KevinBaconNumbers.exe& timer

Timer 1 on: 12:07:04

Degree 0 has # of people: 1

Degree 1 has # of people: 3475

Degree 2 has # of people: 385345

Degree 3 has # of people: 894996

Degree 4 has # of people: 121903

Degree 5 has # of people: 7462

Degree 6 has # of people: 446

Degree 7 has # of people: 28

Degree 8 has # of people: 0

Degree 9 has # of people: 0

Timer 1 off: 12:07:33 Elapsed: 0:00:29.81


The entire C# source code for these two applications are part of the download of VelocityDB, download it for free from here.

 

We ran this on a desktop PC, the same one that was used for the triangle counter.


How would you compute this matrix using only SQL code? We bet it wouldn't compute as fast, the entire matrix is calculated in less than half a minute using VelocityDB.

   1:  using System;
   2:  using System.Collections;
   3:  using System.Linq;
   4:  using VelocityDb;
   5:  using VelocityDb.Session;
   6:   
   7:  namespace Verify
   8:  {
   9:    class Verify
  10:    {
  11:      static int Main(string[] args)
  12:      {
  13:        if (args.Length == 0)
  14:        {
  15:          System.Console.WriteLine("ERROR, no boot path specified. Restart Verify and add bootup database path as a command line parameter");
  16:          return 1;
  17:        }
  18:        int ct = 0;
  19:        try
  20:        {
  21:          using (SessionNoServer session = new SessionNoServer(args[0]))
  22:          {
  23:            DataCache.MaximumMemoryUse = 5000000000; // 5 GB, set this to what fits your case
  24:            session.BeginRead();
  25:            foreach (object obj in session.AllObjects<object>())
  26:            {
  27:              ++ct;
  28:              IOptimizedPersistable iPers = obj as IOptimizedPersistable;
  29:              if (iPers != null)
  30:              {
  31:                UInt64 id = iPers.Id;
  32:                OptimizedPersistable pObj = iPers as OptimizedPersistable;
  33:                if (pObj != null)
  34:                {
  35:                  pObj.LoadFields();
  36:                  foreach (OptimizedPersistable fObj in pObj.OptimizedPersistableFieldValues())
  37:                  {
  38:                    fObj.LoadFields();
  39:                  }
  40:                  foreach (object value in pObj.GetFieldValues())
  41:                  {
  42:                    WeakReferenceBase weakRef = value as WeakReferenceBase;
  43:                    if (weakRef != null)
  44:                      session.Open(weakRef.Id);
  45:                  }
  46:                }
  47:              }
  48:              else if (obj is string)
  49:                continue;
  50:              else if (obj is Array)
  51:                continue;
  52:              IEnumerable anEnum = obj as IEnumerable;
  53:              if (anEnum != null)
  54:                foreach (object o in anEnum)
  55:                {
  56:                }
  57:            }
  58:            session.Commit();
  59:          }
  60:          Console.WriteLine("OK, verfied " + ct + " objects");
  61:          return 0;
  62:        }
  63:        catch (Exception ex)
  64:        {
  65:          Console.WriteLine("FAILED, verfied " + ct + " objects");
  66:          Console.WriteLine(ex.ToString());
  67:          return -1;
  68:        }
  69:      }
  70:    }
  71:  }

Shows how to display persistent objects using an asp TreeView control. We are making the source code public so that you can help us improve the browser. Suggestions are welcome.

This is a mini web crawler that grabs the text from selected web sites and shows which words are used and how frequently. I tried to use teqniques as described in a paper by the Google founders. Did I do it right? Help me out!

The TextIndexer has been extended to also indexing local text files. We downloaded some free complete books from Top 100 - Project Gutenberg

This sample applications imports Baseball statistics data from csv files provided by Sean Lahman

We are including some NUnit tests. These tests should further help explaining how use the VelocityDb API. New NUnit tests are welcome! The NUnit GUI driver is awesome!

There are some good bug tracking software out there but we needed a more complete sample application so we build my own issue (bug) tracking system. Please help us improve this application so that it feels more complete. This application is now included in the sample solution provided with the download of VelocityDB. A live demo of this application is available here.

By working together we can improve the samples. We develop using git as a source control tool. We will setup a repository on GitHub or a similar site until then please email us suggestions and C# sample code. Alternativly, you can use the Isuue tracker and open an issue describing an improvement or a bug. You can attach files to an issue. Email to: Samples@VelocityDB.com

VelocityGraph Sample Applications

This sample is a preview of VelocityGraph, help us define the best possible graph api! The start of this api is inspired by Dex, the sample here mimics what the dex sample is doing. Dex is considered by some to have the best performing graph api currently available. Unlike Dex, which is a C++/C implemented api, VelocityGraph is all implemented in C# and enable any type of Element values. VelocityGraph is provided as open source on GitHub, https://github.com/VelocityDB/VelocityGraph. and implements the graph API Blueprints standard interfaces as provided in Frontenac Blueprints. Anyone is welcome to contribute! VelocityGraph is built on top of VelocityDB. You will need a VelocityDB license to use VelocityGraph. Eventually VelocityGraph may have its own web site, VelocityGraph.com

   1:  using System;
   2:  using System.IO;
   3:  using System.Linq;
   4:  using VelocityDb.Session;
   5:  using VelocityGraph;
   6:   
   7:  namespace VelocityGraphSample
   8:  {
   9:    using Frontenac.Blueprints;
  10:    using System.Collections.Generic;
  11:    class VelocityGraphSample
  12:    {
  13:      static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
  14:        "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "VelocityGraphSample");
  15:   
  16:      static void Main(string[] args)
  17:      {
  18:        if (Directory.Exists(systemDir))
  19:          Directory.Delete(systemDir, true); // remove systemDir from prior runs and all its databases.
  20:   
  21:        using (SessionNoServer session = new SessionNoServer(systemDir, false))
  22:        {
  23:          session.BeginUpdate();
  24:          Graph g = new Graph(session);
  25:   
  26:      // SCHEMA
  27:          // Add a node type for the movies, with a unique identifier and two indexed Propertys
  28:          VertexType movieType = g.NewVertexType("MOVIE");
  29:          PropertyType movieTitleType = g.NewVertexProperty(movieType, "TITLE", DataType.String, PropertyKind.Indexed);
  30:          PropertyType movieYearType = g.NewVertexProperty(movieType, "YEAR", DataType.Integer, PropertyKind.Indexed);
  31:   
  32:          // Add a node type for the people, with a unique identifier and an indexed Property
  33:          VertexType peopleType = g.NewVertexType("PEOPLE");
  34:          PropertyType peopleNameType = g.NewVertexProperty(peopleType, "NAME", DataType.String, PropertyKind.Indexed);
  35:   
  36:          // Add an undirected edge type with a Property for the cast of a movie
  37:          EdgeType castType = g.NewEdgeType("CAST", false);
  38:          PropertyType castCharacterType = g.NewEdgeProperty(castType, "CHARACTER", DataType.String, PropertyKind.Indexed);
  39:   
  40:          // Add a directed edge type restricted to go from people to movie for the director of a movie
  41:          EdgeType directsType = g.NewEdgeType("DIRECTS", true, peopleType, movieType);
  42:   
  43:      // DATA
  44:          // Add some MOVIE nodes
  45:   
  46:          Vertex mLostInTranslation = g.NewVertex(movieType);
  47:          mLostInTranslation.SetProperty(movieTitleType, "Lost in Translation");
  48:          mLostInTranslation.SetProperty(movieYearType, (int) 2003);
  49:   
  50:          Vertex mVickyCB = g.NewVertex(movieType);
  51:          mVickyCB.SetProperty(movieTitleType, "Vicky Cristina Barcelona");
  52:          mVickyCB.SetProperty(movieYearType, (int)2008);
  53:   
  54:          Vertex mManhattan = g.NewVertex(movieType);
  55:          mManhattan.SetProperty(movieTitleType, "Manhattan");
  56:          mManhattan.SetProperty(movieYearType, (int) 1979);
  57:   
  58:          // Add some PEOPLE nodes
  59:          Vertex pScarlett = g.NewVertex(peopleType);
  60:          pScarlett.SetProperty(peopleNameType, "Scarlett Johansson");
  61:   
  62:          Vertex pBill = g.NewVertex(peopleType);
  63:          pBill.SetProperty(peopleNameType, "Bill Murray");
  64:   
  65:          Vertex pSofia = g.NewVertex(peopleType);
  66:          pSofia.SetProperty(peopleNameType, "Sofia Coppola");
  67:   
  68:          Vertex pWoody = g.NewVertex(peopleType);
  69:          pWoody.SetProperty(peopleNameType, "Woody Allen");
  70:   
  71:          Vertex pPenelope = g.NewVertex(peopleType);
  72:          pPenelope.SetProperty(peopleNameType, "Penélope Cruz");
  73:   
  74:          Vertex pDiane = g.NewVertex(peopleType);
  75:          pDiane.SetProperty(peopleNameType, "Diane Keaton");
  76:   
  77:          // Add some CAST edges
  78:          Edge anEdge;
  79:          anEdge = g.NewEdge(castType, mLostInTranslation, pScarlett);
  80:          anEdge.SetProperty(castCharacterType, "Charlotte");
  81:   
  82:          anEdge = g.NewEdge(castType, mLostInTranslation, pBill);
  83:          anEdge.SetProperty(castCharacterType, "Bob Harris");
  84:   
  85:          anEdge = g.NewEdge(castType, mVickyCB, pScarlett);
  86:          anEdge.SetProperty(castCharacterType, "Cristina");
  87:   
  88:          anEdge = g.NewEdge(castType, mVickyCB, pPenelope);
  89:          anEdge.SetProperty(castCharacterType, "Maria Elena");
  90:   
  91:          anEdge = g.NewEdge(castType, mManhattan, pDiane);
  92:          anEdge.SetProperty(castCharacterType, "Mary");
  93:   
  94:          anEdge = g.NewEdge(castType, mManhattan, pWoody);
  95:          anEdge.SetProperty(castCharacterType, "Isaac");
  96:   
  97:          // Add some DIRECTS edges
  98:          anEdge = g.NewEdge(directsType, pSofia, mLostInTranslation);
  99:          anEdge = g.NewEdge(directsType, pWoody, mVickyCB);
 100:          anEdge = g.NewEdge(directsType, pWoody, mManhattan);
 101:   
 102:      // QUERIES
 103:          // Get the movies directed by Woody Allen
 104:          Dictionary<Vertex, HashSet<Edge>> directedByWoody = pWoody.Traverse(directsType, Direction.Out);
 105:   
 106:          // Get the cast of the movies directed by Woody Allen
 107:          Dictionary<Vertex, HashSet<Edge>> castDirectedByWoody = g.Traverse(directedByWoody, castType, Direction.Both);
 108:   
 109:          // Get the movies directed by Sofia Coppola
 110:          Dictionary<Vertex, HashSet<Edge>> directedBySofia = pSofia.Traverse(directsType, Direction.Out);
 111:   
 112:          // Get the cast of the movies directed by Sofia Coppola
 113:          Dictionary<Vertex, HashSet<Edge>> castDirectedBySofia = g.Traverse(directedBySofia, castType, Direction.Both);
 114:   
 115:          // We want to know the people that acted in movies directed by Woody AND in movies directed by Sofia.
 116:          IEnumerable<Vertex> castFromBoth = castDirectedByWoody.Keys.Intersect(castDirectedBySofia.Keys);
 117:   
 118:          // Say hello to the people found
 119:          foreach (Vertex person in castFromBoth)
 120:          {
 121:            object value = person.GetProperty(peopleNameType);
 122:            System.Console.WriteLine("Hello " + value);
 123:          }
 124:   
 125:          var billM = g.Traverse(directedBySofia, castType, Direction.Both).Keys.Where(vertex => vertex.GetProperty(peopleNameType).Equals("Bill Murray"));
 126:   
 127:          // Say hello to Bill Murray
 128:          foreach (Vertex person in billM)
 129:          {
 130:            object value = person.GetProperty(peopleNameType);
 131:            System.Console.WriteLine("Hello " + value);
 132:          }
 133:   
 134:          session.Persist(g);
 135:          session.Commit();
 136:        }
 137:   
 138:        using (SessionNoServer session = new SessionNoServer(systemDir, false))
 139:        {
 140:          session.BeginRead();
 141:          Graph g = Graph.Open(session);
 142:          VertexType movieType = g.FindVertexType("MOVIE");
 143:          PropertyType movieTitleProperty = g.FindVertexProperty(movieType, "TITLE");
 144:          Vertex obj = g.FindVertex(movieTitleProperty, "Manhattan");
 145:          session.Commit();
 146:        }
 147:      }
 148:    }
 149:  }
/*
Social graph test
http://odysseas.calit2.uci.edu/doku.php/public:online_social_networks#facebook_social_graph_-_breadth_first_search

Contains a set of unique users on Facebook, and all the userids of their friends. Can be used as a good example for finding shortest path queries.
queries:
- Find Shortest path between two given user ids
- Get all the paths between two user ids
- Get the number of unique 2nd level friends a given user has (friends of my friends)
- Get the top 10 users with most friends
*/

using Frontenac.Blueprints;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VelocityDb;
using VelocityDb.Session;
using VelocityGraph;

namespace FacebookGraph
{
  class FacebookGraph
  {
    static readonly string systemDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "FacebookGraph");

    static readonly string inputData = "D:\\bfs-1-socialgraph-release"; // change if you need to, download dataset from http://www.VelocityDB.com/Public/bfs-1-socialgraph-release.7z.exe (then rename it bfs-1-socialgraph-release.7z and uncompress with 7-zip http://www.7-zip.org/
    //static readonly string inputData = "D:\\bfs-28-socialgraph-release";// change if you need to, download dataset from http://odysseas.calit2.uci.edu/doku.php/public:online_social_networks#facebook_social_graph_-_breadth_first_search
    static readonly string licenseDbFile = "D:\\dbs\\4.odb";
    enum Gender { Male, Female, Unknown };

    static void Main(string[] args)
    {
      DataCache.MaximumMemoryUse = 7000000000; // 7 GB, set this to what fits your case
      FacebookGraph facebookGraph = new FacebookGraph();
      bool import = args.Length > 0 && args[0].ToLower() == "-import";
      bool dirExist = Directory.Exists(systemDir);
      if (import || !dirExist)
        facebookGraph.ingestData();
      facebookGraph.doQueries();
    }

    public void ingestData()
    {
      if (Directory.Exists(systemDir))
        Directory.Delete(systemDir, true); // remove systemDir from prior runs and all its databases.
      Directory.CreateDirectory(systemDir);
      File.Copy(licenseDbFile, Path.Combine(systemDir, "4.odb"));

      using (SessionNoServer session = new SessionNoServer(systemDir, false, 5000, false, true))
      {
        session.BeginUpdate();
        session.DefaultDatabaseLocation().CompressPages = false;
        Graph g = new Graph(session);
        session.Persist(g);

// SCHEMA
        VertexType userType = g.NewVertexType("User");

        EdgeType friendEdgeType = g.NewEdgeType("Friend", true, userType, userType);

// DATA
        int lineNumber = 0;
        long fiendsCt = 0;
        int stop = (int) Math.Pow(2, 26); // make sure to create enough of these
        for (int i = 1; i < stop; i++)
          userType.NewVertex(g);
        session.Commit();
        session.BeginUpdate();
        foreach (string line in File.ReadLines(inputData))
        {
          if (++lineNumber % 10000 == 0)
            Console.WriteLine("Parsing user " + lineNumber + ", friends total: " + fiendsCt + " at " + DateTime.Now);
          string[] fields = line.Split(' ');
          Vertex aUser = new Vertex(g, userType, lineNumber);
          foreach (string s in fields)
          {
            if (s.Length > 0)
            {
              ++fiendsCt;
              Vertex aFriend = new Vertex(g, userType, int.Parse(s));
              friendEdgeType.NewEdge(g, aUser, aFriend, session);
            }
          }
          if (lineNumber >= 60000) // remove this condition if you have time to wait a while...
            break;
        }
        Console.WriteLine("Done importing " + lineNumber + " users with " + fiendsCt + " friends");
        session.Commit();
      }
    }

// Queries
    public void doQueries()
    {
      using (SessionNoServer session = new SessionNoServer(systemDir, false))
      {
        session.BeginRead();
        Graph g = Graph.Open(session); // it takes a while to open graph fresh from databases
        VertexType userType = g.FindVertexType("User");
        EdgeType friendEdgeType = g.FindEdgeType("Friend");

        Vertex someUser = userType.GetVertex(1);
        Vertex someUser2 = userType.GetVertex(2);

        var someUserFriends = from Edge e in someUser.GetEdges(friendEdgeType, Direction.Out)
                              select e.Head;

// Find Shortest path between two given user ids
        List<List<Edge>> path = someUser.Traverse(someUser2, friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(41), friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(2876), friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(2798), friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(175), friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(1531), friendEdgeType, 10, false);
        path = someUser.Traverse(userType.GetVertex(1537), friendEdgeType, 10, false);
        Console.WriteLine();

// Get all the paths between two user ids
        path = someUser.Traverse(someUser2, friendEdgeType, 10, true);
        path = someUser.Traverse(userType.GetVertex(41), friendEdgeType, 10, true);

// Get the number of unique 2nd level friends a given user has (friends of my friends)

        var someUsers2ndLevelFriends = (from v in someUserFriends
                                        from Edge e in v.GetEdges(friendEdgeType, Direction.Out)
                                        select e.Head).Distinct();

        Console.WriteLine("unique 2nd level friends a given user has");
        int ct = 0;
        foreach (Vertex v in someUsers2ndLevelFriends)
        {
          if (++ct % 100 == 0)
            Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine("Some user has: " + ct + " 2nd level friends");
        Console.WriteLine();

// Get the top 10 users with most friends
        var top10mostFriends = from vertex in userType.GetTopNumberOfEdges(friendEdgeType, 10, Direction.Out)
                               select vertex;
        Console.WriteLine("top 10 users with most friends");
        foreach (Vertex v in top10mostFriends)
        {
          long count = v.GetNumberOfEdges(friendEdgeType, Direction.Out);
          Console.WriteLine("User id: " + v.VertexId + "\t number of friends: " + count);
        }
        Console.WriteLine();
        session.Commit();
      }
    }
  }
}
/*
Dating Recommendations
http://www.occamslab.com/petricek/data/

Dating site with ratings. Can be used to recommend other people a user might like.
17 million ratings, 168 000 profiles that are rated by 135 000 users.

Complex queries
-    Given a user id, and based on the rated profiles, find other users who have rated similarly on the same profiles, and find other profiles to recommend based on what those other users have rated
-    Get all female users with less than 50 ratings
-    Get all male users with at least one 10 rating
-    Get the first 10 male users who have rated at least 3 of the same profiles as the given user.

Statistics queries
-    Get the 20 profiles with the most ratings
-    Get the 20 best rated profiles regardless of gender
-    Get the 20 best rated males
-    Get the 20 best rated females
*/

using System;
using System.IO;
using System.Linq;
using VelocityDb.Session;
using VelocityGraph;
using Frontenac.Blueprints;
using System.Collections.Generic;
using VelocityDb;

namespace DatingRecommendations
{

  class DatingRecommendations
  {
    static readonly string systemDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
      "VelocityDB" + Path.DirectorySeparatorChar + "Databases" + Path.DirectorySeparatorChar + "DatingRecommendations");

    static readonly string inputDataDir = "D:\\libimseti"; // change if you need to, download dataset from http://www.occamslab.com/petricek/data/
    static readonly string licenseDbFile = "D:\\4.odb";
    enum Gender { Male, Female, Unknown };

    static void Main(string[] args)
    {
      bool import = args.Length > 0 && args[0].ToLower() == "-import";
      bool dirExist = Directory.Exists(systemDir);
      if (import || !dirExist)
      {
        if (dirExist)
          Directory.Delete(systemDir, true); // remove systemDir from prior runs and all its databases.
        Directory.CreateDirectory(systemDir);
        File.Copy(licenseDbFile, Path.Combine(systemDir, "4.odb"));

        using (SessionNoServer session = new SessionNoServer(systemDir, false))
        {
          DataCache.MaximumMemoryUse = 7000000000; // 7 GB, set this to what fits your case
          session.BeginUpdate();
          session.DefaultDatabaseLocation().CompressPages = false;
          Graph g = new Graph(session);
          session.Persist(g);

          // SCHEMA
          VertexType userType = g.NewVertexType("User");
          PropertyType genderType = userType.NewProperty("Gender", DataType.Integer, PropertyKind.Indexed);

          VertexType ratingType = g.NewVertexType("Rating");
          PropertyType ratingValuePropertyType = ratingType.NewProperty("RatingValue", DataType.Integer, PropertyKind.Indexed);

          EdgeType ratingEdgeType = g.NewEdgeType("UserToRating", true, userType, ratingType);

          EdgeType ratingOfType = g.NewEdgeType("RatingOf", false, userType, userType);
          PropertyType ratingEdgePropertyType = ratingOfType.NewProperty("Rating", DataType.Integer, PropertyKind.Indexed);

          // DATA
          using (FileStream stream = File.OpenRead(System.IO.Path.Combine(inputDataDir, "gender.dat")))
          {
            using (StreamReader file = new System.IO.StreamReader(stream))
            {
              string line;
              int lineNumber = 0;
              while ((line = file.ReadLine()) != null)
              {
                lineNumber++;
                string[] fields = line.Split(',');
                Vertex aUser = g.NewVertex(userType);
                aUser.SetProperty(genderType, (int)fields[1][0] == 'M' ? Gender.Male : fields[1][0] == 'F' ? Gender.Female : Gender.Unknown);
              }
              Console.WriteLine("Done importing " + lineNumber + " users");
            }
          }

          using (FileStream stream = File.OpenRead(System.IO.Path.Combine(inputDataDir, "ratings.dat")))
          {
            using (StreamReader file = new System.IO.StreamReader(stream))
            {
              string line;
              int lineNumber = 0;
              Vertex rater = null;
              int raterId;
              int priorRaterId = -1;

              while ((line = file.ReadLine()) != null)
              {
                lineNumber++;
                if (lineNumber % 1000000 == 0)
                  Console.WriteLine("Parsing rating # " + lineNumber);
                string[] fields = line.Split(',');
                raterId = int.Parse(fields[0]);
                if (raterId != priorRaterId)
                  rater = userType.GetVertex(raterId);
                priorRaterId = raterId;
                int ratedId = int.Parse(fields[1]);
                int rating = int.Parse(fields[2]);
                Vertex ratingVertex = (from v in ratingType.GetVertices(g) where ((int)v.GetProperty(ratingValuePropertyType)) == rating select v).FirstOrDefault();
                if (ratingVertex == null)
                {
                  ratingVertex = ratingType.NewVertex(g);
                  ratingVertex.SetProperty(ratingValuePropertyType, rating);
                }
                Vertex rated = userType.GetVertex(ratedId);
                Edge aRatingOf = ratingOfType.NewEdge(g, rater, rated, session);
                aRatingOf.SetProperty(ratingEdgePropertyType, rating);
                Edge userRating = ratingEdgeType.NewEdge(g, rated, ratingVertex, session);
                if (lineNumber >= 10000000) // remove this condition if you have time to wait a while and you have at least 16GB of RAM memory
                  break;
              }
              Console.WriteLine("Done importing " + lineNumber + " ratings");
            }
          }
          session.Commit();
        }
      }
      // Query
      using (SessionNoServer session = new SessionNoServer(systemDir, false))
      {
        session.BeginRead();
        Graph g = Graph.Open(session);
        VertexType userType = g.FindVertexType("User");
        PropertyType genderType = userType.FindProperty("Gender");

        VertexType ratingType = g.FindVertexType("Rating");
        PropertyType ratingValuePropertyType = ratingType.FindProperty("RatingValue");

        EdgeType ratingEdgeType = g.FindEdgeType("UserToRating");
        PropertyType userRatingEdgePropertyType = ratingEdgeType.FindProperty("UserRating");

        EdgeType ratingOfType = g.FindEdgeType("RatingOf");
        PropertyType ratingEdgePropertyType = ratingOfType.FindProperty("Rating");
        // Complex queries
        var ratingsVertexEnum = from v in ratingType.GetVertices(g) orderby v.GetProperty(ratingValuePropertyType) descending select v;
        int ct = 0;

// Given a user id, and based on the rated profiles, find other users who have rated similarly on the same profiles, and find other profiles to recommend based on what those other users have rated
        Vertex someUser = userType.GetVertex(1);
        var similarRatings = (from Edge e in someUser.GetEdges(ratingOfType, Direction.Out)
                              from Edge edge in e.Head.GetEdges(ratingOfType, Direction.Out)
                              where someUser != edge.Head
                              where ((int)e.GetProperty(ratingEdgePropertyType) == (int)edge.GetProperty(ratingEdgePropertyType))
                              select edge.Tail).Distinct();

        var someUserRated = from Edge e in someUser.GetEdges(ratingOfType, Direction.Out)
                            select e.Head;

        var recommendedProfles = from v in similarRatings
                                 from Edge e in v.GetEdges(ratingOfType, Direction.Out)
                                 where someUserRated.Contains(e.Head) == false
                                 select e.Head;

        Console.WriteLine("Some user's rated profiles");
        ct = 0;
        foreach (Vertex v in someUserRated)
        {
          ct++;
          //Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine("Number of some user's rated profiles: " + ct);

        Console.WriteLine("Given a user id, and based on the rated profiles, find other users who have rated similarly on the same profiles");
        ct = 0;
        foreach (Vertex v in similarRatings)
        {
          ct++;
          //Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine("Number of matching profiles: " + ct);

        Console.WriteLine("Given a user id, and based on the rated profiles, find other users who have rated similarly on the same profiles, and find other profiles to recommend based on what those other users have rated");
        ct = 0;
        foreach (Vertex v in recommendedProfles)
        {
          if (ct++ % 5000 == 0) // don't print them all !
            Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine("Number of matching profiles: " + ct);

// Get all female users with less than 50 ratings
        Console.WriteLine();
        var females = from u in userType.GetVertices(g)
                      where ((Gender)u.GetProperty(genderType)) == Gender.Female
                      select u;
        var femalesLess50Ratings = from f in females
                                   where f.GetNumberOfEdges(ratingEdgeType, Direction.Out) < 50
                                   select f;
        Console.WriteLine("Female users with less than 50 ratings");
        ct = 0;
        foreach (Vertex f in femalesLess50Ratings)
        {
          long count = f.GetNumberOfEdges(ratingEdgeType, Direction.Out);
          if (ct++ % 5000 == 0) // don't print them all !
            Console.WriteLine("User id: " + f.VertexId + "\tnumber of ratings: " + count);
        }
        Console.WriteLine("Number of females with fewer than 50 ratings: " + ct);
        Console.WriteLine();

// Get all male users with at least one 10 rating
        Console.WriteLine();
        var rated10vertex = (from v in ratingType.GetVertices(g)
                             where ((int)v.GetProperty(ratingValuePropertyType)) == 10
                             select v).First();

        var rated10 = (from e in rated10vertex.GetEdges(ratingEdgeType, Direction.In)
                       select e.GetVertex(Direction.Out)).Distinct();

        var rated10male = from Vertex v in rated10
                          where ((Gender)v.GetProperty(genderType)) == Gender.Male
                          select v;

        Console.WriteLine("Males with at least one 10 rating");
        ct = 0;
        foreach (Vertex v in rated10male)
        {
          if (ct++ % 5000 == 0) // don't print them all !
            Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine("Number of males with at least one 10 rating: " + ct);
        Console.WriteLine();

// Get the first 10 male users who have rated at least 3 of the same profiles as the given user.
        Console.WriteLine("10 male users who have rated at least 3 of the same profiles as the given user");
        var males = from u in userType.GetVertices(g)
                    where ((Gender)u.GetProperty(genderType)) == Gender.Male
                    select u;

        var someUserHasRated = from Edge o in someUser.GetEdges(ratingOfType, Direction.Out)
                               select o.Head;

        var first10withSame3ratedAs = from m in males
                                      where (from Edge r in m.GetEdges(ratingOfType, Direction.Out) select r.Head).Intersect(someUserHasRated).ToArray().Length >= 3
                                      select m;
        ct = 0;
        foreach (Vertex v in first10withSame3ratedAs)
        {
          if (++ct > 10)
            break;
          Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine();

        // Statistical queries
// Get the 20 profiles with the most ratings
        var top20mostRatings = (from v in userType.GetVertices(g)
                               orderby v.GetNumberOfEdges(ratingEdgeType, Direction.Out) descending
                               select v).Take(20);
        Console.WriteLine("20 profiles with the most ratings");
        ct = 0;
        foreach (Vertex v in top20mostRatings)
        {
          int count = (int)v.GetNumberOfEdges(ratingEdgeType, Direction.Out);  
          Console.WriteLine("User id: " + v.VertexId + "\tnumber of ratings: " + count);
        }
        Console.WriteLine();

// Get the 20 best rated profiles regardless of gender
        var top = from v in ratingsVertexEnum
                  from e in v.GetEdges(ratingEdgeType, Direction.In)
                  select e.GetVertex(Direction.Out);
        Console.WriteLine("20 best rated profiles regardless of gender");
        ct = 0;
        foreach (Vertex v in top)
        {
          if (++ct > 20)
            break;
          Console.WriteLine("User id: " + v.VertexId);
        }
        Console.WriteLine();

// Get the 20 best rated males
        Console.WriteLine("20 best rated male profiles");
        var top20males = from Vertex v in top
                         where ((Gender)v.GetProperty(genderType)) == Gender.Male
                         select v;
        ct = 0;
        foreach (Vertex v in top20males)
        {
          if (++ct > 20)
            break;
          Console.WriteLine("Male User id: " + v.VertexId);
        }
        Console.WriteLine();

// Get the 20 best rated females
        Console.WriteLine("20 best rated female profiles");
        var top20females = from Vertex v in top 
                           where ((Gender)v.GetProperty(genderType)) == Gender.Female
                           select v;
        ct = 0;
        foreach (Vertex v in top20females)
        {
          if (++ct > 20)
            break;
          Console.WriteLine("Female User id: " + v.VertexId);
        }
        session.Commit();
      }
    }
  }
}