OptGroup in SelectListItem Not showing in DropDownList - asp.net-core

I wrote a method to show Items and in Groups in DropDownlist with SelectListItem, But the problem is that only show the first group name and child plus the childs of other groups. The problem is that do not show second, third,.. groups (but show their childs).
My model is
public class PermissionsViewModel
{
public long ID { get; set; }
public string Title { get; set; }
public long TypeId { get; set; }
public long? ParentId { get; set; }
public string ParentTitle { get; set; }
public List<PermissionsViewModel> ParentList { get; set; }
public List<PermissionsViewModel> OperationsList { get; set; }
public List<PermissionTypesDto> PermissionTypesList { get; set; }
public bool Status { get; set; }
}
Method to retrieve data:
public Dictionary<long?,List<PermissionsViewModel>> GetPermissionsByModule()
{
var ItemValue = (_ipermissionTypes.Expose().FirstOrDefault(x => x.Title == "Operation").Id);
var permissionbymodule = _ntumcontext.Tbl_Permissions
.Where(x => x.Status == true && x.TypeId == ItemValue)
.Select(x => new PermissionsViewModel
{
ID = x.ID,
Title = x.Title,
Status = x.Status,
ParentId = x.ParentId,
ParentTitle=x.permission.Title,
TypeId=x.TypeId,
}).AsEnumerable().GroupBy(x => x.ParentId).ToList();
return permissionbymodule.ToDictionary(k => k.Key, v => v.ToList());
}
And the Method to get on razor page (View):
public List<SelectListItem> Permissions = new List<SelectListItem>();
public List<SelectListItem> GetPermissionsByModule()
{
var AllPermissions = _ipermissionsApplication.GetPermissionsByModule();
foreach (var (key, value) in AllPermissions)
{
var parentTitle = _ipermissionsApplication.GetDetails(key).Title; //get group title from key
var group = new SelectListGroup() { Name = parentTitle };
foreach (var per in value)
{
var item = new SelectListItem(per.Title, per.ID.ToString())
{
Group = group
};
Permissions.Add(item);
}
}
return Permissions;
}
And in cshtml :
<select asp-for="RoleVM.SelectedPermissions" asp-items="Model.Permissions">
At present with the above codes, the problem is that do not show the second and third and ... , only show the first group name, but show all child items of all groups.

Firstly,you need to check the value of AllPermissions,maybe the parentID is not exists,and then you need to check var parentTitle = _ipermissionsApplication.GetDetails(key).Title;maybe the second and third and.. parentTitle is null.
Here is a working demo(I use fake data):
public class TestPermissionsModel : PageModel
{
public List<SelectListItem> Permissions = new List<SelectListItem>();
public void GetPermissionsByModule()
{
var AllPermissions = new Dictionary<long, List<PermissionsViewModel>>()
{
{1,new List<PermissionsViewModel>{ new PermissionsViewModel{ ID=11,Title="title11", ParentId=1}, new PermissionsViewModel { ID = 12, Title = "title12", ParentId = 1 } } },
{2,new List<PermissionsViewModel>{ new PermissionsViewModel{ ID=21,Title="title21", ParentId=2}, new PermissionsViewModel { ID = 22, Title = "title22", ParentId = 2 } } },
{3,new List<PermissionsViewModel>{ new PermissionsViewModel{ ID=31,Title="title31", ParentId=3}, new PermissionsViewModel { ID = 32, Title = "title32", ParentId = 3 } } }
};
foreach (var (key, value) in AllPermissions)
{
var parentTitle = "parentTitle" + key; //get group title from key
var group = new SelectListGroup() { Name = parentTitle };
foreach (var per in value)
{
var item = new SelectListItem(per.Title, per.ID.ToString())
{
Group = group
};
Permissions.Add(item);
}
}
}
public void OnGet()
{
GetPermissionsByModule();
}
}
View:
<select asp-items="Model.Permissions"></select>
result:

Related

Add <a> link for every node in JTree using Asp.Net Core

how i can add a tag link to every node(root or children) in JTree, I Fetch data from database with EFCore
and i want to every node have link like this:
<a class="btn btn-primary" asp-action="TaskTypeDetail" asp-controller="Admin" asp-route-TaskTypeNumber=#item.TaskTypeNumber>details</a>
mycontroller like this :
public class TreeviewController : Controller
{
private readonly RoleManager<IdentityRole> roleManager;
private readonly UserManager<ApplicationUser> userManager;
private readonly ApplicationDbContext applicationDbContext;
public TreeviewController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager, ApplicationDbContext applicationDbContext)
{
this.roleManager = roleManager;
this.userManager = userManager;
this.applicationDbContext = applicationDbContext;
}
public IActionResult Index()
{
return View();
}
public JsonResult GetRoot()
{
List<JsTreeModel> items = GetTree();
return new JsonResult ( items );
}
public JsonResult GetChildren(string id)
{
List<JsTreeModel> items = GetTree(id);
return new JsonResult ( items );
}
public List<JsTreeModel> GetTree()
{
bool checkchildren;
var items = new List<JsTreeModel>();
foreach (var role in roleManager.Roles)
{
foreach (var user in userManager.Users)
{
checkchildren = false;
var checkUserInRole = userManager.IsInRoleAsync(user, role.Name).Result;
if (checkUserInRole)
{
var checkEmployeeForUser = applicationDbContext.EmployeeInRoles.Where(s => s.RoleId == role.Id).ToList();
if (checkEmployeeForUser.Count > 0)
{
checkchildren = true;
}
items.Add(new JsTreeModel { id = user.Id.ToString(), parent = "#", text = user.Name+" "+user.Family+" ریشه " , children = checkchildren,a_attr=""});
}
}
}
// set items in here
return items;
}
public List<JsTreeModel> GetTree(string id)
{
var items = new List<JsTreeModel>();
// set items in here
//Loop and add the Child Nodes.
bool checkchildren;
string Role="";
foreach (var subType in applicationDbContext.EmployeeInRoles)
{
checkchildren = false;
string Parentid = "";
foreach (var findParent in userManager.Users)
{
var roleid = roleManager.FindByIdAsync(subType.RoleId);
var checkParent = userManager.IsInRoleAsync(findParent, roleid.Result.Name).Result;
if (checkParent) {
Parentid = findParent.Id;
}
}
var user = userManager.Users.SingleOrDefault(s => s.Id == subType.UserId);
foreach(var role in roleManager.Roles)
{
var checkUserInRole = userManager.IsInRoleAsync(user, role.Name).Result;
if (checkUserInRole)
{
Role = role.Id;
break;
}
}
var checkEmployeeForUser = applicationDbContext.EmployeeInRoles.Where(s => s.RoleId == Role).ToList();
if (checkEmployeeForUser.Count > 0)
{
checkchildren = true;
}
items.Add(new JsTreeModel { id = subType.Id.ToString(), parent = Parentid, text = user.Name + " " + user.Family, children=checkchildren});
}
return items;
}
}
JsTreeModel like this:
public class JsTreeModel
{
public string id { get; set; }
public string parent { get; set; }
public string text { get; set; }
public string a_attr { get; set; }
public bool children { get; set; } // if node has sub-nodes set true or not set false
}
and Index.cshtml
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"></script>
<div id='treeview'></div>
<script>
$('#treeview').jstree({
"plugins": ["search","contextmenu"],
'core': {
'data': {
'url': function (node) {
return node.id === '#' ? "/Treeview/GetRoot" : "/Treeview/GetChildren/" + node.id;
},
'data': function (node) {
return {'id': node.id };
}
}
}
});
$('#treeview').on('changed.jstree', function (e, data) {
console.log("=> selected node: " + data.node.id);
});
but every node that show me, when I click on it, doesn't have link and # instead of link
can any one help me ?

Take function doesn't work and cannot be sent to RavenDB for query

Query Code:
var query = session.IndexQuery<App_OrgSearch.IndexResult, App_OrgSearch>();
var organizationUnitResults = query.Statistics(out stats)
.Skip(0)
.Take(5)
.AsProjection<Org>().ToList();
public static IRavenQueryable<TResult> IndexQuery<TResult, TIndex>(this IDocumentSession session)
where TIndex : AbstractIndexCreationTask, new()
{
return session.Query<TResult, TIndex>();
}
App_OrgSearch is the index I defined as below:
public class App_OrgSearch : AbstractIndexCreationTask<Org, App_OrgSearch.IndexResult>
{
public class IndexResult
{
public string Id { get; set; }
public string BusinessName { get; set; }
public string ShortName { get; set; }
public IList<string> Names { get; set; }
public List<string> PhoneNumbers { get; set; }
public List<OrganizationUnitPhone> OrganizationUnitPhones { get; set; }
}
public App_OrganizationUnitSearch()
{
Map = docs => from doc in docs
select new
{
Id = doc.Id,
Names = new List<string>
{
doc.BusinessName,
doc.ShortName,
},
BusinessName = doc.BusinessName,
ShortName = doc.ShortName,
PhoneNumbers = doc.OrganizationUnitPhones.Where(x => x != null && x.Phone != null).Select(x => x.Phone.Number),
};
Indexes.Add(x => x.Names, FieldIndexing.Analyzed);
}
}
I have 27 records in database. I want to take 5, but after query, all 27 records are returned. Why does Take function not work?
Your sample code seems wrong.
var query = session.IndexQuery<App_OrgSearch.IndexResult, App_OrgSearch>();
var organizationUnitResults = organizationUnitsQuery.Statistics(out stats)
What is organizationUnitsQuery ? You have the query as query, but there is no IndexQuery method on the session

How to create a search index by related document field in RavenDb?

I think my scenario is a common scenario in any application. I will exemplify using an common used domain: Products and Orders with Items (of products).
What I am trying to do is search orders by the product name, considering that the order has a list of itens and each item has a related productId.
I did some searches and read the Raven documentation, but I was unable to find the answer to my problem.
Please consider the code below:
public class Product
{
public string Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
}
public class Order
{
public string Id { get; set; }
public string OrderNumber { get; set; }
public decimal Total { get; set; }
public string Customer { get; set; }
public Item[] Items { get; set; }
}
public class Item
{
public string ProductId { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Order_ByProductName : AbstractMultiMapIndexCreationTask<Order_ByProductName.Result>
{
public class Result
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public string[] OrdersIds { get; set; }
public string[] OrdersNumbers { get; set; }
}
public Order_ByProductName()
{
AddMap<Product>(products => from product in products
select new
{
ProductId = product.Id,
ProductName = product.ProductName,
OrderId = default(string),
OrderNumber = default(string)
});
AddMap<Order>(orders => from order in orders
group order by order.Items.Select(c => c.ProductId)
into g
select new
{
ProductId = g.Key,
ProductName = default(string),
OrdersIds = g.Select(c => c.Id),
OrdersNumbers = g.Select(c => c.OrderNumber)
});
Reduce = results => from result in results
group result by result.ProductId
into g
select new
{
ProductId = g.Key,
ProductName = g.Select(r => r.ProductName).Where(t => t != null).First(),
OrdersIds = g.Where(r => r.OrdersIds != null).SelectMany(r => r.OrdersIds),
OrdersNumbers = g.Where(r => r.OrdersNumbers != null).SelectMany(r => r.OrdersNumbers)
};
Sort("ProductName", SortOptions.String);
Index(x => x.ProductName, FieldIndexing.Analyzed);
}
}
class Program
{
static void Main(string[] args)
{
var documentStore = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDatabase"
};
documentStore.Initialize();
new Order_ByProductName().Execute(documentStore);
using (var session = documentStore.OpenSession())
{
var product1 = new Product() { Price = 100, ProductName = "Phone" };
var product2 = new Product() { Price = 1000, ProductName = "Laptop" };
var product3 = new Product() { Price = 200, ProductName = "Windows Phone" };
session.Store(product1);
session.Store(product2);
session.Store(product3);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var products = session.Query<Product>().ToList();
var order1 = new Order();
order1.Customer = "Jhon Doe";
order1.OrderNumber = "001";
order1.Items = new Item[] {
new Item { ProductId = products[0].Id, Price = products[0].Price, Quantity = 1 },
new Item { ProductId = products[1].Id, Price = products[1].Price, Quantity = 1 }
};
order1.Total = order1.Items.Sum(c => (c.Quantity * c.Price));
var order2 = new Order();
order2.Customer = "Joan Doe";
order2.OrderNumber = "002";
order2.Items = new Item[] {
new Item { ProductId = products[2].Id, Price = products[2].Price, Quantity = 1 }
};
order2.Total = order2.Items.Sum(c => (c.Quantity * c.Price));
session.Store(order1);
session.Store(order2);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var results = session
.Query<Order_ByProductName.Result, Order_ByProductName>()
.Where(x => x.ProductName == "Phone")
.ToList();
foreach (var item in results)
{
Console.WriteLine($"{item.ProductName}\t{string.Join(", ", item.OrdersNumbers)}");
}
Console.ReadKey();
}
}
}
I found a related issue here How to index related documents in reverse direction in Ravendb, but this scenario is a bit different. The code above runs if you comment the index creation, but thorws an exception otherwise: "Could not understand query: Variable initializer must be a select query expression".
I need somthing like below SQL but in RavenDb:
select pro.ProductName, ord.OrderNumber from orders ord
inner join items itm on itm.OrderId = ord.Id
inner join products pro on pro.Id = itm.ProductId
where pro.ProductName like '%Phone%';
How to create a search by ProductName and return a list or Orders in RavenDb?
Im using version 3.5(.1)
Consider the main problem as search "entities by related entity property".
Thanks in advance!
Consider storing the product name directly in the order items. That will have two advantages - the order items won't be affected by later product changes, and indexing will be cheaper.
The following code demonstrates both approaches:
public class Product
{
public string Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
}
public class Order
{
public string Id { get; set; }
public string OrderNumber { get; set; }
public decimal Total { get; set; }
public string Customer { get; set; }
public Item[] Items { get; set; }
}
public class Item
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class OrderView
{
public string OrderId { get; set; }
public string OrderNumber { get; set; }
public string[] ProductIds { get; set; }
public string[] ProductNames { get; set; }
}
public class Order_ByItemName : AbstractIndexCreationTask<Order, OrderView>
{
public Order_ByItemName()
{
Map = orders => from order in orders
select new
{
OrderId = order.Id,
OrderNumber = order.OrderNumber,
ProductIds = order.Items.Select(x => x.ProductId).ToArray(),
ProductNames = order.Items.Select(x => x.ProductName).ToArray(),
};
Index(x => x.ProductNames, FieldIndexing.Analyzed);
StoreAllFields(FieldStorage.Yes);
}
}
public class Order_ByProductName : AbstractIndexCreationTask<Order, OrderView>
{
public Order_ByProductName()
{
Map = orders => from order in orders
let products = LoadDocument<Product>(order.Items.Select(x => x.ProductId))
select new
{
OrderId = order.Id,
OrderNumber = order.OrderNumber,
ProductIds = products.Select(x => x.Id).ToArray(),
ProductNames = products.Select(x => x.ProductName).ToArray(),
};
Index(x => x.ProductNames, FieldIndexing.Analyzed);
StoreAllFields(FieldStorage.Yes);
}
}
class Program
{
static void Main(string[] args)
{
var documentStore = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDatabase"
};
documentStore.Initialize();
new Order_ByProductName().Execute(documentStore);
new Order_ByItemName().Execute(documentStore);
using (var session = documentStore.OpenSession())
{
var product1 = new Product() { Id = "products/1", Price = 100, ProductName = "Phone" };
var product2 = new Product() { Id = "products/2", Price = 1000, ProductName = "Laptop" };
var product3 = new Product() { Id = "products/3", Price = 200, ProductName = "Windows Phone" };
session.Store(product1);
session.Store(product2);
session.Store(product3);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var products = session.Query<Product>().ToList();
var order1 = new Order();
order1.Id = "orders/1";
order1.Customer = "Jhon Doe";
order1.OrderNumber = "001";
order1.Items = new Item[] {
new Item { ProductId = products[0].Id, ProductName = products[0].ProductName, Price = products[0].Price, Quantity = 1 },
new Item { ProductId = products[1].Id, ProductName = products[1].ProductName, Price = products[1].Price, Quantity = 1 }
};
order1.Total = order1.Items.Sum(c => (c.Quantity * c.Price));
var order2 = new Order();
order1.Id = "orders/1";
order2.Customer = "Joan Doe";
order2.OrderNumber = "002";
order2.Items = new Item[] {
new Item { ProductId = products[2].Id, ProductName = products[2].ProductName, Price = products[2].Price, Quantity = 1 }
};
order2.Total = order2.Items.Sum(c => (c.Quantity * c.Price));
session.Store(order1);
session.Store(order2);
session.SaveChanges();
}
Thread.Sleep(5000); // wait for indexing
using (var session = documentStore.OpenSession())
{
var itemResults = session
.Query<OrderView, Order_ByItemName>()
.Search(x => x.ProductNames, "Phone")
.ProjectFromIndexFieldsInto<OrderView>()
.ToList();
var results = session
.Query<OrderView, Order_ByProductName>()
.Search(x => x.ProductNames, "Phone")
.ProjectFromIndexFieldsInto<OrderView>()
.ToList();
Console.WriteLine("Order_ByItemName");
foreach (var order in itemResults)
{
Console.WriteLine($"OrderNumber: {order.OrderNumber}");
for (int i = 0; i < order.ProductIds.Length; i++)
{
Console.WriteLine($"Item: {order.ProductIds[i]} - {order.ProductNames[i]}");
}
}
Console.WriteLine("Order_ByProductName");
foreach (var order in results)
{
Console.WriteLine($"OrderNumber: {order.OrderNumber}");
for (int i = 0; i < order.ProductIds.Length; i++)
{
Console.WriteLine($"Item: {order.ProductIds[i]} - {order.ProductNames[i]}");
}
}
Console.ReadKey();
}
}
}

How to count all posts belonging to multiple tags in NHibernate?

I have a many to many relationship:
A post can have many tags
A tag can have many posts
Models:
public class Post
{
public virtual string Title { get; set; }
public virtual string Content{ get; set; }
public virtual User User { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
I want to count all posts that belong to multiple tags but I don't know how to do this in NHibernate. I am not sure if this is the best way to do this but I used this query in MS SQL:
SELECT COUNT(*)
FROM
(
SELECT Posts.Id FROM Posts
INNER JOIN Users ON Posts.UserId=Users.Id
LEFT JOIN TagsPosts ON Posts.Id=TagsPosts.PostId
LEFT JOIN Tags ON TagsPosts.TagId=Tags.Id
WHERE Users.Username='mr.nuub' AND (Tags.Title in ('c#', 'asp.net-mvc'))
GROUP BY Posts.Id
HAVING COUNT(Posts.Id)=2
)t
But NHibernate does not allow subqueries in the from clause. It would be great if someone could show me how to do this in HQL.
I found a way of how to get this result without a sub query and this works with nHibernate Linq. It was actually not that easy because of the subset of linq expressions which are supported by nHibernate... but anyways
query:
var searchTags = new[] { "C#", "C++" };
var result = session.Query<Post>()
.Select(p => new {
Id = p.Id,
Count = p.Tags.Where(t => searchTags.Contains(t.Title)).Count()
})
.Where(s => s.Count >= 2)
.Count();
It produces the following sql statment:
select cast(count(*) as INT) as col_0_0_
from Posts post0_
where (
select cast(count(*) as INT)
from PostsToTags tags1_, Tags tag2_
where post0_.Id=tags1_.Post_id
and tags1_.Tag_id=tag2_.Id
and (tag2_.Title='C#' or tag2_.Title='C++'))>=2
you should be able to build your user restriction into this, I hope.
The following is my test setup and random data which got generated
public class Post
{
public Post()
{
Tags = new List<Tag>();
}
public virtual void AddTag(Tag tag)
{
this.Tags.Add(tag);
tag.Posts.Add(this);
}
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual int Id { get; set; }
}
public class PostMap : ClassMap<Post>
{
public PostMap()
{
Table("Posts");
Id(p => p.Id).GeneratedBy.Native();
Map(p => p.Content);
Map(p => p.Title);
HasManyToMany<Tag>(map => map.Tags).Cascade.All();
}
}
public class Tag
{
public Tag()
{
Posts = new List<Post>();
}
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual int Id { get; set; }
}
public class TagMap : ClassMap<Tag>
{
public TagMap()
{
Table("Tags");
Id(p => p.Id).GeneratedBy.Native();
Map(p => p.Description);
Map(p => p.Title);
HasManyToMany<Post>(map => map.Posts).LazyLoad().Inverse();
}
}
test run:
var sessionFactory = Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2012
.ConnectionString(#"Server=.\SQLExpress;Database=TestDB;Trusted_Connection=True;")
.ShowSql)
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<PostMap>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
.BuildSessionFactory();
using (var session = sessionFactory.OpenSession())
{
var t1 = new Tag() { Title = "C#", Description = "C#" };
session.Save(t1);
var t2 = new Tag() { Title = "C++", Description = "C/C++" };
session.Save(t2);
var t3 = new Tag() { Title = ".Net", Description = "Net" };
session.Save(t3);
var t4 = new Tag() { Title = "Java", Description = "Java" };
session.Save(t4);
var t5 = new Tag() { Title = "lol", Description = "lol" };
session.Save(t5);
var t6 = new Tag() { Title = "rofl", Description = "rofl" };
session.Save(t6);
var tags = session.Query<Tag>().ToList();
var r = new Random();
for (int i = 0; i < 1000; i++)
{
var post = new Post()
{
Title = "Title" + i,
Content = "Something awesome" + i,
};
var manyTags = r.Next(1, 3);
while (post.Tags.Count() < manyTags)
{
var index = r.Next(0, 6);
if (!post.Tags.Contains(tags[index]))
{
post.AddTag(tags[index]);
}
}
session.Save(post);
}
session.Flush();
/* query test */
var searchTags = new[] { "C#", "C++" };
var result = session.Query<Post>()
.Select(p => new {
Id = p.Id,
Count = p.Tags.Where(t => searchTags.Contains(t.Title)).Count()
})
.Where(s => s.Count >= 2)
.Count();
var resultOriginal = session.CreateQuery(#"
SELECT COUNT(*)
FROM
(
SELECT count(Posts.Id)P FROM Posts
LEFT JOIN PostsToTags ON Posts.Id=PostsToTags.Post_id
LEFT JOIN Tags ON PostsToTags.Tag_id=Tags.Id
WHERE Tags.Title in ('c#', 'C++')
GROUP BY Posts.Id
HAVING COUNT(Posts.Id)>=2
)t
").List()[0];
var isEqual = result == (int)resultOriginal;
}
As you can see at the end I do test against your original query (without the users) and it is actually the same count.
In HQL:
var hql = "select count(p) from Post p where p in " +
"(select t.Post from Tag t group by t.Post having count(t.Post) > 1)";
var result = session.Query(hql).UniqueResult<long>();
You can add additional criteria to the subquery if you need to specify tags or other criteria.
Edit : In the future I should read the questions until the last words. I would have seen in HQL...
After some seach, realizing that RowCount removes any grouping in the query ( https://stackoverflow.com/a/8034921/1236044 ). I found a solution using QueryOver and SubQuery which I post here as information.
I find this solution interesting as it offers some modularity, and seprates the counting from the subquery itself, which can be reused as it is.
var searchTags = new[] { "tag1", "tag3" };
var userNames = new[] { "mr.nuub" };
Tag tagAlias = null;
Post postAlias = null;
User userAlias = null;
var postsSubquery =
QueryOver.Of<Post>(() => postAlias)
.JoinAlias(() => postAlias.Tags, () => tagAlias)
.JoinAlias(() => postAlias.User, () => userAlias)
.WhereRestrictionOn(() => tagAlias.Title).IsIn(searchTags)
.AndRestrictionOn(() => userAlias.UserName).IsIn(userNames)
.Where(Restrictions.Gt(Projections.Count<Post>(p => tagAlias.Title), 1));
var numberOfPosts = session.QueryOver<Post>()
.WithSubquery.WhereProperty(p => p.Id).In(postsSubquery.Select(Projections.Group<Post>(p => p.Id)))
.RowCount();
Hope this will help

RavenDB Index no longer returning results

I'm attempting to apply Ayende's order search from here to an existing index.
The current index looks like this:
public class HomeBlurb_IncludeTotalCosts_Search2 : AbstractIndexCreationTask<MPDocument, HomeBlurb_IncludeTotalCosts_Search2.ReduceResult>
{
public class ReduceResult
{
public string Name { get; set; }
public string Constituency { get; set; }
public decimal? AmountPaid { get; set; }
}
public HomeBlurb_IncludeTotalCosts_Search2()
{
Map = mps => from mp in mps
from exp in mp.Expenses
select new
{
mp.Name,
mp.Constituency,
exp.AmountPaid
};
Reduce = results => from result in results
group result by new { result.Name, result.Constituency } into g
select new
{
Name = g.Key.Name,
Constituency = g.Key.Constituency,
AmountPaid = g.Sum(x => x.AmountPaid)
};
Index(x => x.Name, FieldIndexing.Analyzed);
Index(x => x.Constituency, FieldIndexing.Analyzed);
}
}
This index works fine. However when I try to change the Map to:
from mp in mps
from exp in mp.Expenses
select new
{
Query = new object[]{mp.Name,mp.Constituency},
mp.Name,
mp.Constituency,
exp.AmountPaid
};
and the reduce to
from result in results
group result by new { result.Name, result.Constituency } into g
select new
{
Query = "",
Name = g.Key.Name,
Constituency = g.Key.Constituency,
AmountPaid = g.Sum(x => x.AmountPaid)
};
I then get no results when querying on the Query property. If I remove the reduce the index returns data, but it always returns the full MPDocument, which is much more that I was to materialise. Is there a way to use the technique described in the original post that also utilises a reduce?
You can use this in your reduce function:
Query = g.Select(x => x.Query).Where(x => x != null).FirstOrDefault()
In order to query on that field, you need to have a seperate query model and a result model. Here is a full example using your code:
public class MultiTermFieldInMapReduce
{
public class MPDocument
{
public List<Epense> Expenses { get; set; }
public string Name { get; set; }
public string Constituency { get; set; }
public class Epense
{
public decimal? AmountPaid { get; set; }
}
}
public class HomeBlurb_IncludeTotalCosts_Search2 : AbstractIndexCreationTask<MPDocument, HomeBlurb_IncludeTotalCosts_Search2.ReduceResult>
{
public class ReduceResult
{
public string Name { get; set; }
public string Constituency { get; set; }
public decimal? AmountPaid { get; set; }
public object[] Query { get; set; }
}
public class SearchModel
{
public string Name { get; set; }
public string Constituency { get; set; }
public decimal? AmountPaid { get; set; }
public string Query { get; set; }
}
public HomeBlurb_IncludeTotalCosts_Search2()
{
Map = mps => from mp in mps
from exp in mp.Expenses
select new
{
mp.Name,
mp.Constituency,
exp.AmountPaid,
Query = new object[]
{
mp.Name,
mp.Constituency
}
};
Reduce = results => from result in results
group result by new { result.Name, result.Constituency } into g
select new
{
Name = g.Key.Name,
Constituency = g.Key.Constituency,
AmountPaid = g.Sum(x => x.AmountPaid),
Query = g.Select(x => x.Query).Where(x => x != null).FirstOrDefault()
};
Index(x => x.Name, FieldIndexing.Analyzed);
Index(x => x.Constituency, FieldIndexing.Analyzed);
}
}
[Fact]
public void Query_returns_results()
{
using (var store = new EmbeddableDocumentStore { RunInMemory = true }.Initialize())
{
using (var session = store.OpenSession())
{
session.Store(new MapReduceError.MPDocument
{
Name = "test1",
Expenses = new List<MapReduceError.MPDocument.Epense>
{
new MapReduceError.MPDocument.Epense {AmountPaid = 5.5m},
new MapReduceError.MPDocument.Epense {AmountPaid = 5.5m},
new MapReduceError.MPDocument.Epense {AmountPaid = 5.5m},
new MapReduceError.MPDocument.Epense {AmountPaid = 5.5m}
}
});
session.Store(new MapReduceError.MPDocument
{
Name = "test2",
Expenses = new List<MapReduceError.MPDocument.Epense>
{
new MapReduceError.MPDocument.Epense {AmountPaid = 10},
new MapReduceError.MPDocument.Epense {AmountPaid = 10},
new MapReduceError.MPDocument.Epense {AmountPaid = 10},
new MapReduceError.MPDocument.Epense {AmountPaid = 10}
}
});
session.SaveChanges();
}
new HomeBlurb_IncludeTotalCosts_Search2().Execute(store);
using (var session = store.OpenSession())
{
var results =
session.Query
<HomeBlurb_IncludeTotalCosts_Search2.SearchModel, HomeBlurb_IncludeTotalCosts_Search2>()
.Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
.Where(x => x.Query == "test1")
.As<HomeBlurb_IncludeTotalCosts_Search2.ReduceResult>()
.ToList();
Assert.Equal(1, results.Count);
Assert.Equal(22, results.First().AmountPaid);
}
}
}
}
I don't think you need a Map/Reduce index to do this, you seem to only be reducing on MPDocument Name and Constituency, which I guess are unique to each MP.
I think that you want to use TransformResults, so that you can change the shape of the output instead, something like so:
TransformResults =
(database, mps) => from mp in mps
select new
{
mp.Name,
mp.???
< JUST THE BITS YOU WANT RETURNED >
};
Then you query like so:
session.Query<mp>("index name")
.Where(..)
.As<MPQueryResult>()
.ToList()