Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

Edge Data Store

Indexes in ​.NET framework

  • Last UpdatedSep 25, 2025
  • 4 minute read

The following examples are in csharp (C#), but you can apply the concepts such as simple, compound, and secondary indexes to any language. For more information on indexes in JavaScript and Python, see Indexes outside of .NET framework.

Simple indexes

When working in .NET, use the SdsTypeBuilder together with either the OSIsoft.Sds.SdsMemberAttribute (preferred) or the System.ComponentModel.DataAnnotations.KeyAttribute to identify the property that defines the simple index. Using the SdsTypeBuilder eliminates potential errors that might occur when working with SdsTypes manually.

public enum State

{

Ok,

Warning,

Alarm

}

public class Simple

{

[SdsMember(IsKey = true, Order = 0) ]

public DateTime Time { get; set; }

public State State { get; set; }

public Double Measurement { get; set; }

}

SdsType simpleType = SdsTypeBuilder.CreateSdsType<Simple>();

To read data that is located between two indexes, define both a start index and an end index. For DateTime, use the ISO 8601 representation of dates and times. For example, to query for a window of simple values between January 1, 2010 and February 1, 2010, you can define indexes and query as follows:

IEnumerable<Simple> values = await client.GetWindowValuesAsync<Simple>(simpleStream.Id, "2010-01-01T08:00:00.000Z","2010-02-01T08:00:00.000Z");

For more information about querying data, see Read data.

Secondary indexes

Secondary indexes are defined at the stream level. To add indexes to a stream, add them to the stream Indexes field. For example, to add a second index on Measurement, use the following code:

SdsStreamIndex measurementIndex = new SdsStreamIndex()

{

SdsTypePropertyId = simpleType.Properties.First(p => p.Id.Equals("Measurement")).Id

};

SdsStream secondary = new SdsStream()

{

Id = "Simple with Secondary",

TypeId = simpleType.Id,

Indexes = new List<SdsStreamIndex>()

{

measurementIndex

}

};

secondary = await config.GetOrCreateStreamAsync(secondary);

To read data indexed by a secondary index, use a filtered GET method(IEnumerable<Simple> orderedBySecondary = await client.GetFilteredValuesAsync<Simple>(secondary.Id, "Measurement gt 0 and Measurement lt 6");).

Use indexes to order data. On a stream level, you can set the property to be the secondary index. To improve performance when working with a large set of data:

  • Ensure that the property is a secondary index.

  • Use Filter expressions for filtering.

    await client.UpdateValuesAsync<Simple>(secondary.Id, new List<Simple>()

    {

    new Simple()

    {

    Time = time,

    State = State.Ok,

    Measurement = 5

    },

    new Simple()

    {

    Time = time + TimeSpan.FromSeconds(1),

    State = State.Ok,

    Measurement = 4

    },

    new Simple()

    {

    Time = time + TimeSpan.FromSeconds(2),

    State = State.Ok,

    Measurement = 3

    },

    new Simple()

    {

    Time = time + TimeSpan.FromSeconds(3),

    State = State.Ok,

    Measurement = 2

    },

    new Simple()

    {

    Time = time + TimeSpan.FromSeconds(4),

    State = State.Ok,

    Measurement = 1

    },

    });

    IEnumerable<Simple> orderedByKey = await client.GetWindowValuesAsync<Simple>(secondary.Id,

    time.ToString("o"), time.AddSeconds(4).ToString("o"));

    foreach (Simple value in orderedByKey)

    Console.WriteLine("{0}: {1}", value.Time, value.Measurement);

    Console.WriteLine();

    IEnumerable<Simple> orderedBySecondary = await client.GetFilteredValuesAsync<Simple>(secondary.Id,

    "Measurement gt 0 and Measurement lt 6");

    foreach (Simple value in orderedBySecondary)

    Console.WriteLine("{0}: {1}", value.Time, value.Measurement);

    Console.WriteLine();

    // Output:

    // 1/20/2017 12:00:00 AM: 5

    // 1/20/2017 12:00:01 AM: 4

    // 1/20/2017 12:00:02 AM: 3

    // 1/20/2017 12:00:03 AM: 2

    // 1/20/2017 12:00:04 AM: 1

    //

    // 1/20/2017 12:00:04 PM: 1

    // 1/20/2017 12:00:03 PM: 2

    // 1/20/2017 12:00:02 PM: 3

    // 1/20/2017 12:00:01 PM: 4

    // 1/20/2017 12:00:00 PM: 5

Compound indexes

Compound indexes are defined using the SdsMemberAttribute as follows:

public class Simple

{

[SdsMember(IsKey = true, Order = 0)]

public DateTime Time { get; set; }

public State State { get; set; }

public Double Measurement { get; set; }

}

public class DerivedCompoundIndex : Simple

{

[SdsMember(IsKey = true, Order = 1)]

public DateTime Recorded { get; set; }

}

Events of type DerivedCompoundIndex are sorted first by the Time parameter and then by the Recorded parameter. A collection of times is sorted as follows:

Time

Recorded

Measurement

01:00

00:00

0

01:00

01:00

2

01:00

14:00

5

02:00

00:00

1

02:00

01:00

3

02:00

02:00

4

02:00

14:00

6

If the Order parameter was reversed, with Recorded set to 0 and Time set to 1, the results are sorted as follows:

Time

Recorded

Measurement

01:00

00:00

0

02:00

00:00

1

01:00

01:00

2

02:00

01:00

3

02:00

02:00

4

01:00

14:00

5

02:00

14:00

6

// estimates at 1/20/2017 00:00

await client.UpdateValuesAsync(compoundStream.Id, new List<DerivedCompoundIndex>()

{

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 01:00"),

Recorded = DateTime.Parse("1/20/2017 00:00"),

State = State.Ok,

Measurement = 0

},

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 02:00"),

Recorded = DateTime.Parse("1/20/2017 00:00"),

State = State.Ok,

Measurement = 1

},

});

// measure and estimates at 1/20/2017 01:00

await client.UpdateValuesAsync(compoundStream.Id, new List<DerivedCompoundIndex>()

{

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 01:00"),

Recorded = DateTime.Parse("1/20/2017 01:00"),

State = State.Ok,

Measurement = 2

},

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 02:00"),

Recorded = DateTime.Parse("1/20/2017 01:00"),

State = State.Ok,

Measurement = 3

},

});

// measure at 1/20/2017 02:00

await client.UpdateValuesAsync(compoundStream.Id, new List<DerivedCompoundIndex>()

{

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 02:00"),

Recorded = DateTime.Parse("1/20/2017 02:00"),

State = State.Ok,

Measurement = 4

},

});

// adjust earlier values at 1/20/2017 14:00

await client.UpdateValuesAsync(compoundStream.Id, new List<DerivedCompoundIndex>()

{

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 01:00"),

Recorded = DateTime.Parse("1/20/2017 14:00"),

State = State.Ok,

Measurement = 5

},

new DerivedCompoundIndex()

{

Time = DateTime.Parse("1/20/2017 02:00"),

Recorded = DateTime.Parse("1/20/2017 14:00"),

State = State.Ok,

Measurement = 6

},

});

var from = new Tuple<DateTime, DateTime>(DateTime.Parse("1/20/2017 01:00"), DateTime.Parse("1/20/2017 00:00"));

var to = new Tuple<DateTime, DateTime>(DateTime.Parse("1/20/2017 02:00"), DateTime.Parse("1/20/2017 14:00"));

var compoundValues = await client.GetWindowValuesAsync<DerivedCompoundIndex, DateTime, DateTime>(compoundStream.Id, from, to);

foreach (DerivedCompoundIndex value in compoundValues)

Console.WriteLine("{0}:{1} {2}", value.Time, value.Recorded, value.Measurement);

// Output:

// 1/20/2017 1:00:00 AM:1/20/2017 12:00:00 AM 0

// 1/20/2017 1:00:00 AM:1/20/2017 1:00:00 AM 2

// 1/20/2017 1:00:00 AM:1/20/2017 2:00:00 PM 5

// 1/20/2017 2:00:00 AM:1/20/2017 12:00:00 AM 1

// 1/20/2017 2:00:00 AM:1/20/2017 1:00:00 AM 3

// 1/20/2017 2:00:00 AM:1/20/2017 2:00:00 AM 4

// 1/20/2017 2:00:00 AM:1/20/2017 2:00:00 PM 6

Note: The GetWindowValuesAsync() call specifies an expected return type and the index types as generic parameters.

In This Topic
Related Links
TitleResults for “How to create a CRG?”Also Available in