I have a field "details" varchar(MAX) in my table with the detailed description of an item, I need to select the records that contain the keywords passed by the user in a string array.
var searchTerms = new List<string> { "car", "black", "bike", "blue" };
I tried to do it like this:
var result = (from i in _contexto.itens
where searchTerms.Any(d => i.details.Contains(d))
select i.id);
and I get the following error:
The LINQ expression 'DbSet()
.Where(i => __searchTerms_0
.Any(d => i.details.Contains(d)))' 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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
I also tried this link's approach using the "SearchExtensions nuget package" and the same error occurs.
How can I do this? or should I make a query for each item in the array?
Example of a text I want to search for:
It is a long established fact that a car will be distracted by the
readable content of a page when looking at its bike. The point of
using Lorem Ipsum is that it has a more-or-less normal distribution
Thanks!
var searchTerms = new List<string> { "car", "black", "bike", "blue" };
string examplestring = "It is a long established fact that a car will be distracted by the readable content of a page when looking at its bike. The point of using Lorem Ipsum is that it has a more-or-less normal distribution";
var result = searchTerms.Select(s => (examplestring.Contains(s)) ? (object)searchTerms.IndexOf(s) : null).Where(w => w != null).ToList();
Adapting Alexander's advice from the link you gave, for your situation:
var searchTerms = new List<string> { "car", "black", "bike", "blue" };
Expressions<Func<TYPE_OF_OBJECT_IN_ITENS_HERE, bool>> expression = it => false;
foreach(var searchTerm in searchTerms)
{
expression = expression.Or(it => it.details.Contains(searchTerm));
}
var result = _contexto.itens.Where(expression);
You didn't post any detail about the type of object inside itens, so you'll have to replace that above
Related
This is my first post in this site.
I have a list of vehicles in my database and every vehicle has a Vin (License Plate Number, in example "6TRJ244", it is a string value).
I receive a list of search values, in example "A", "B","J"
I need to filter the vehicles which Vin Contains one of the search values.
In example if I have three vehicles with Vin: Vehicle1_Vin = "123AJ", Vehicle2_Vin = "123BJ", Vehicle3_Vin = "777CR"
If I receive as search values "X", "A","C" I should return Vehicles 1 and 3
With LINQ I am trying to do something like this
var searchParams = new List<string>() { "A", "B", "C"};
vehicles = vehicles.Where((vehicle) => searchParams.Any((searchParam) => vehicle.Vin.Contains(searchParam)));
But I receive this error message
"System.InvalidOperationException: The LINQ expression 'searchParam => EntityShaperExpression:
ProjectAlpha.BusinessObjects.Models.Vehicle
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
.Vin.Contains(searchParam)' 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 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information."
Thanks ins advance!
You could try use functions if you are using net core try with something like this
IQueryable<T> nightClub = nightClub.Where(nc => EF.Functions.Like(nc.Name, "%Dfox%");
It is not clear what collection type vehicles variable is so better introduce new variable or transfer IEnumerable to same type as source collection (ToArray/ToList etc):
var searchParams = new List<string>() { "A", "B", "C"};
var filtered = vehicles.Where(vehicle => searchParams.Any(vehicle.Vin.Contains));
I am trying to fetch a list of users after filtering by their name.
Query:
string filter="alex, faheem, Cohen";
var filterArr=filter.Split(new []{','},StringSplitOptions.RemoveEmptyEntries).Select(f=>f.Trim()).ToList();
var users= (from u in DbContext.Users
where filterArr.Any(y=> u.Name.Contains(y)) select u);
This gives me the error:
Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
I can't use filterArr.Contains(x.Name) because Name column contains both first name and second name. Just Like in list above their is an item "alex" and I have a name "Alex Hales" combined in Name column. So If I use filterArr.Contains(x.Name) it will not give me the result.
Any help will be much appreciated.
I'm not sure this is possible in a single statement like this. It's too complicated for the poor parsing stuff to work out.
However, you can get an IQueryable(), then iterate over your filters append these as individual WHERE clauses, then these should get added to the SQL properly later.
Something like this:
//this just gets a reference the DbSet, which implements IQueryable<User>
var queryable = _dbContext.Users;
//iterate over the filters and add each as a separate WHERE clause
foreach(var f in filters)
{
//this just adds to the existing expression tree..
queryable = queryable.Where(u=>u.Name.Contains(f));
}
//this will actually hit the database.
var results = queryable.ToList();
This should generate something like this in SQL (entirely pseudo-code)
select
u.*
from
users u
where
(u.username like "%Sue%")
or (u.username like "%Bob%")
Hope this helps...
I think you can do something like this
string filter = "alex, faheem, Cohen";
var filterArr = filter.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(f => f.Trim()).ToList();
var users = _dbContext.Users.Where(x => filterArr.Any(n => n.Contains(x.Name))).ToList();
UPDATE
For your requirement following query will work fine.
string filter = "Alex, faheem, Cohen";
var filterArr = filter.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(f => f.Trim())
.ToList();
var users = _dbContext.Users
.Where(x => filterArr.Any(n => x.UserName.Contains(n))).ToList();
If user has searched for "alex" and in Name (database column) there is "Alex Hales". users query will return the user "Alex Hales".
I have a simple document.
{
Name: "Foo",
Tags: [
{ Name: "Type", Value: "One" },
{ Name: "Category", Value: "A" },
{ Name: "Source", Value: "Example" },
]
}
I would like to make a LINQ query that can find these documents by matching multiple Tags.
i.e. Not a SQL query, unless there is no other option.
e.g.
var tagsToMatch = new List<Tag>()
{
new Tag("Type", "One"),
new Tag("Category", "A")
};
var query = client
.CreateDocumentQuery<T>(documentCollectionUri)
.Where(d => tagsToMatch.All(tagToMatch => d.Tags.Any(tag => tag == tagToMatch)));
Which gives me the error Method 'All' is not supported..
I have found examples where a single property on the child object is being matched: LINQ Query Issue with using Any on DocumentDB for child collection
var singleTagToMatch = tagsToMatch.First();
var query = client
.CreateDocumentQuery<T>(documentCollectionUri)
.SelectMany
(
d => d.Tags
.Where(t => t.Name == singleTagToMatch.Name && t.Value == singleTagToMatch.Value)
.Select(t => d)
);
But it's not obvious how that approach can be extended to support matching multiple child objects.
I found there's a function called ARRAY_CONTAINS which can be used: Azure DocumentDB ARRAY_CONTAINS on nested documents
But all the examples I came across are using SQL queries.
This thread indicates that LINQ support was "coming soon" in 2015, but it was never followed up so I assume it wasn't added.
I haven't come across any documentation for ARRAY_CONTAINS in LINQ, only in SQL.
I tried the following SQL query to see if it does what I want, and it didn't return any results:
SELECT Document
FROM Document
WHERE ARRAY_CONTAINS(Document.Tags, { Name: "Type", Value: "One" })
AND ARRAY_CONTAINS(Document.Tags, { Name: "Category", Value: "A" })
According to the comments on this answer, ARRAY_CONTAINS only works on arrays of primitives, not objects. SO it appears not to be suited for what I want to achieve.
It seems the comments on that answer are wrong, and I had syntax errors in my query. I needed to add double quotes around the property names.
Running this query did return the results I wanted:
SELECT Document
FROM Document
WHERE ARRAY_CONTAINS(Document.Tags, { "Name": "Type", "Value": "One" })
AND ARRAY_CONTAINS(Document.Tags, { "Name": "Category", "Value": "A" })
So ARRAY_CONTAINS does appear to achieve what I want, so I'm looking for how to use it via the LINQ syntax.
Using .Contains in the LINQ query will generate SQL that uses ARRAY_CONTAINS.
So:
var tagsToMatch = new List<Tag>()
{
new Tag("Type", "One"),
new Tag("Category", "A")
};
var singleTagToMatch = tagsToMatch.First();
var query = client
.CreateDocumentQuery<T>(documentCollectionUri)
.Where(d => d.Tags.Contains(singleTagToMatch));
Will become:
SELECT * FROM root WHERE ARRAY_CONTAINS(root["Tags"], {"Name":"Type","Value":"One"})
You can chain .Where calls to create a chain of AND predicates.
So:
var query = client.CreateDocumentQuery<T>(documentCollectionUri)
foreach (var tagToMatch in tagsToMatch)
{
query = query.Where(s => s.Tags.Contains(tagToMatch));
}
Will become:
SELECT * FROM root WHERE ARRAY_CONTAINS(root["Tags"], {"Name":"Type","Value":"One"}) AND ARRAY_CONTAINS(root["Tags"], {"Name":"Category","Value":"A"})
If you need to chain the predicates using OR then you'll need some expression predicate builder library.
I hope this is not a duplicate but I wasn't able to find an answer on this.
It either seems to be an undesired behavior or missing knowledge on my part.
I have a list of platform and configuration objects. Both contains a member string CodeName in it.
The list of CodeNames look like this:
dbContext.Platforms.Select(x => x.CodeName) => {"test", "PC", "Nintendo"}
dbContext.Configurations.Select(x => x.CodeName) => {"debug", "release"}
They are obtained from a MySQL database hence the dbContext object.
Here is a simple code that I was to translate in LINQ because 2 foreach are things of the past:
var choiceList = new List<List<string>>();
foreach (Platform platform in dbContext.Platforms.ToList())
{
foreach (Configuration configuration in dbContext.Configurations.ToList())
{
choiceList.Add(new List<string>() { platform.CodeName, configuration.CodeName });
}
}
This code gives my exactly what I want, keeping the platform name first which looks like :
var results = new List<List<string>>() {
{"test", "debug"},
{"test", "release"},
{"PC", "debug"}
{"PC", "release"}
{"Nintendo", "debug"}
{"Nintendo", "release"}};
But if I translate that to this, my list contains item in a different order:
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
I will end up with this, where the platform name isn't always first, which is not what is desired:
var results = new List<List<string>>() {
{"debug", "test"},
{"release", "test"},
{"debug", "PC"}
{"PC", "release"}
{"debug", "Nintendo"}
{"Nintendo", "release"}};
My question is, is it possible to obtain the desired result using LINQ?
Let me know if I'm not clear or my question lacks certain details.
Thanks
EDIT: So Ivan found the explanation and I modified my code in consequence.
In fact, only the Enumerable in front of the SelectMany needed the .ToList().
I should also have mentioned that I was stuck with the need of a List>.
Thanks everyone for the fast input, this was really appreciated.
When you use
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
it's really translated to some SQL query where the order of the returned records in not defined as soon as you don't use ORDER BY.
To get the same results as your nested loops, execute and materialize both queries, and then do SelectMany in memory:
var platforms = dbContext.Platforms.ToList();
var configurations = dbContext.Configurations.ToList();
var choiceList = platforms.SelectMany(p => configurations,
(p, c) => new List<string>() { p.CodeName, c.CodeName })
.ToList();
Rather than projecting it out to an array, project it out two a new object with two fields (potentially an anonymous object) and then, if you need it, project that into a two element array after you have retrieved the objects from the database, if you really do need these values in an array.
Try this-
var platforms= dbContext.Platforms.Select(x=>x.CodeName);
var configurations=dbContext.Configurations.Select(x=>x.CodeName);
var mix=platforms.SelectMany(num => configurations, (n, a) => new { n, a });
If you want to learn more in detail- Difference between Select and SelectMany
I have dictionary with regular expressions and a data table.
var data = ds.Tables["mytable"].AsEnumerable();
var regexlist = new Dictionary<string, Regex>
{
{"PI1", new Regex(#"(-.[^I](-P[^IS]))|(-[^P].(-P[^IS]))")},
{"SM1", new Regex("(-.[^I](-P[^IS]))|(-[^P].(-P[^IS]))")}
};
Now I want to select all rows of the data table that does not match the regular expressions in the list and also the key of the dictionary (the error code).
So far I have this:
var query = data.Select(dr => dr.Field<string>("F1"));
query = regexlist.Aggregate(query, (current, regex1) => current.Where(u => regex1.Value.IsMatch(u) ));
but I think that only the first regex is added as where clause.
And I don't know how to output the "error code"
I hope I explained my problem clear.
jonas
Whole things looks OK except that Where expression that doesn't reflect your objective, you need to invert test clause to get data that does not match regex'es
current.Where(u => regex1.Value.IsMatch(u) == false)