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

Translate hstore access to SQL #212

Open
davidkvc opened this issue Jul 13, 2017 · 8 comments · May be fixed by #3285
Open

Translate hstore access to SQL #212

davidkvc opened this issue Jul 13, 2017 · 8 comments · May be fixed by #3285
Labels
enhancement New feature or request good first issue Good for newcomers
Milestone

Comments

@davidkvc
Copy link

Is there a way to query hstore columns using LINQ ?
I would expect something like this to work:

public class Product
{
   public int Id { get; set; }
   //This is correctly set as hstore in OnModelCreating
   public Dictionary<string, string> Tags { get; set; } 
}
ApplicationDbContext db = ...

var prods = db.Products.Where(p => p.Tags["Variant"].Equals("SD")).ToList();

However that WHERE statement is evaluated at client and I want it to be evaluated in db server.
I expect something like this to be generated
select * from "Products" where "Tags" -> 'Variant' = 'SD'

This works so everything is configured correctly.
var prods = db.Products.FromSql("select * from \"Products\" where \"Tags\" -> 'Variant' = 'SD'").ToList();

@roji
Copy link
Member

roji commented Jul 13, 2017

It's definitely possible to perform this kind of translation to SQL, but this isn't currently supported. Since the translation doesn't exist at the moment, the above code will trigger client evaluation - EF Core will select everything and perform the Where() client-side.

I did something similar for translating basic .NET Regex functionality to PostgreSQL regex operators (see #6), also see #4 which would translate JSON operators.

I'll keep this open as a feature request.

@roji roji changed the title Can I query hstore using LINQ Translate hstore access to SQL Jul 13, 2017
@roji roji added the enhancement New feature or request label Jul 13, 2017
@davidkvc
Copy link
Author

Would this be difficult to implement ? I have never done anything like this but I would like to look into it. Could you point me in some direction ?

@roji
Copy link
Member

roji commented Jul 14, 2017

@DavizOWNS while translating expressions to SQL is usually simple (and even fun), unfortunately this case doesn't involve simple method call translation (like, say, regular expressions) but the indexing operator.

A while back I did some work in a branch on #120, which involves something similar but for an array. Let me bring that branch up to date and see if I can even merge it (or some of it) for 2.0.0. After that I'll come back here and try to give some guidance.

@roji
Copy link
Member

roji commented Jul 14, 2017

@DavizOWNS you may want to take a look at dotnet/efcore#9183, which is me asking for a review from the EF Core team on work I did to translate array operations (including subscripting). You would probably do something not too far off, so you may want to look at my work, and let's wait for their feedback.

@roji
Copy link
Member

roji commented Jul 22, 2017

FYI the array operation translation work has been merged for 2.0.0. You may wish to take a look at b79fd0c to see how that was done and start working from there.

@davidkvc
Copy link
Author

davidkvc commented Sep 1, 2017

Created PR #240 with my first attempt at this.

@roji roji added this to the Backlog milestone Jun 9, 2018
@roji roji added the good first issue Good for newcomers label Jul 22, 2019
@rafaelvascc
Copy link

rafaelvascc commented Sep 26, 2019

Just dropping by my solution in case anyone else is struggling with this.
First, I created the following function on my database:

CREATE OR REPLACE FUNCTION public.hstore_has_key_value(IN "@hstore" hstore, IN "@key" text, IN "@operator" text, IN "@value" text)
    RETURNS boolean
	AS $func$
	SELECT
		CASE 
			WHEN "@operator" = '=' THEN "@hstore" -> "@key" = "@value"
			WHEN "@operator" = '<>' THEN "@hstore" -> "@key" <> "@value" 
			WHEN "@operator" = '>' THEN "@hstore" -> "@key" > "@value" 
			WHEN "@operator" = '>=' THEN "@hstore" -> "@key" >= "@value" 
			WHEN "@operator" = '<' THEN "@hstore" -> "@key" < "@value" 
			WHEN "@operator" = '<=' THEN "@hstore" -> "@key" <= "@value"
			WHEN "@operator" = 'LIKE' THEN "@hstore" -> "@key" LIKE "@value"
			ELSE "@hstore" -> "@key" = "@value"
		END
$func$ LANGUAGE sql;

Then a dummy DbFunction on my C# code:

public static class DbFunctionsExtensions
{
    public static bool HasKeyValue(Dictionary<string, string> dic, string key, string @operator, string value)
    {
        throw new NotImplementedException("For use only as an EF core Db function");
    }
}

Configured the mapping between the functions on the DbContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //other stuff

    modelBuilder.HasDbFunction(typeof(DbFunctionsExtensions).GetMethods()
        .Where(m =>
        {
            return m.Name == "HasKeyValue";
        })
        .SingleOrDefault())
        .HasName("public.hstore_has_key_value");
}

Use it and be happy:

var result = dbContext.Entites.AsNoTracking().Where(r => DbFunctionsExtensions.HasKeyValue(r.Dictionary, "someKey", "=", "someValue")).FirstOrDefault();

yinzara added a commit to yinzara/efcore.pg that referenced this issue Sep 19, 2024
@yinzara yinzara linked a pull request Sep 19, 2024 that will close this issue
yinzara added a commit to yinzara/efcore.pg that referenced this issue Sep 19, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Sep 19, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Sep 19, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Sep 19, 2024
@yinzara
Copy link

yinzara commented Sep 19, 2024

I have implemented full support for hstore based querying. I know it has been a little while :-)

You can now do all the above queries you've suggested as well as the following methods are all supported:

  • Dictionary<string, string>.Item (i.e. myDictionary["key"])
  • Dictionary<string, string>.Item
  • Dictionary<string, string>.ContainsKey
  • ImmutableDictionary<string, string>.ContainsKey
  • Dictionary<string, string>.ContainsValue
  • ImmutableDictionary<string, string>.ContainsValue
  • Dictionary<string, string>.Count
  • ImmutableDictionary<string, string>.Count
  • ImmutableDictionary<string, string>.IsEmpty
  • Enumerable.Any

If you're going to use the Where(myDictionary => myDictionary["key"] == myValue), you should probably do a Where(myDictionary => myDictionary.ContainsKey("key") && myDictionary["key"] == myValue) instead. Since in .NET accessing the item at an index throws an exception, however technically when this evaluates on the SQL server, because PostgreSQL does not, it won't actually throw an exception.

yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 4, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 5, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 9, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
yinzara added a commit to yinzara/efcore.pg that referenced this issue Oct 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants