Git Product home page Git Product logo

Comments (6)

DvirDukhan avatar DvirDukhan commented on June 13, 2024 1

@shaunsales great input, as always.

Let's iterate over the design as I would like to have the IEnumerable also implemented. This will cause it to be an implementation of IReadOnlyList
for an example

using System;
using System.Collections;
using System.Collections.Generic;

namespace NRedisTimeSeries.DataTypes
{
    public class TimeSeriesCollection : IReadOnlyList<(long, double)>
    {
        public TimeSeriesCollection()
        {
        }

        public (long, double) this[int index] => throw new NotImplementedException();

        public int Count => throw new NotImplementedException();

        public IEnumerator<(long, double)> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }
}

What do you think?
Thanks for your inputs

from nredistimeseries.

shaunsales avatar shaunsales commented on June 13, 2024 1

Here's a first cut of a timeseries collection class that I think would meet our requirements;


public class TsCollection : IEnumerator<(long TimeStamp, double Value)>
{
	private int _index = -1;

	private RedisResult[] _redisResults;

	public TsCollection(RedisResult redisResult)
	{
		_redisResults = (RedisResult[])redisResult;
	}

	public (long TimeStamp, double Value) this[int index]
	{
		get
		{
			var item = (RedisResult[])_redisResults[index];
			return ((long)item[0], (double)item[1]);
		}
	}
	
	public int Count => _redisResults.Length;

	public IEnumerator<(long TimeStamp, double Value)> GetEnumerator() => this;

	public (long TimeStamp, double Value) Current => (_index > -1 && _index <= _redisResults.Length) ? this[_index] : throw new IndexOutOfRangeException();

	object IEnumerator.Current => Current;

	public bool MoveNext() => ++_index < _redisResults.Length;

	public void Dispose() => Reset();

	public void Reset() => _index = -1;
}

I've not implemented it as a readonly or immutable collection as that adds quite a bit of overhead that I didn't feel was necessary. If there's a good reason to make the collection immutable, I suggest we use ImmutableArray<T> or ImmutableList<T> but given the underlying RedisResult[] is not immutable or readonly it seems better to follow the same pattern.

from nredistimeseries.

DvirDukhan avatar DvirDukhan commented on June 13, 2024

@shaunsales
Let's split the logic between the enumerator and the direct access functionality
something like

using System;
using System.Collections;
using System.Collections.Generic;
using StackExchange.Redis;

namespace NRedisTimeSeries.DataTypes
{

    public class TsCollection : IReadOnlyList<(long TimeStamp, double Value)>
    {

        private static (long, double) ResultAsTuple(RedisResult result)
        {
            var item = (RedisResult[])result;
            return ((long)item[0], (double)item[1]);
        }

        private class TsCollectionEnumerator : IEnumerator<(long TimeStamp, double Value)>
        {
            private int _index = -1;
            RedisResult[] _results;
            public TsCollectionEnumerator(TsCollection collection)
            {
                _results = collection._redisResults;
            }

            public (long TimeStamp, double Value) Current => (_index > -1 && _index <= _results.Length) ? ResultAsTuple(_results[_index]) : throw new IndexOutOfRangeException();

            object IEnumerator.Current => Current;

            public bool MoveNext() => ++_index < _results.Length;

            public void Reset() => _index = -1;

            public void Dispose() => Reset();

        }

        private RedisResult[] _redisResults;

        public TsCollection(RedisResult redisResult)
        {
            _redisResults = (RedisResult[])redisResult;
        }

        public (long, double) this[int index]
        {
            get => ResultAsTuple(_redisResults[index]);
 
        }

        public int Count => _redisResults.Length;

        public IEnumerator<(long, double)> GetEnumerator() => new TsCollectionEnumerator(this);

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}

WDYT?

from nredistimeseries.

shaunsales avatar shaunsales commented on June 13, 2024

Looking good. I've made a few (quick and dirty) updates to include MRANGE as a collection of ranges with Name and Label support.

public class TsMRangeResults : IReadOnlyList<TsMRangeResult>
{
	private class TsMRangeEnumerator : IEnumerator<TsMRangeResult>
	{
		private int _index = -1;

		private IReadOnlyList<TsMRangeResult> _tsMRangeResults;

		public TsMRangeEnumerator(TsMRangeResults collection) => _tsMRangeResults = collection._tsMRangeResults;

		public TsMRangeResult Current => (_index > -1 && _index <= _tsMRangeResults.Count) ? _tsMRangeResults[_index] : throw new IndexOutOfRangeException();

