GetItemLinqQueryable doesn't return any items - azure-cosmosdb

I have two sets of code (below) that fetches items from a container that match conditions specified in "where" statement. One with the sql query works as expected whereas the other with the Linq expressions doesn't. The Linq one would return items if I remove all "Where" statements but what I want is to fetch items matching the conditions in those "Where" statements. Can you help me understand why the Linq one doesn't work?
QueryRequestOptions queryRequestOptions = new QueryRequestOptions()
{
MaxItemCount = DefaultPageSize, // 100
};
// THIS CODE WORKS
string query = $"SELECT * FROM root r WHERE r.documentType = #documentType and r._ts > #timestamp";
QueryDefinition queryDefinition = new QueryDefinition(query);
queryDefinition.WithParameter("#documentType", docType);
queryDefinition.WithParameter("#timestamp", TimestampConverter.ToTimestamp(fromTime));
using (var feedIterator = container.GetItemQueryIterator<T>(
queryDefinition,
null,
queryRequestOptions))
{
while (feedIterator.HasMoreResults)
{
FeedResponse<T> feedResponse = await feedIterator.ReadNextAsync(cancellationToken);
// THIS CODE DOESN"T WORK
using (var feedIterator = container.GetItemLinqQueryable<T>(false, null, queryRequestOptions)
.Where(d => d.DocumentType == docType)
.Where(d => d.Timestamp > fromTime)
.ToFeedIterator())
{
while (feedIterator.HasMoreResults)
{
FeedResponse<T> feedResponse = await feedIterator.ReadNextAsync(cancellationToken);
}

you can try like this
select many is used wherever i have an array in document
container.GetItemLinqQueryable<>()
.Where(ar => ar.FiscalYear == fiscalYear)
.SelectMany(ar => ar.ResourcePrivileges.Where(arp => arp.ResourceName == accessRequest.PendingUserRequest.ResourcePrivileges.ResourceName)
.SelectMany(rp => rp.Filters.Where(f => (int)f.Role > 2 && subRegionLst.Contains(f.SubRegion) && reqProduct.Contains(f.ProductName)))
.Select(i => new
{
Alias = ar.UserAlias,
Filter = ar.ResourcePrivileges.Where(arp => arp.ResourceName == accessRequest.PendingUserRequest.ResourcePrivileges.ResourceName).SelectMany(x => x.Filters),
})).Distinct();

I believe your issue has to do with how the linq query in serialized. By default GetItemLinqQueryable doesn't use camel case.
You can control the serialization by passing serializing options:
container.GetItemLinqQueryable<T>(
linqSerializerOptions: new CosmosLinqSerializerOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
});

Related

EntityFramework Core LINQ expression unable to be translated to SQL

I'm attempting to make a nested group join (category > subcategory > item). And return all of that data through my controller as lists of data below one another, like:
[
{
"name":"cat1",
"subcategories":[
{
"name":"subcat1",
"items":[
{
"name":"item1"
}
]
}
]
}
]
However the query I'm attempting to write is unable to be converted to SQL. It keeps saying it cannot be translated to SQL because of a client evaluation issue.
I've checked the documentation about client evaluation but I can't seem to find any plausible cause as to why this is still happening.
Supposedly it's because of code which cannot be transformed into SQL and has to be executed on the client itself, which needs to be configured explicitly, I want to avoid this if at all possible and execute the entire query on the server at once.
Is it even possible in the way I'm attempting to run it (with 2 GroupJoins nested), or do I need to switch to another way?
var categories = Db.WorkListCategories
.Where(w => w.CompanyId == null || w.CompanyId.Value == companyId);
var subCategories = Db.WorkListSubCategories
.Where(w => w.Category!.CompanyId == null || w.Category!.CompanyId.Value == companyId);
var workLists = Db.WorkLists
.Where(w => w.SubCategory!.Category!.CompanyId == null || w.SubCategory.Category.CompanyId.Value == companyId);
return await categories
.GroupJoin(subCategories, o => o.Id, i => i.CategoryId, (category, subCats) => new WorkListCategoryFull<WorkListSubCategoryFull>
{
Id = category.Id,
Name = category.Name,
IsEditable = category.IsEditable,
SubCategories = subCats
.GroupJoin(workLists, o => o.Id, i => i.SubCategoryId, (subCategory, workLists) => new WorkListSubCategoryFull
{
Id = subCategory.Id,
Name = subCategory.Name,
WorkLists = workLists
.Select(s1 => new WorkList
{
Id = s1.Id,
Name = s1.Name
})
.ToList()
})
.ToList()
})
.ToListAsync(cancellationToken);
For better visualization I've added the schema as an image below:
GroupJoin is less supported operator in EF Core. Use it only for LEFT JOIN. Your query can be rewritten in more simple way:
...
return await categories
.Select(category => new WorkListCategoryFull<WorkListSubCategoryFull>
{
Id = category.Id,
Name = category.Name,
IsEditable = category.IsEditable,
SubCategories = subCategories
.Where(subCategory => subCategory.CategoryId == category.Id)
.Select(subCategory => new WorkListSubCategoryFull
{
Id = subCategory.Id,
Name = subCategory.Name,
WorkLists = workLists
.Where(wl => subCategory.Id == wl.SubCategoryId)
.Select(wl => new WorkList
{
Id = wl.Id,
Name = wl.Name
})
.ToList()
})
.ToList()
})
.ToListAsync(cancellationToken);

Razor Pages get class Attributes in Unit Tests

currently I'm trying to check if there is a way to get the class names of all attributes on razor page classes.
I unit tests all my controllers to look for an AuthorizeFilter so that it is impossible to forget it
Currently this is how I do it via MVC (does not work in Razor Pages):
var values = actionDescriptorCollectionProvider
.ActionDescriptors
.Items
.OfType<ControllerActionDescriptor>()
.Select(a => new
{
a.DisplayName,
a.ControllerName,
a.ActionName,
AttributeRouteTemplate = a.AttributeRouteInfo?.Template,
HttpMethods = string.Join(", ", a.ActionConstraints?.OfType<HttpMethodActionConstraint>().SingleOrDefault()?.HttpMethods ?? new string[] { "any" }),
Parameters = a.Parameters?.Select(p => new
{
Type = p.ParameterType.Name,
p.Name
}),
ControllerClassName = a.ControllerTypeInfo.FullName,
ActionMethodName = a.MethodInfo.Name,
Filters = a.FilterDescriptors?.Select(f => new
{
ClassName = f.Filter.GetType().FullName,
f.Scope //10 = Global, 20 = Controller, 30 = Action
}),
Constraints = a.ActionConstraints?.Select(c => new
{
Type = c.GetType().Name
}),
RouteValues = a.RouteValues.Select(r => new
{
r.Key,
r.Value
}),
});
The problem is, that this code won't work with Razor Pages, i.e. FilterDescriptors is empty for PageActionDescriptor.
You need to use PageActionDescriptor for RazorPages instead of ControllerActionDescriptor:
var values = actionDescriptorCollectionProvider
.ActionDescriptors
.Items
.OfType<PageActionDescriptor>()
.Select(descriptor => new
{
// descriptor...,
// ...
});

Firestore query with multiple where clauses based on parameters

I want to query a Firestore database with multiple where clauses based on the parameters that are passed in. The following block of code works:
getProducts2(accountId: string, manufacturer?: string, materialType?: string): Promise<Product[]> {
return new Promise<Product[]>((resolve, reject) => {
const productCollection2: AngularFirestoreCollection<FreightRule> = this.afs.collection('products');
const query = productCollection2.ref
.where('materialType', '==', materialType)
.where('manufacturer', '==', manufacturer);
query.get().then(querySnapshot => {
if (querySnapshot.size > 0) {
const data = querySnapshot.docs.map(documentSnapshot => {
return documentSnapshot.data();
}) as Product[];
resolve(data);
} //todo else...
});
});
}
But what I really want to do is conditionally include the where clauses based on the optional parameters. The following is what I want, but it doesn't work:
getProducts2(accountId: string, manufacturer?: string, materialType?: string): Promise<Product[]> {
return new Promise<Product[]>((resolve, reject) => {
const productCollection2: AngularFirestoreCollection<FreightRule> = this.afs.collection('products');
const query = productCollection2.ref;
if (manufacturer) {
query.where('manufacturer', '==', manufacturer);
}
if (materialType) {
query.where('materialType', '==', materialType);
}
query.get().then(querySnapshot => {
if (querySnapshot.size > 0) {
const data = querySnapshot.docs.map(documentSnapshot => {
return documentSnapshot.data();
}) as Product[];
resolve(data);
} //todo else...
});
});
}
While valid, this code just returns all of the products with no filtering.
Is there a way to structure this so I can filter based on the optional parameters?
edit: I realize I can do something like:
let query;
if (manufacturer && materialType) {
query = productCollection2.ref.where(....).where(....)
} else if (manufacturer) {
query = productCollection2.ref.where(....)
} else if (materialType) {
query = productCollection2.ref.where(....)
}
I was just hoping for something a little more elegant.
Build upon the prior query, don't repeat the prior query:
let query = collection // initial query, no filters
if (condition1) {
// add a filter for condition1
query = query.where(...)
}
if (condition2) {
// add a filter for condition2
query = query.where(...)
}
// etc
If using different query structure, You can try below ways:
db.collection("subscriptions").where("email", '==', req.body.email,"&&","subscription_type","==","free").get();
OR
db.collection("subscriptions").where("email", '==', req.body.email).where("subscription_type", '==', 'free111').get();

FlowType errors using Object.entries

So, I have the following code, but flow errors keep popping up. I've tried to cast the Object.entries, but just won't work - others things to. Any insight?
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.entries(a)
.map(([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
I've left some things off, but the slow is the same. Flow is reading that entries as a return Tuple Type. I've tried all sorts of things, but instead of mudding things up, I left it untouched.
I can't seem to annotate the destructured items here ([key, obj]), get tuple errors...
Any assistance on getting that code assigned to var c, to work with annotations etc..?
The errors I get:
Cannot call method on mixed type (from obj.func)
Cannot assign value in Tuple etc..
The error is accurate. Object.entries has the type
entries(object: any): Array<[string, mixed]>;
It has no way to know what the type of the second item in the tuple will be. That means your code
.map(([key, obj]) => obj.func(key) ? obj : false)
would need to do
.map(([key, obj]) => {
if (typeof obj.func !== 'function') throw new Error();
return obj.func(key) ? obj : false;
})
so that flow knows that it is guaranteed to be a function.
Alternatively, you could change your data structure to use a type where the second item in the tuple has a guaranteed type, like Map, e.g.
type Fields = {
name: string,
func: (string) => boolean
};
type S = Map<string, Fields>;
var a: S = new Map([
['key1', {name: 'mary', func: (str) => str === 'mary'}],
['bill', {name: 'bill', func: (str) => str === 'bill'}],
]);
var c = Array.from(a, ([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
In my case, I had:
let objectsByName : { [string] : MyObjectType } = {}; //simple map
...
objectsByName[object.name] = object; //call repeatedly to populate map.
...
let results : any[] = []; //next we will populate this
Trying to operate on it like this failed for Flow (though this is executable JavaScript):
for (let [name : string, object : MyObjectType] of Object.entries(objectsByName))
{
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
This succeeded for Flow:
for (let name : string in objectsByName)
{
let object = objectsByName[name];
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
It is annoying having to change code structure to suit a supposedly non-intrusive system like Flow comment types, which I chose in the hopes of making my code completely oblivious to Flow's presence. In this case I have to make an exception and structure my code as Flow wants it.
Replacing Object.entries with Object.keys + lookup fixes flow errors for me assuming the input object is properly typed.
i.e. replace Object.entries(a) with Object.keys(a).map(key => [key, a[key]])
This works with flow:
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.keys(a)
.map(key => a[key].func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});

Reference members in Moq lambda

I have a function in a class that returns a value based on the state of a class property. In this example, I want HasName() to return true if Name is not null. I could simply do Returns(false), however I want it evaluate as a lambda so that it works properly if Name is modified during the test.
public interface IThing
{
string Name { get; set; }
bool HasName();
}
var mocks = new Dictionary<string, IThing>();
Mock<IThing> mockThing;
mockThing = new Mock<IThing>();
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
mocks["first"] = mockThing.Object;
mockThing = new Mock<IThing>();
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
mocks["second"] = mockThing.Object;
Console.WriteLine(mocks["first"].HasName());
mocks["first"].Name = null;
Console.WriteLine(mocks["first"].HasName());
The 2nd Console.WriteLine prints true instead of false due to scoping (referencing the 2nd mock). Resharper actually complains of "Access to modified closure". What is the correct way to do this?
Although your design a little bit strange but you can access the generated mock object with mockThing.Object in your Setup function:
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mockThing.Object.Name != null;
});
var thing = mockThing.Object;
var hasName = thing.HasName(); // true because Name returns "test"
thing.Name = null;
hasName = thing.HasName(); // false
The problem is that you are referencing the mockThing with your lambdas and then you are reasigning it. So both setup will end up using the same instance.
Use the mocks from the dictionary and it will work:
var mocks = new Dictionary<string, IThing>();
Mock<IThing> mockThing;
mockThing = new Mock<IThing>();
mocks["first"] = mockThing.Object;
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mocks["first"].Name != null;
});
mockThing = new Mock<IThing>();
mocks["second"] = mockThing.Object;
mockThing.SetupProperty(m => m.Name, "test");
mockThing.Setup(m => m.HasName()).Returns(() =>
{
return mocks["second"].Name != null;
});
Console.WriteLine(mocks["first"].HasName());
mocks["first"].Name = null;
Console.WriteLine(mocks["first"].HasName());

Resources