ninjanye / searchextensions Goto Github PK
View Code? Open in Web Editor NEWLibrary of IQueryable extension methods to perform searching
License: MIT License
Library of IQueryable extension methods to perform searching
License: MIT License
Hi!
Lets say that you have a customers which have 0 to n orders.
You would construct the query using EF something like this:
query = dbContext.Customers.AsQueryable();
.
.
.
query = query.Search(customer => customer.Firstname).ContainingAll(filter.FirstNames);
.
.
.
And then lastly you would add the include for each customer's orders like so:
query = query.Include(customer => customer.Orders);
However this does nothing if you are using SearchExtensions. It does work if you are using just IQueryable (I.E. Entity Framework) or if you add the desired includes before using any SearchExtensions -methods
this is more of a feature request. We are using quite widely used library called EntityFramework.Plus https://entityframework-plus.net/
however it seems that if I use query generation by SearchExtensions -> the resulting object query is not compatible with EF.Plus
I wonder if it would be possible to provide a compatible object query -> here is the impacted EF.Plus code.
Also an email from the author I got in regards to this issue:
We support multiple ways to retrieve the object query but it look that this library doesn’t provide it in one of the support ways we offer: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/src/shared/Z.EF.Plus._Core.Shared/EF5_EF6/IQueryable%60/IQueryable%60.GetObjectQuery.cs
Add a search option to Containing()
and ContainingAll()
to allow for matching on whole words only
Given "I like coffee"
is present in a string
When searching for "fee" with WholeWord
search options selected
Then no data is returned
Given "I like coffee"
is present in a string
When searching for "coffee" with WholeWord
search options selected
Then data is returned
Hi, your libary is awesome, really helped me out!
So, given the following example:
query = query.Search(x => x.Code, x => x.Name, x => x.Brand) .Containing(stringArray);
How would I do to get the results filtered by the multiple strings in the stringArray
? Currently I see the result is additive. Or is this a feature you're still working on? I cloned your code but I don't have much experience with LINQ expressions, so i've been kind of lost trying to tweak it.
To clarify what i mean, lets say that for the two properties Name and Brand I have the following table:
1 | doggy | Nutram
2 | ring | Nutram
If the containg has params ("nutram", "ring") I should only get back result #2....
Thanks, appreciate any feedback
Hi,
By not specifying properties in Search() all the properties are searched for the search term. I have a List of objects with a lot of properties and searching works fine! But now I want to exclude 2 of them. As far as I can see the only way to achieve this is to specify all the other properties.
Is there a way to specify which properties not to search? Thus, all of them, except...
Kind regards,
Kees Alderliesten
Hi, I have just started using your extension and let me tell you. Its working perfectly. You saved lots of my time. I have unique requirement and see if this can be achieve using your extension.
I want to do OR on same property but with different options like "StartsWith", "EndsWtih" etc.
for Example.
var result = queryableData.Search(x => x.Property1)
.StartsWith("abc")
.Containing("mno");
On above code get all property1 where its either startwith "abc" or contains "mno".
I think current implementation does AND. Is there any way i can achieve this?
Thanks
We're getting an InvalidOperationException
when used with ef core and ToListAsync
System.InvalidOperationException: The source IQueryable doesn't implement IAsyncEnumerable<...>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsAsyncEnumerable[TSource](IQueryable`1 source)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at ...(String searchText) in ...
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
e.g.
// .csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.3" />
<PackageReference Include="NinjaNye.SearchExtensions" Version="3.0.1" />
</ItemGroup>
</Project>
// Program.cs
using Microsoft.EntityFrameworkCore;
using NinjaNye.SearchExtensions;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var ctx = new Context(new DbContextOptionsBuilder().UseSqlServer(@"server=(localdb)\mssqllocaldb;database=db").Options);
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
ctx.Foo.Search(f => f.Bar).Containing("").ToList();
await ctx.Foo.Search(f => f.Bar).Containing("").ToListAsync(); // throws exception
}
}
public class Context : DbContext
{
public DbSet<Foo> Foo { get; set; }
public Context(DbContextOptions options) : base(options) { }
}
public class Foo
{
public int FooId { get; set; }
public string Bar { get; set; }
}
README.md needs to be updated to reflect the current version.
Additionally, the instructions for Soundex and Levensthein functionality should live with their respective projects
Hello.
If lazy loading disabled and you are using Include to load nav props after using of search extensions - Include simply dont work.
Example code:
query.Search(a => a.Street.Name) .Containing(keywords).Include(a=>a.Street)
Ideally, I need something like this:
var searchCustomersByEmployeeNameOrEmailExpression =
NinjanyeSearch<Customer>(c => c.Employee.Name, c.Employee.EmailAddress)
.Containing(viewModel.SearchQuery)
.AsExpression();
I am trying to build up what is basically a object that holds my search configuration which gets mapped to a request to my API essentially.
I can then save that expression and pass it right to EntityFramework when I am ready.
I want to ignore the case when using Containg
or ContainigAll
. But SetCulture
is not available. Is the documentation outdated or am I doing something wrong?
Simplified example of what I'm doing
var searchTerms = new string[] { "mockingbird", "kill" }
var found = await books.Search(book => book.Title)
.ContainingAll(searchTerms)
.ToListAsync(stoppingToken);
I'm searching on an EfCore DbSet
which inherits from IQueryable
Hi,
I have a problem or maybe just a question.
I've just seen your post on stackoverflow and finally found your library (https://stackoverflow.com/questions/18292618/linq-search-multiple-columns)
My code shortened looks like that:
public int _DokumentID { get; set; }
public string _Dokumentnr { get; set; }
public string _Dokumenttyp { get; set; }
var query = from Dok in dbContext.Dokumente
join ReAdr in dbContext.Adressen on Dok.REAdresseID equals ReAdr.AdresseID into ad
from subReAdresse in ad.DefaultIfEmpty()
select new ClassDok()
{
_DokumentID = Dok.DokumentID,
_Dokumentnr = Dok.Dokumentnr,
_Dokumenttyp = Dok.Dokumenttyp,
_rFirma = subReAdresse.Firma
}
Now I want to Search in that query for a String.
I thought it would be just using this:
query = query.Search().Containing("Searchparam");
But if I run that code, I get weird error messages that has nothing to do with the actual part of code that is in use.
Is it even possible to use .Search() in my scenario?
I also tested
query = query.Search(x => x._Dokumentnr).Containing(parSuchbegriff);
and that works.
I'm getting an error when trying to add this NuGet package to my portable class library.
Could not install package 'NinjaNye.SearchExtensions 2.0.6149.33145'. You are trying to install this package into a project that targets '.NETPortable,Version=v4.6,Profile=Profile44', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.
Are there any plans to include support for targeting other frameworks besides regular .NET?
in SQL i can do a "Guid like '%blah%'" style query which I can't figure out how to do with this library.
Integers was a bit of work but the following worked
.Search(p => SqlFunctions.Convert((decimal)p.IntegerProperty))
Investigate the possibility of have SearchExtensions return a parent item if a match is found in a child collection.
A possible example might be:
Given I have a collection of Shops,
I want to return all shops that stock a product with aName
containing "football".
Additionally, support for non strings should be considered:
Given I have a collection of Shops,
I want to return all shops that stock a product with aPrice
greater than 10.
@bzbetty - does this adequately fulfill what you were lookng for?
I'm using version 3.0.1 and I am unable to find the IsEqual method. Containing and StartsWith is available...has IsEqual been deprecated?
Hi,
I think there is an issue that prevents the inclusion of child entities in certain cases. Here's some example code:
var employees = context.Employees.AsNoTracking();
if (statusArray.Any()) employees = employees.Where(x => statusArray.Contains(x.Status));
var result = await employees.Search(x => x.LastName, x => x.FirstName)
.Containing(comparisonResult)
.ToRanked().OrderByDescending(x => x.Hits)
.Select(x => x.Item)
.Include(x => x.Child1).ThenInclude(x => x.Child11).ThenInclude(x => x.Child111)
.Include(x => x.Child2).ThenInclude(x => x.Child22).ThenInclude(x => x.Child222)
.Take(200)
.ToListAsync();
Suppose the number of lines in the "employees" variable is 1000 before the ranked search. The actual result from the query brings 200 entities but only some of them have the proper references to the "Child" sub-entities. All the others have them as NULL even though they are present in the database.
If I move the .Take(200) after the query brought all the results (so after the async call), the results are fine, but that defeats the purpose - I don't want to bring thousands of lines in memory and only then be able to limit their number in the result.
I've also tried moving the .Include() statements in other positions but it makes no difference. It seems to be an issue with the ToRanked() and Hits and .Take() applying the .Include() before the ordering and not after, thus resulting in entities without children.
Code looks like below:
return await _entities.Search(x => x.Propname).Containing(keywords.Split(' '))
.ToRanked().OrderByDescending(x => x.Hits)
.Select(x => x.Item)
.ToListAsync();
It seems to me the query created has some kind of issue:
System.InvalidOperationException: The LINQ expression 'DbSet
.Where(t => t.Propname.Contains("omni"))
.OrderByDescending(t => new Ranked{
Hits = (t.Propname ?? "").Length - (t.Propname ?? "").Replace(
oldValue: "omni",
newValue: "").Length / 4,
Item = t
}
.Hits)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Let me know if any other information could be useful to understand the problem.
Hi. thanks for the lib, I was trying it and saw searches across properties but did not list any help on the entity collections/tables.
I have 4 tables with that contain composite Keys made of a string* and a SerialNo
Since the search Key for e.g. AX903200287 or Uz09238744222344
is long (and no human friendly the user cannot differentiate if it belongs to a Parts Table or Services Table or Chem/Materials tables.)
Can you please show me
how can I find hits for the entire search strings in those tables/entities?
how can I fragment the search to begin with the numbers segment only?
thanks
I need to do a ContainingAll
query on an object's scalar properties and that of a child collection object.
q = q.Search(Function(t) t.Name,
Function(t) t.ClassName,
Function(t) t.OrderNumber).
ContainingAll(fields).
Where(Function(t) t.Contacts.Search(Function(x) x.Name).ContainingAll(fields).Any)
That t.Contacts
property is itself a collection that I need to search, but doing it this way throws the exception System.NotSupportedException: 'LINQ to Entities does not recognize the method 'NinjaNye.SearchExtensions.EnumerableStringSearch'1[Suntex.FirstInMath.Models.Api.Admin.Teams+Contact] ContainingAll(System.String[])' method, and this method cannot be translated into a store expression.'
Is there a way to do this kind of sub-query?
After upgrading to 1.6 I noticed Containing() adds not just the expression for .Contains() but also for StartsWith() and EndsWith(). This is rather painfull for search performance.
I think the cause is in QueryableContainsExpressionBuilder line 23-27, added in 1bdfb74
Hi John,
In version 2.0.6149.33145 a empty string is manage such as a space and return only string containing a space.
Expected ==> all results.
context.Companies.Search(Company => Company.CIE_NAME.ToUpper())
.Containing(searchString.ToUpper())
.Select(Company => new
{
CIE_ID = Company .CIE_ID,
CIE_NAME = Company .CIE_NAME,
})
.Distinct()
.OrderBy(Company => Company.CIE_NAME)
.ToList();
Regards,
First, of all I would like to thank you for this awesome library. I've been using this library just recently and it works great on my end.
I just want to ask if this kind of search is supported. I tried the chain search but no luck maybe because I'm missing the OR clause.
var total = context.Products.Where(x => keywords.All(k => x.Name.Contains(k)) || keywords.Any(k => x.SkuId.Contains(k))).Count();
Searching by empty string :
var resultsInTitles = this.repo
.All()
.Search(x => x.Title.ToLower())
.Containing(query.ToLower())
.ToRanked()
.OrderByDescending(r => r.Hits);
If query is "" :
Actual -> Throws
Expected -> Return all results
IE:
dbcontext.entity.Search("LastName").Contains("Billybob")
This is my current code which is not going so well:
foreach (var column in dataTableRequest.columns)
{
if(column.searchable && !string.IsNullOrWhiteSpace(column.search.value))
{
PropertyInfo pinfo = typeof(TViewModel).GetProperty(column.data);
Expression<Func<TViewModel, string>> propertyExpression = u =>
(
column.data
);
projectedQuery = projectedQuery
.Search(propertyExpression)
.Containing(column.search.value);
}
}
You can probably guess projectedQuery
is an IQueryable<T>
where I dont know what T
is yet. dataTableRequest.columns
is an array of simple object eventually ending up with two important properties:
dataTableRequest.columns[0].search.value
: The search valuedataTableRequest.columns[0].data
: The property of T
to search against.Is my approach okay? Should I somehow "lift" the search configuration up to my API Controller action so each entity is configured manually in the action?
Not sure if this is related to Core specifically, but I get a console full of errors when I try and use this library related to the Lambda passed to .Search()
not being able to be translated and instead being evaluated locally. No bueno.
If need be I could upload an example.
I was using predicate builder prior to switching to your library and was doing searches on joined data.
Example..
var predicateF = PredicateBuilder.New<PurchaseOrder>()
.Or(f =>f.Vendor.Name.Contains(parms.searchText))
.Or(f => f.LineItems.Any(y => y.StrainName.Contains(parms.searchText)))
.Or(f => f.LineItems.Any(y => y.ToComplianceId.Contains(parms.searchText)))
.Or(f => f.LineItems.Any(y => y.ComplianceId.Contains(parms.searchText)))
.Or(f => f.Manifest.ComplianceId.Contains(parms.searchText))
Is this possible?
I have tried using SearchChildren with EFCore and a query against a SQL database but it doesn't seem to be applying any filters in the generated SQL query.
I saw an old blog post that indicated this might only be available with in-memory queries - is that still the case?
EF doesnt support full text searching. It would be nice if at a certain point this library could make this work...
Hi,
Just discovered this library. I was way down a path of Expression magic when a colleague pointed me here. So far, this is awesome :).
I'm now trying to find all Persons in my DB (EF Core), where a certain relationship has a number of conditions met. My relationship is an EAV model, so the relation stores many records in the form of "Name=Value" pairs.
I tried this.
db.Persons.SearchChildren(x => x.EAV)
.With(x=>x.Name).EqualTo("SomeProperty")
.With(x=>x.Value).Contains("a", "b");
Which works. But chaining it like this, does not. It simply replaces the final query with just the last SearchChildren call:
db.Persons.SearchChildren(x => x.EAV)
.With(x=>x.Name).EqualTo("SomeProperty")
.With(x=>x.Value).Contains("a", "b")
.SearchChildren(x => x.EAV)
.With(x=>x.Name).EqualTo("OtherProperty")
.With(x=>x.Value).Contains("b", "c");
Should this be possible, if so, what am I missing?
What i want to do;
query.Search(x => string.Join(" ", x.Property1, x.Property2)).Containing("searchTerm");
This method does not work well. is there another way?
thanks.
Hello,
I am filtering a list of users by using a search pattern and NinjaNye.SearchExtensions 3.0.1.
One of the user has a special character within its name ( the literal Æ )
The search returns all expected results but the user with the special character.
Here is my code :
string pattern = "";
var coaches = context.Coaches.Search(t => t.UserName.ToLower())
.Containing(pattern.ToLower())
.OrderBy(c => c.UserName);
// a row is missing in the coaches variable.
Hi I got this error When I try to install it from NuGet:
Could not install package 'NinjaNye.SearchExtensions 2.1.0'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.5', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.
Hi!
Your project is very useful and it will be great, if you will introduce NET Core support.
I am trying to use the library as an expression builder so I can pass the expression's it builds around and make things more re-usable.
As such I really want access to CompleteExpression
because then I can create a Lambda and store that in a configuration object and apply it whenever i need to.
Ideally, everything in SearchBase should be public. It would be also nice if BuildExpression in QueryableSearchBase wasn't protected either.
When using this library and attempting a ToListAsync, we get the following error:
System.InvalidOperationException: 'The source IQueryable doesn't implement IDbAsyncEnumerable<System.String>. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations.
I'm certain this is just an implementation issue where we simply need to wrapper the existing methods in the expected manner, unfortunately, I'm not very familiar with Function Expressions. If someone can point me to an example, I'd be happy to do the implementation work.
query.Search(p=>p.date).soundex("10")
search datetime
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.