		object IEnumerator.Current => Current;

		public bool MoveNext() => ++_index < _tsMRangeResults.Count;

		public void Reset() => _index = -1;

		public void Dispose() => Reset();
	}
	
	private IReadOnlyList<TsMRangeResult> _tsMRangeResults;

	public int Count { get; }

	public TsMRangeResults(RedisResult redisResult)
	{
		var redisResults = (RedisResult[])redisResult;

		Count = redisResults.Length;

		if (redisResults.Length > 0)
		{
			var list = new List<UserQuery.TsMRangeResult>(redisResults.Length);
			for (int i = 0; i < redisResults.Length; i++)
			{
				list.Add(new TsMRangeResult(redisResults[i]));
			}
			_tsMRangeResults = list;
		}
	}

	public TsMRangeResult this[int index] => _tsMRangeResults[index];

	public IEnumerator<TsMRangeResult> GetEnumerator() => new TsMRangeEnumerator(this);

	IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class TsMRangeResult : TsRangeResult
{
	public string Name { get; }

	public IList<(string, string)> Labels { get; }

	public TsMRangeResult(RedisResult redisResult) : base((RedisResult[])redisResult)
	{
		var redisResults = (RedisResult[])redisResult;

		Name = (string)redisResults[0];

		var labels = (RedisResult[])redisResults[1];

		if (labels.Length > 0)
		{
			Labels = new List<(string, string)>(labels.Length);

			for (int i = 0; i < labels.Length; i++)
			{
				var labelValue = (RedisResult[])labels[i];
				Labels.Add(((string)labelValue[0], (string)labelValue[1]));
			}
		}
	}
}

public class TsRangeResult : IReadOnlyList<(long TimeStamp, double Value)>
{
	private static (long TimeStamp, double Value) ResultAsTuple(RedisResult result)
	{
		var item = (RedisResult[])result;
		return ((long)item[0], (double)item[1]);
	}

	private class TsRangeResultEnumerator : IEnumerator<(long TimeStamp, double Value)>
	{
		private int _index = -1;

		RedisResult[] _redisResults;

		public TsRangeResultEnumerator(TsRangeResult collection) => _redisResults = collection._redisResults;

		public (long TimeStamp, double Value) Current => (_index > -1 && _index <= _redisResults.Length) ? ResultAsTuple(_redisResults[_index]) : throw new IndexOutOfRangeException();

		object IEnumerator.Current => Current;

		public bool MoveNext() => ++_index < _redisResults.Length;

		public void Reset() => _index = -1;

		public void Dispose() => Reset();
	}

	private RedisResult[] _redisResults;

	public int Count { get; }

	public TsRangeResult(RedisResult redisResult)
	{
		// TODO: Add some data shape checks
		_redisResults = (RedisResult[])redisResult;
		
		Count = _redisResults.Length;
	}

	protected TsRangeResult(RedisResult[] redisResults)
	{
		_redisResults = (RedisResult[])redisResults[2];
	}

	public (long TimeStamp, double Value) this[int index] => ResultAsTuple(_redisResults[index]);

	public IEnumerator<(long TimeStamp, double Value)> GetEnumerator() => new TsRangeResultEnumerator(this);

	IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

This will allow users to access the timeseries results with fairly simply syntax. Here's a couple of examples;

var tsMRangeResults = new TsMRangeResults(db.Execute("TS.MRANGE", new[] { "-", "+", "WITHLABELS", "FILTER", "lbl=abc" }));

foreach (var mRange in tsMRangeResults)
{
	foreach (var item in mRange)
	{
		$"{mRange.Name} {item.TimeStamp}:{item.Value} {mRange.Labels.Count}".Dump();
	}
}

var tsRange = new TsRangeResult(db.Execute("TS.RANGE", new[] { key, "-", "+" }));

foreach (var item in tsRange)
{
	$"{item.TimeStamp}:{item.Value}".Dump();
}

from nredistimeseries.

DvirDukhan avatar DvirDukhan commented on June 13, 2024

Nice approach
I think that you can apply the iterator approach everywhere here so you will not have to allocate lists at all
Let's first finish with PR #35 and continue with this

from nredistimeseries.

shaunsales avatar shaunsales commented on June 13, 2024

Nice approach

I think that you can apply the iterator approach everywhere here so you will not have to allocate lists at all

Let's first finish with PR #35 and continue with this

Agreed - the labels implementation was a bit quick and dirty. I think we can optimise and improve this once we turn it into a PR.

from nredistimeseries.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.