5. Collections and Arrays


We will slowly move towards real-time data processing now by installing sensors to our car and collecting their output.

using System;
using System.Text;
    
namespace Db4objects.Db4o.Tutorial.F1.Chapter3
{   
    public class SensorReadout
    {
        double[] _values;
        DateTime _time;
        Car _car;
        
        public SensorReadout(double[] values, DateTime time, Car car)
        {
            _values = values;
            _time = time;
            _car = car;
        }
        
        public Car Car
        {
            get
            {
                return _car;
            }
        }
        
        public DateTime Time
        {
            get
            {
                return _time;
            }
        }
        
        public int NumValues
        {
            get
            {
                return _values.Length;
            }
        }
        
        public double[] Values
        {
            get
            {
                return _values;
            }
        }
        
        public double GetValue(int idx)
        {
            return _values[idx];
        }
        
        override public string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(_car);
            builder.Append(" : ");
            builder.Append(_time.TimeOfDay);
            builder.Append(" : ");
            for (int i=0; i<_values.Length; ++i)
            {
                if (i > 0)
                {
                    builder.Append(", ");
                }
                builder.Append(_values[i]);
            }
            return builder.ToString();
        }
    }
}


A car may produce its current sensor readout when requested and keep a list of readouts collected during a race.

using System;
using System.Collections;
    
namespace Db4objects.Db4o.Tutorial.F1.Chapter3
{
    public class Car
    {
        string _model;
        Pilot _pilot;
        IList _history;
        
        public Car(string model) : this(model, new ArrayList())
        {
        }
        
        public Car(string model, IList history)
        {
            _model = model;
            _pilot = null;
            _history = history;
        }
        
        public Pilot Pilot
        {
            get
            {
                return _pilot;
            }
            
            set
            {
                _pilot = value;
            }
        }
        
        public string Model
        {
            get
            {
                return _model;
            }
        }
        
        public IList History
        {
            get
            {
                return _history;
            }
        }
        
        public void Snapshot()
        {
            _history.Add(new SensorReadout(Poll(), DateTime.Now, this));
        }
        
        protected double[] Poll()
        {
            int factor = _history.Count + 1;
            return new double[] { 0.1d*factor, 0.2d*factor, 0.3d*factor };
        }
        
        override public string ToString()
        {
            return string.Format("{0}[{1}]/{2}", _model, _pilot, _history.Count);
        }
    }
}


We will constrain ourselves to rather static data at the moment and add flexibility during the next chapters.


    5.1. Storing


    This should be familiar by now.

    // storeFirstCar
    Car car1 = new Car("Ferrari");
    Pilot pilot1 = new Pilot("Michael Schumacher", 100);
    car1.Pilot = pilot1;
    db.Store(car1);


    The second car will take two snapshots immediately at startup.

    // storeSecondCar
    Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
    Car car2 = new Car("BMW");
    car2.Pilot = pilot2;
    car2.Snapshot();
    car2.Snapshot();
    db.Store(car2);



    5.2. Retrieving



      5.2.1. QBE


      First let us verify that we indeed have taken snapshots.

      // retrieveAllSensorReadout
      IObjectSet result = db.QueryByExample(typeof(SensorReadout));
      ListResult(result);


      As a prototype for an array, we provide an array of the same type, containing only the values we expect the result to contain.

      // retrieveSensorReadoutQBE
      SensorReadout proto = new SensorReadout(new double[] { 0.3, 0.1 }, DateTime.MinValue, null);
      IObjectSet result = db.QueryByExample(proto);
      ListResult(result);


      Note that the actual position of the given elements in the prototype array is irrelevant.

      To retrieve a car by its stored sensor readouts, we install a history containing the sought-after values.

      // retrieveCarQBE
      SensorReadout protoReadout = new SensorReadout(new double[] { 0.6, 0.2 }, DateTime.MinValue, null);
      IList protoHistory = new ArrayList();
      protoHistory.Add(protoReadout);
      Car protoCar = new Car(null, protoHistory);
      IObjectSet result = db.QueryByExample(protoCar);
      ListResult(result);


      We can also query for the collections themselves, since they are first class objects.

      // retrieveCollections
      IObjectSet result = db.QueryByExample(new ArrayList());
      ListResult(result);


      This doesn't work with arrays, though.

      // retrieveArrays
      IObjectSet result = db.QueryByExample(new double[] { 0.6, 0.4 });
      ListResult(result);



      5.2.2. Native Queries


      If we want to use Native Queries to find SensorReadouts with matching values, we simply write this as if we would check every single instance:

      public class RetrieveSensorReadoutPredicate : Predicate
      {
          public bool Match(SensorReadout candidate)
          {
              return Array.IndexOf(candidate.Values, 0.3) > -1
                  && Array.IndexOf(candidate.Values, 0.1) > -1;
          }
      }

      // retrieveSensorReadoutNative
      IObjectSet results = db.Query(new RetrieveSensorReadoutPredicate());
      ListResult(results);


      And here's how we find Cars with matching readout values:

      public class RetrieveCarPredicate : Predicate
      {
          public bool Match(Car car)
          {
              foreach (SensorReadout sensor in car.History)
              {
                  if (Array.IndexOf(sensor.Values, 0.3) > -1
                      && Array.IndexOf(sensor.Values, 0.1) > -1)
                  {
                      return true;
                  }
              }
              return false;
          }
      }

      // retrieveCarNative
      IObjectSet results = db.Query(new RetrieveCarPredicate());
      ListResult(results);



      5.2.3. Query API


      Handling of arrays and collections is analogous to the previous example. First, lets retrieve only the SensorReadouts with specific values:

      // retrieveSensorReadoutQuery
      IQuery query = db.Query();
      query.Constrain(typeof(SensorReadout));
      IQuery valueQuery = query.Descend("_values");
      valueQuery.Constrain(0.3);
      valueQuery.Constrain(0.1);
      IObjectSet results = query.Execute();
      ListResult(results);


      Then let's get some Cars with matching Readout values:

      // retrieveCarQuery
      IQuery query = db.Query();
      query.Constrain(typeof(Car));
      IQuery historyQuery = query.Descend("_history");
      historyQuery.Constrain(typeof(SensorReadout));
      IQuery valueQuery = historyQuery.Descend("_values");
      valueQuery.Constrain(0.3);
      valueQuery.Constrain(0.1);
      IObjectSet results = query.Execute();
      ListResult(results);



    5.3. Updating and deleting


    This should be familiar, we just have to remember to take care of the update depth.

    // updateCarPart1
    Db4oFactory.Configure().ObjectClass(typeof(Car)).CascadeOnUpdate(true);


    // updateCarPart2
    IObjectSet result = db.QueryByExample(new Car("BMW", null));
    Car car = (Car)result.Next();
    car.Snapshot();
    db.Store(car);
    RetrieveAllSensorReadout(db);


    There's nothing special about deleting arrays and collections, too.

    Deleting an object from a collection is an update, too, of course.

    // updateCollection
    IQuery query = db.Query();
    query.Constrain(typeof(Car));
    IObjectSet result = query.Descend("_history").Execute();
    IList history = (IList)result.Next();
    history.RemoveAt(0);
    db.Store(history);
    Car proto = new Car(null, null);
    result = db.QueryByExample(proto);
    foreach (Car car in result)
    {    
        foreach (object readout in car.History)
        {
            Console.WriteLine(readout);
        }
    }


    (This example also shows that with db4o it is quite easy to access object internals we were never meant to see. Please keep this always in mind and be careful.)

    We will delete all cars from the database again to prepare for the next chapter.

    // deleteAllPart1
    Db4oFactory.Configure().ObjectClass(typeof(Car)).CascadeOnDelete(true);


    // deleteAllPart2
    IObjectSet result = db.QueryByExample(new Car(null, null));
    foreach (object car in result)
    {
        db.Delete(car);
    }
    IObjectSet readouts = db.QueryByExample(new SensorReadout(null, DateTime.MinValue, null));
    foreach (object readout in readouts)
    {
        db.Delete(readout);
    }



    5.4. Conclusion


    Ok, collections are just objects. But why did we have to specify the concrete ArrayList type all the way? Was that necessary? How does db4o handle inheritance? We will cover that in the next chapter.


    5.5. Full source


    using System;
    using System.Collections;
    using System.IO;
    using Db4objects.Db4o;
    using Db4objects.Db4o.Query;
    namespace Db4objects.Db4o.Tutorial.F1.Chapter3
    {    
        public class CollectionsExample : Util
        {
            public static void Main(string[] args)
            {
                File.Delete(Util.YapFileName);            
                IObjectContainer db = Db4oFactory.OpenFile(Util.YapFileName);
                try
                {
                    StoreFirstCar(db);
                    StoreSecondCar(db);
                    RetrieveAllSensorReadout(db);
                    RetrieveSensorReadoutQBE(db);
                    RetrieveCarQBE(db);
                    RetrieveCollections(db);
                    RetrieveArrays(db);
                    RetrieveSensorReadoutQuery(db);
                    RetrieveCarQuery(db);
                    db.Close();
                    UpdateCarPart1();
                    db = Db4oFactory.OpenFile(Util.YapFileName);
                    UpdateCarPart2(db);
                    UpdateCollection(db);
                    db.Close();
                    DeleteAllPart1();
                    db=Db4oFactory.OpenFile(Util.YapFileName);
                    DeleteAllPart2(db);
                    RetrieveAllSensorReadout(db);
                }
                finally
                {
                    db.Close();
                }
            }
            
            public static void StoreFirstCar(IObjectContainer db)
            {
                Car car1 = new Car("Ferrari");
                Pilot pilot1 = new Pilot("Michael Schumacher", 100);
                car1.Pilot = pilot1;
                db.Store(car1);
            }
            
            public static void StoreSecondCar(IObjectContainer db)
            {
                Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
                Car car2 = new Car("BMW");
                car2.Pilot = pilot2;
                car2.Snapshot();
                car2.Snapshot();
                db.Store(car2);       
            }
            
            public static void RetrieveAllSensorReadout(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(typeof(SensorReadout));
                ListResult(result);
            }
            
            public static void RetrieveSensorReadoutQBE(IObjectContainer db)
            {
                SensorReadout proto = new SensorReadout(new double[] { 0.3, 0.1 }, DateTime.MinValue, null);
                IObjectSet result = db.QueryByExample(proto);
                ListResult(result);
            }
            
            public static void RetrieveCarQBE(IObjectContainer db)
            {
                SensorReadout protoReadout = new SensorReadout(new double[] { 0.6, 0.2 }, DateTime.MinValue, null);
                IList protoHistory = new ArrayList();
                protoHistory.Add(protoReadout);
                Car protoCar = new Car(null, protoHistory);
                IObjectSet result = db.QueryByExample(protoCar);
                ListResult(result);
            }
            
            public static void RetrieveCollections(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(new ArrayList());
                ListResult(result);
            }
            
            public static void RetrieveArrays(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(new double[] { 0.6, 0.4 });
                ListResult(result);
            }
            
            public static void RetrieveSensorReadoutQuery(IObjectContainer db)
            {
                IQuery query = db.Query();
                query.Constrain(typeof(SensorReadout));
                IQuery valueQuery = query.Descend("_values");
                valueQuery.Constrain(0.3);
                valueQuery.Constrain(0.1);
                IObjectSet results = query.Execute();
                ListResult(results);
            }
            
            public static void RetrieveCarQuery(IObjectContainer db)
            {
                IQuery query = db.Query();
                query.Constrain(typeof(Car));
                IQuery historyQuery = query.Descend("_history");
                historyQuery.Constrain(typeof(SensorReadout));
                IQuery valueQuery = historyQuery.Descend("_values");
                valueQuery.Constrain(0.3);
                valueQuery.Constrain(0.1);
                IObjectSet results = query.Execute();
                ListResult(results);
            }
            public class RetrieveSensorReadoutPredicate : Predicate
            {
                public bool Match(SensorReadout candidate)
                {
                    return Array.IndexOf(candidate.Values, 0.3) > -1
                        && Array.IndexOf(candidate.Values, 0.1) > -1;
                }
            }
            
            public static void RetrieveSensorReadoutNative(IObjectContainer db)
            {
                IObjectSet results = db.Query(new RetrieveSensorReadoutPredicate());
                ListResult(results);
            }
            public class RetrieveCarPredicate : Predicate
            {
                public bool Match(Car car)
                {
                    foreach (SensorReadout sensor in car.History)
                    {
                        if (Array.IndexOf(sensor.Values, 0.3) > -1
                            && Array.IndexOf(sensor.Values, 0.1) > -1)
                        {
                            return true;
                        }
                    }
                    return false;
                }
            }
            public static void RetrieveCarNative(IObjectContainer db)
            {
                IObjectSet results = db.Query(new RetrieveCarPredicate());
                ListResult(results);
            }
            public static void UpdateCarPart1()
            {
                Db4oFactory.Configure().ObjectClass(typeof(Car)).CascadeOnUpdate(true);
            }
            
            public static void UpdateCarPart2(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(new Car("BMW", null));
                Car car = (Car)result.Next();
                car.Snapshot();
                db.Store(car);
                RetrieveAllSensorReadout(db);
            }
            
            public static void UpdateCollection(IObjectContainer db)
            {
                IQuery query = db.Query();
                query.Constrain(typeof(Car));
                IObjectSet result = query.Descend("_history").Execute();
                IList history = (IList)result.Next();
                history.RemoveAt(0);
                db.Store(history);
                Car proto = new Car(null, null);
                result = db.QueryByExample(proto);
                foreach (Car car in result)
                {    
                    foreach (object readout in car.History)
                    {
                        Console.WriteLine(readout);
                    }
                }
            }
            
            public static void DeleteAllPart1()
            {
                Db4oFactory.Configure().ObjectClass(typeof(Car)).CascadeOnDelete(true);
            }
            public static void DeleteAllPart2(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(new Car(null, null));
                foreach (object car in result)
                {
                    db.Delete(car);
                }
                IObjectSet readouts = db.QueryByExample(new SensorReadout(null, DateTime.MinValue, null));
                foreach (object readout in readouts)
                {
                    db.Delete(readout);
                }
            }
        }
    }




    www.db4o.com