Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Set commands #7

Closed
gozeloglu opened this issue Apr 15, 2024 · 8 comments
Closed

Adding Set commands #7

gozeloglu opened this issue Apr 15, 2024 · 8 comments

Comments

@gozeloglu
Copy link

Hi @nalgeon.

I just discovered your project and loved it. I want to contribute to the project by adding the set commands you listed in README: https://github.com/nalgeon/redka?tab=readme-ov-file#sets. What do you think? Is it okay to implement from me? I can implement SADD, SCARD, SDIFF, SMEMBER, SPOP, and SREM. Or, maybe all of them 😃

@nalgeon
Copy link
Owner

nalgeon commented Apr 16, 2024

Hi Gökhan, thank you for your interest in Redka! I think it's a great idea :)

I suggest we start by deciding on SQL schema changes and method signatures right here in the issue. That way we reduce the risk of further debate in the PR phase. Once we agree on schema and API, you can move on to implementation.

@gozeloglu
Copy link
Author

Hi @nalgeon,

I understand your point. You are right. Let me introduce the structure for Set commands. We can discuss and enhance them.

SADD

func (d *DB) Sadd(key string, members ...any) (int, error) {}

We return an error if len(members) == 0. Or, as an alternative approach, we can implement as follows:

func (d *DB) Sadd(key string, member any, members ...any) (int, error) {}

In that way, we guarantee that there will be at least 1 member.

SCARD

// Scard returns number of elements of the set stored at key.
func (d *DB) Scard(key string) int {}

SDIFF

func (d *DB) Sdiff(key string, keys ...string) []any {}

SMEMBER

func (d *DB) Smember(key string) []any {}

SPOP

func (d *DB) Spop(key string, count int) ([]any, error) {}

SREM

func (d *DB) Srem(key string, member any, members ...any) (int, error) {}

SQL Schema

create table if not exists
rset (
    key_id integer not null,
    member text not null,  -- this can be blob as well. I am not sure

    foreign key (key_id) references rkey (id)
	  on delete cascade
);

I am open to discuss the method signatures and SQL Schama. In SQL part, there might be missing parts because I had limited time to create this draft. Also, I can define method signatures for rest of the commands. I took only the commands I mentioned above.

@nalgeon
Copy link
Owner

nalgeon commented Apr 17, 2024

I think it's better to start with a limited-scope PR. I suggest implementing SADD, SREM, and SISMEMBER. And only at the Go API level (without touching the command package, which may be the scope of the next PR).

I think the schema is fine (with a blob). However, you also need a unique index and a view.

As for method signatures, there is no need for the S prefix, since they are implemented in a separate package rset. Also, it's better to keep the naming consistent, so I'd go with the following:

Add(key string, elems ...any) (int, error)
Delete(key string, elems ...any) (int, error)
Exists(key string, elem any) (bool, error)

If you agree with the above, could you please also write SQL queries for the three methods mentioned?

@gozeloglu
Copy link
Author

I agree that starting with limited commands is a good way. Function/method signatures look fine except for naming. Our command names are Add, Rem(Remove), and IsMember. However, you suggested Delete and Exists instead of real names. I think choosing real command names will be more appropriate for naming convention and consistent naming scope. That's why I suggest using Remove and IsMember as function names. I know I need to write SQL queries for creating unique index and view. Please give me a little more time to create queries since I am a little bit busy.

@nalgeon
Copy link
Owner

nalgeon commented Apr 18, 2024

Sorry, I'm not ready to go with Rem and IsMember. Redis naming is really inconsistent, the same things are called differently all the time (Exists/IsMember, Card/Len/Count, Del/Rem etc). I'm sure Salvatore had his reasons for doing this, and of course I will keep the command names for the RESP API. But I don't want this inconsistency in the Go API.

@gozeloglu
Copy link
Author

What do you think about the SQL queries?

create table if not exists
    rset (
             key_id integer not null,
             member blob not null,

             foreign key (key_id) references rkey (id)
    on delete cascade
    );

create unique index if not exists
rset_pk_idx on rset (key_id, member);

create index if not exists
rset_key_id_idx on rset (key_id);

create view if not exists
vset as
    select
        rkey.id as key_id, rkey.key, rset.member
        datetime(etime/1000, 'unixepoch') as etime,
        datetime(mtime/1000, 'unixepoch') as mtime
    from rkey join rset on rkey.id = rset.key_id
    where rkey.type = 3
        and (rkey.etime is null or rkey.etime > unixepoch('subsec'))

@nalgeon
Copy link
Owner

nalgeon commented Apr 20, 2024

I think it's mostly fine. The rset_key_id_idx is unnecessary, and member is better called elem, so it'll probably be like this:

create table if not exists
rset (
    key_id integer not null,
    elem blob not null,

    foreign key (key_id) references rkey (id)
      on delete cascade
);

create unique index if not exists
rset_pk_idx on rset (key_id, elem);

create view if not exists
vset as
select
    rkey.id as key_id, rkey.key, rset.elem
    datetime(etime/1000, 'unixepoch') as etime,
    datetime(mtime/1000, 'unixepoch') as mtime
from rkey join rset on rkey.id = rset.key_id
where rkey.type = 3
    and (rkey.etime is null or rkey.etime > unixepoch('subsec'))

@nalgeon
Copy link
Owner

nalgeon commented May 5, 2024

Okay, this didn't work out. I'm closing the issue and the associated PR.

@nalgeon nalgeon closed this as not planned Won't fix, can't repro, duplicate, stale May 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants