I got the following documents:
public class TreeNode
{
string Id;
string Owner; //"users/1"
string TodoListId; //"todolists/1"
string ParentId; //"treenodes/1"
}
public class TodoList
{
string Id;
List<TodoItem> Items;
}
public class TodoListItem
{
bool IsCompleted;
}
How can I fetch all items for the current user which has not completed? Should I redesign any of the documents?
I want something like:
from all treenodes belonging to the current user
load all todolists
and return all active items within those lists
But within one server roundtrip
Update 2
Here is how I tried to do it with two queries (SelectMany is not supported):
var todoListIds = _dbSession.Query<UserTreeNode>()
.Where(x => x.UserId == user.Id)
.Select(x => x.TodolistId);
var nodes = _dbSession.Query<Todolist>()
.Where(x => x.Id.In(todoListIds))
.SelectMany(x => x.Items.Where(item => !item.IsCompleted));
You can't make RavenDB only return a sub-set of a single doc, so in your case you need to get the entire TodoList and then just filter on the client.
You can do this in a single network call using the Include feature, this should work:
var todoListIds = _dbSession.Query<UserTreeNode>()
.Include(x => x.TodoListId)
.Where(x => x.UserId == user.Id)
.Select(x => x.TodolistId);
foreach (var userListId in todoLisIds)
{
//This won't cause an extra network call, because the session will have already loaded it
var todoList = _dbSession.Load<TodoList>(userListId);
//In-memory filtering to get the outstanding items
var activeItems = todoList.Items.Where(x => x.IsCompleted).ToList();
}
I think what you have provided is not the real code but the following gives you uncompleted items from a todolist object.
list.Items.Where(q => q.IsCompleted == false);
I spent some time on it and I believe you need a different approach, (please note its something related to architecture and I can't be 100% sure about it, it might need some modification). It seems like you want to create a TODO List for the user. I think, its may be better if you could structure it in a way That
A User Can have one or more To Do List(s) (I have assumed one To Do List in my example)
One To Do List Item can have multiple instances of Actual work to Do
I would follow a structure similar to below:
public class User
{
public string ID { get; set; }//.... All User Attributes AND
}
public class TodoList
{
public string Id { get; set; }
public User owner { get; set; }
}
public class TodoListItem
{
public string ItemID { get; set; }
public TodoList parent { get; set; }
public string ItemDescription { get; set; }
public bool IsCompleted { get; set; }
}
Above I have a class for User which is currently representing your user. Then I have a class for ToDoList which is holding User class object (not the id of user) then I have ToDoListItem which is holding ToDoList object as parent.
If we look through database perspective than we have One to Many relationship between User and ToDoList and again one to many in ToDoList and ToDoListItem.
Now if you want to search user with incomplete work to do , just try the following linq query:
var query = from t in listTDL
where t.IsCompleted == false
select t.parent.owner;
You might need these lines to fill a test data structure:
User user = new User() { ID = "User1" };
TodoList td = new TodoList() { Id = "TD1", owner = user};
List<TodoListItem> listTDL = new List<TodoListItem>();
TodoListItem tdl = new TodoListItem() { ItemID = "TDL1", ItemDescription = "Frist Item", IsCompleted = false, parent=td };
listTDL.Add(tdl);
listTDL.Add(new TodoListItem() { ItemID = "TDL2", ItemDescription = "second Item", IsCompleted = true, parent = td });
listTDL.Add(new TodoListItem() { ItemID = "TDL3", ItemDescription = "third Item", IsCompleted = true, parent = td });
listTDL.Add(new TodoListItem() { ItemID = "TDL4", ItemDescription = "fourth Item", IsCompleted = false, parent = td });
List<User> userList = new List<User>();
userList.Add(user);
Here is how I would do:
var result = todoList.Where
(
x => nodeList.Any
(
y => y.Owner == "ownerId" && y.TodoListId == x.Id
)
).SelectMany(x => x.Items).Where(z => !z.IsCompleted);
P.s. I'am not familiar with RavenDB so showing just an idea
Related
i have ravendb document called Orders like this
{
"MyOrders": [
"S1",
"S2"
],
"Id": "6666"
}
I will query this document and pass order string for example S1
Then it should return me that document as S1 matches it. I am pretty new to this ravendb. I am unable to find way. I have written only this so far
public class MyOrderIndexes: AbstractIndexCreationTask<Order>
{
public MyOrderIndexes()
{
Map = Orders => from Order in Orders
select new
{
ID= Order.Id
};
Index(x => x.Id, FieldIndexing.NotAnalyzed);
}
}
can someone help me
To query that, you don't need to create an index. Just do a query, Raven will create it for you.
This query should work just fine:
var hasS2 = session.Query<Orders>()
.Where(o => o.MyOrders.Contains("S2"))
.ToList();
One problem is the FieldIndexing on Id, and also that you need to map MyOrders to perform query on it.
Index:
public class MyOrderIndexes: AbstractIndexCreationTask<Order>
{
public MyOrderIndexes()
{
Map = Orders => from Order in Orders
select new
{
Id = Order.Id,
MyOrders = Order.MyOrders
};
}
}
Query:
var results = session
.Query<Order, MyOrderIndexes>()
.Where(x => x.MyOrders.Any(l => l == "S1"))
.ToList();
Model:
public class Order
{
public string Id { get; set; }
public ICollection<string> MyOrders { get; set; }
}
I'm trying to update the Selectedproperty of an IEnumerable<SelectListItem> for a MVC-Combobox website using linq. However this is not working, as shown in the debbuging result: The Count() for the criteria returns an item, however the Count()for .Selected == truereturns 0.
public IEnumerable<SelectListItem> Categories { get; set; }
public CategoryModel Category
{
get { return category; }
set
{
category = value;
Categories.Where(x => x.Value == value.Id.ToString()).First().Selected = true;
}
//Debugging Results
//?Categories.Where(x => x.Value == value.Id.ToString()).Count()
//1
//?Categories.Count(x => x.Selected == true);
//0
}
Update:
I guess the problem is more bound to the IEnumerable<SelectListItem>, because the after changing Categories to an ObservableCollection it works fine (example below), even though LinQ is not designed for changing data....
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description = "Stackoverflow";
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
LINQ is to query your data-source not to modify it.
Your current approach has a drawback anyway, you would select one but you would not deselect the others. So you need a loop:
public CategoryModel Category
{
get { return category; }
set
{
category = value;
// consider to use a lock here to avoid multi threading issues
foreach(SelectListItem catItem in Categories)
catItem.Selected = catItem.Value == value.Id.ToString();
}
}
I would use a method SetSelectedCategory instead of a property if i'd modify a collection.
IEnumerable does not guarantee that changes get persisted across enumerations.
It all depends on the underlying implementation in the end (List, Array, Observable, etc).
Among the options that you have is to change your actual Categories to a writable collection (like List)...
But you might not be able to do that, or you might simply prefer to stay lean and keep using the IEnumerable.
In that case you could simply mutate the original collection and project it over the original
void Main()
{
Categories = Load();
var active = new Func<CategoryModel, int, CategoryModel>((category, match) =>
{
return new CategoryModel
{
Id = category.Id,
Name = category.Name,
Active = category.Id == match
};
});
Categories = Categories.Select(p => active(p, 2));
Categories.Dump();
}
public IEnumerable<CategoryModel> Categories { get; set; }
public IEnumerable<CategoryModel> Load()
{
yield return new CategoryModel { Id=1, Name = "one" };
yield return new CategoryModel { Id=2, Name = "two" };
yield return new CategoryModel { Id=3, Name = "three" };
}
public class CategoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
Id|Name|Active
1 one False
2 two True
3 three False
this is also to emphasize that you can use linq for "transformations" using "projections"
I have the following order object which contains a list of order addons. I am trying to create a report that shows all the addon types and their quantities summed.
public class Order {
public IList<OrderAddon> OrderAddons { get; set; }
}
public class OrderAddon {
public enum OrderType { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
}
This is where I am at and can't figure out if the entire query is wrong of I am just missing something.
var query = from order in Model.Orders
from addon in order.OrderAddons
group order by addon.AddonType
into orderAddons select new
{
Name = orderAddons.Key,
Quantity = orderAddons.Sum(x => x.) << This is where I am stuck
};
When I hit . my intellisense is showing me properties in order object not the addon object.
That's because you're saying group order by ..., so the orderAddons object becomes a grouping of orders. You can use this if you're going to need properties from both objects:
from order in Model.Orders
from addon in order.OrderAddons
group new{addon, order} by addon.AddonType
into orderAddons select new
{
Name = orderAddons.Key,
Quantity = orderAddons.Sum(x => x.addon.Quantity)
};
If this is all the data you need, this is a little simpler:
from order in Model.Orders
from addon in order.OrderAddons
group order.Quantity by addon.AddonType
into quantityByAddonType select new
{
Name = quantityByAddonType.Key,
Quantity = quantityByAddonType.Sum()
};
an alternative syntax same result...
var result = Model.Orders
.SelectMany(order => order.OrderAddons)
.GroupBy(addon => addon.OrderType)
.Select(grouping => new
{
Name = grouping.Key,
Quantity = grouping.Sum(addon => addon.Quantity)
});
i would like to make a treelistview for my Data.
Tree should look like this
Accounts
-> Providers
-> Accounts
public sealed class AccountRoot
{
public AccountRoot()
{
Providers = new Collection<Hoster>();
}
public long AccountRootId { get; set; }
public ICollection<Hoster> Providers { get; set; }
}
public sealed class Hoster
{
public Hoster()
{
Accounts = new Collection<Account>();
}
[Key]
public long HosterId { get; set; }
public long AccountRootId { get; set; }
public string Name { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public sealed class Account
{
[Key]
public long AccountId { get; set; }
public long HosterId { get; set; }
public Hoster Hoster { get; set; }
public string Name { get; set; }
}
I would like to order my query.
should be sth like
Accounts
Providers A-Z
Accounts A-Z
what i got until now is..
var query = _entity.AccountRoot.Local
.Select(x => new AccountRoot()
{
AccountRootId = x.AccountRootId,
Providers = x.Providers.OrderBy(y => y.Name).ToList()
}).ToList();
What is missing is the orderby for the next nested collection.
Thank you for your help ! :-)
It can be a bit different approaches depending on if you already have a result set, and want to just sort it in code, or if you want to construct IQueryable<> for EF which will be successfully compiled to SQL and executed with actual sorting in database.
First, assume you already have the collection in code. In this case, you have object AccountRoot, which contains collection of Providers, each of which has collection of Accounts. Obviously, you cannot return the same objects, as you need to reorder collection properties, so all you need is to just construct new ones. I would just sort the collections, but you could construct completely new entities, if you need:
var query = ...
.Select(x => new AccountRoot
{
// add copy properties here
// ....
Providers = x.Providers
.Select(y =>
{
// Here we can construct completely new entity,
// with copying all properties one by one,
// or just reorder existing collection as I do here
var result = y;
result.Accounts = y.Accounts.OrderBy(z => z.Name).ToArray();
return result;
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
Second case, if you need to get it directly from SQL, is a bit different, as you cannot use all that var result = ...; ... return result stuff in lambda - it won't compile to SQL. But idea is the same - you need to construct projection from data sets. It should be something like this:
var query = ...
.Select(x => new AccountRoot
{
AccountRootId = x.AccountRootId,
// Other properties to copy
// ...
Providers = x.Providers
.Select(y => new Hoster
{
HosterId = y.HosterId,
// Other properties to copy
// ...
Accounts = y.Accounts.OrderBy(z => z.Name).ToArray(),
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
I'm pulling a dataset into a c# list and to sort it. It's a hierarchical menu:
sample object:
public class NavigationInfo
{
public Int32 Id { get; set; }
public Int32 ParentId { get; set; }
public String Text { get; set; }
public String Url { get; set; }
public Int32 Sort { get; set; }
}
The ParentId is recursive to Id and Sort is an ascending integer within the ParentId. How is that done using a collection of NavigationInfo in List<NavigationInfo>?
You can do something like:
var navigationInfos = new List<NavigationInfo>(); //fill this collection
navigationInfos.sort((a,b) => a.Id.CompareTo(b.Id)); //sort by Id
navigationInfos.sort((a,b) => a.ParentId.CompareTo(b.ParentId)); //sort by ParentId
UPDATE: You can also use LINQ and do an OrderBy on the List. This returns a new collection, but is a lot easier to order by multiple criteria, ascending or descending.
var navigationInfos = new List<NavigationInfo>(); //fill this collection
var listSortedById = navigationInfos
.OrderBy(n => n.Id).ToList();
var listSortedByParentId = navigationInfos
.OrderBy(n => n.ParentId).ToList();
var listSortedByIdThenByParentId = navigationInfos
.OrderBy(n => n.Id)
.ThenBy(p => p.ParentId)
.ToList();
var orderedByIdDescending = navigationInfos
.OrderByDescending(n => n.Id)
.ToList();
If the data comes from DB, you can let the DB send the output in sorted manner (parent first, followed by child Id).
Note: I am assuming DB has the Ids in an order where parent will be a lesser Id than a child.
If your hierarchy will be more than a simple two-level arrangement, you'll need to write a method (probably recursive) that can travel up the tree collecting the entire "path" of each item's ids. Once you have this path, the sorting step is fairly easy.
Add a property to NavigateInfo
public string ItemPath { get; set; }
Then use a method like this to set that property.
public string GetPath(List<NavigationInfo> list, int itemId)
{
NavigationInfo item = list.SingleOrDefault(x => x.Id == itemId);
if (item == null)
{
return "";
}
else
{
return GetPath(list, item.ParentId) + "\\" + itemId;
}
}