I am building an restaurantfinder website with EPiServer and I'm using EPiFind to search on the site. I've encountered a problem though. I'm trying to facet a pagetype in the pagetree, although I'm only searching in one specific pagetype. My goal is to be able to print out the titles of the pagetype. So if I search for example Sweden, all cities that's in the pagetree should be listed with their names in the facet.
My code:
if (query == null && tags == null && cities == null)
{
return View();
}
var q = SearchClient.Instance.Search<RestaurantPage>()
.For(query)
.TermsFacetFor(x => x.CityPage.HeadLine);
if (!string.IsNullOrWhiteSpace(cities))
{
q = q.Filter(x => x.HeadLine.MatchCaseInsensitive(cities));
}
var results = q.Select(x => new SearchHit
{
Title = x.HeadLine,
Url = x.LinkURL,
Tag = x.Tags,
Adress = x.Adress,
Latitude = x.Latitude,
Longitude = x.Longitude,
Image = x.RestaurantImage
}).GetResult();
ViewBag.Query = query;
ViewBag.Id = results.ProcessingInfo.ServerDuration;
ViewBag.Hits = results.TotalMatching;
var facets = new List<FacetResult>();
var testFacet = (TermsFacet)results.Facets["CityPage.HeadLine"];
var testLinks = new FacetResult("Cities", testFacet.Terms.Select(x => new FacetLink
{
Text = x.Term,
Count = x.Count,
}));
facets.Add(testLinks);
ViewBag.Filters = new List<FacetLink>();
if (!string.IsNullOrEmpty(tags))
{
ViewBag.Filters.Add(new FacetLink
{
Text = tags,
Url = Url.QueryBuilder(currentPage.ContentLink).AddSegment("?query=" + query).ToString()
});
}
return View(new SearchResult(results, query) {Facets = facets});
In order to retrieve the facets, you could do:
results.TermsFacetFor<RestaurantPage>(x => x.CityPage.HeadLine)
Related
So I'm trying to use a DTO to reshape and return data, it's not working because I'm trying to push in an array of objects (as an IQueryable - which I don't think works) into the DTO, I'm also trying to push in dynamic data into one of the properties, as seen below in the 'hasCurrentUserLiked' property. I need to figure out How to change the objects from IQueryable, into actual objects so they can all be pushed into the DTO and the dynamic data can be worked out.
This is the code
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
// this doesn't work because 'feeds' is an IQueryable, not an object
var like = await hasCurrentUserLiked(user.Id, feeds.id);
// this has the same problem as above
var feedsToReturn = new FeedsForReturnDto
{
Id = feeds.Id,
PhotoUrl = feeds.photoUrl,
Username = feeds.Username,
Description = feeds.Description,
DateAdded = feeds.DateAdded,
IsImage = feeds.IsImage,
hasCurrentUserLiked = like,
Likes = feeds.Likes
}
return await PagedList<UserPhoto>.CreateAsync(feedsToReturn, userParams.PageNumber, userParams.PageSize);
}
I thought that I might be able to get each image.id in a similar way the 'followerIds' are worked out but I couldn't figure out how to get this to work
[EDIT]
As per Enas Osamas answer, I've changed the code to this and I've debugged it, feedsToReturn has the correct info, so it is doing what its supposed to do. The problem I'm having now is that I'm unable to return it as it can't convert the IEnumerable to an IQueryable. I tried adding an explicit cast but that didn't work, I also tried removing the PagedList, and replacing the type to but this didn't work. My is an IQueryable which might be the problem, I tried changing that to an IEnumerable but this would mess up the code in other places.
This is my new code (still returning 'feeds' instead of 'feedsToReturn', will change when it works)
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<UserPhoto>.CreateAsync(feeds, userParams.PageNumber, userParams.PageSize);
}
I also tried changing some of the types around and this is what I came up with. The problem here is this:
'System.Collections.Generic.IEnumerable<cartalk.api.Dtos.feeds.FeedsForReturnDto>' to 'System.Linq.IQueryable<System.Collections.IEnumerable>'
and this problem is with the 'feedsToReturn' in the last line
public async Task<PagedList<IEnumerable>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<IEnumerable>.CreateAsync(feedToReturn, userParams.PageNumber, userParams.PageSize);
}
PagedList code
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source,
int pageNumber, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
[EDIT]
This is the hasCurrentUserLiked function, it works here
public bool checkCurrentUserLiked(int currentUserId, int imageId)
{
var doesCurrentUserLike = _context.PhotoLikes.Where(x => x.LikerId == currentUserId && x.ImageId == imageId);
var value = true;
if (doesCurrentUserLike == null)
{
value = false;
}
else
{
value = true;
}
return value;
}
You can try something like
feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.id),
Likes = feed.Likes
});
This would return an IEnumerable containing all the feeds mapped to your DTO after being enumerated to avoid performing operations against the database
[EDIT]
you can maybe do a left join with the _context.PhotoLikes.
Like this
feeds.GroupJoin(_context.PhotoLikes,
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
(f, p) => new { feed = f, photoLike = p })
.SelectMany(f => f.photoLike.DefaultIfEmpty(),
(f, p) => new FeedsForReturnDto
{
Id = f.feed.Id,
PhotoUrl = f.feed.photoUrl,
Username = f.feed.Username,
Description = f.feed.Description,
DateAdded = f.feed.DateAdded,
IsImage = f.feed.IsImage,
hasCurrentUserLiked = p != null,
Likes = feed.Likes
});
Just note that in this part
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
the datatypes of properties in both objects must match or it won't compile
I have a linq query that returns users/employees with their corresponding supervisors.
List<OrgChartViewModel> OrgChart = new List<OrgChartViewModel>();
var userlist = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist);
return Json(OrgChart.DistinctBy(x => x.id));
However, I need a query that returns the supervisor and their children along with their sub-children without having a sub-array/nested array.
What I have tried :
List<OrgChartViewModel> OrgChart = new List<OrgChartViewModel>();
var userlist = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist);
foreach (var ul in userlist)
{
var userlist2 = (from u in users
select new OrgChartViewModel
{
id = u?.id.ToString(),
pid = u?.pid.ToString(),
name = u?.name,
title = u?.title,
img = u?.img
}).Where(x => x.pid == ul.id).OrderBy(x => x.name).ToList();
OrgChart.AddRange(userlist2);
}
return Json(OrgChart.DistinctBy(x => x.id));
But this only returns the first layer of sub-children. What I want to achieve is to return unlimited layers of sub-children without having sub-arrays.
I'm hoping you can help me with an issue I'm having. I'm new to ElasticSearch and am currently trying to implement a log search method that's going to be filtering through large amounts of logs. Currently, I can make it filter based on selected log types and that works fine, but as soon as I add the second filter, it no longer returns any results.
The only difference between the two filters I've added so far is, one is an int array, the other is a string list as seen in the current code, I've also tried converting it to a string array but to no help.
[Route("logs/search")]
[HttpGet]
public ActionResult Index(int[] logTypes, int entityType, string entitySource)
{
using (var db = new mtaEntities())
{
var targetSources = new List<string>();
switch (entityType)
{
// Username/charactername
case 0:
var characters = db.characters.Where(i => i.charactername.ToLower().Replace("_", " ") == entitySource.ToLower()).ToList();
accounts user = null;
if (characters.Any())
{
var accountId = characters.FirstOrDefault().account;
if (accountId != null)
{
user = db.accounts.FirstOrDefault(i => i.id == accountId);
characters.AddRange(db.characters.Where(i => i.account == user.id));
}
}
else
{
user = db.accounts.FirstOrDefault(i => i.username.ToLower() == entitySource.ToLower());
if (user != null)
{
characters.AddRange(db.characters.Where(i => i.account == user.id).ToList());
}
}
if (user != null)
{
targetSources.Add("AC-" + user.id);
foreach (var item in characters)
{
targetSources.Add("CH-" + item.id);
}
}
break;
}
var filters = new List<Func<QueryContainerDescriptor<Log>, QueryContainer>>();
filters.Add(fq => fq.Terms(te => te.Field(fi => fi.LogType).Terms(logTypes)));
filters.Add(fq => fq.Terms(te => te.Field(fi => fi.SourceUser).Terms(targetSources)));
var searchDescriptor = new SearchDescriptor<Log>();
searchDescriptor.Query(q => q.Bool(b => b.Filter(filters)));
var query = ElasticClient.Search<Log>(searchDescriptor);
var results = query.Documents;
return View(new Logs
{
IndexLastRebuilt = db.settings.FirstOrDefault(i => i.name == "elasticIndexLastRebuilt").value,
LogResults = results.ToList(),
LogTypes = db.logtypes.ToList()
});
}
}
I have two functions, returning expression translated to EF.:
public static Expression<Func<TripLanguage, TripViewModel>> ToSearchModel(ILookup<int, TagViewModel> tags)
{
return tripLanguage => new TripViewModel()
{
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
Tags = tags[tripLanguage.TripId], //This is only different and in function args of course
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}
public static Expression<Func<TripLanguage, TripViewModel>> ToSearchModel()
{
return tripLanguage => new TripViewModel()
{
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}
The only different is Tags collection. Is possible something like call method, without arguments and add Tags attribute which exclude duplicate code? Or Use some inheritance expression?
Thank you for your time.
Change the first method to work with null tags, and provide a null default for it:
public static Expression<Func<TripLanguage,TripViewModel>> ToSearchModel(ILookup<int, TagViewModel> tags = null) {
return tripLanguage => new TripViewModel() {
From = tripLanguage.From,
To = tripLanguage.To,
Annotation = tripLanguage.Description.Truncate(Strings.TRUNCATE_ANOTATION),
Level = tripLanguage.Trip.Level,
BicycleType = tripLanguage.Trip.BicycleType,
UrlId = tripLanguage.UrlId,
Distance = tripLanguage.Trip.Distance,
Tags = tags?[tripLanguage.TripId], // <<== Note the question mark
MainImage = tripLanguage.Trip.Images.OrderBy(s => s.Date).Select(i => new ImageViewModel
{
Filename = i.Filename,
Id = i.Id,
Title = i.Title
}).Take(1)
};
}
I have a problem with LINQ query (see comment) there is a First method and it only shows me the first element.
When I write in the console "Sales Representative" it shows me only the first element of it as in
I would like to get all of data about Sales Representative. How can I do it?
public PracownikDane GetPracownik(string imie)
{
PracownikDane pracownikDane = null;
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
//Employee matchingProduct = database.Employees.First(p => p.Title == imie);
var query = from pros in database.Employees
where pros.Title == imie
select pros;
// Here
Employee pp = query.First();
pracownikDane = new PracownikDane();
pracownikDane.Tytul = pp.Title;
pracownikDane.Imie = pp.FirstName;
pracownikDane.Nazwisko = pp.LastName;
pracownikDane.Kraj = pp.Country;
pracownikDane.Miasto = pp.City;
pracownikDane.Adres = pp.Address;
pracownikDane.Telefon = pp.HomePhone;
pracownikDane.WWW = pp.PhotoPath;
}
return pracownikDane;
}
Right now you are just getting the .First() result from the Query collection:
Employee pp = query.First();
If you want to list all employees you need to iterate through the entire collection.
Now, if you want to return all the employee's you should then store each new "pracownikDane" you create in some sort of IEnumerable
public IEnumerable<PracownikDane> GetPracownik(string imie) {
using (NORTHWNDEntities database = new NORTHWNDEntities())
{
var query = from pros in database.Employees
where pros.Title == imie
select pros;
var EmployeeList = new IEnumerable<PracownikDane>();
foreach(var pp in query)
{
EmployeeList.Add(new PracownikDane()
{
Tytul = pp.Title,
Imie = pp.FirstName,
Nazwisko = pp.LastName,
Kraj = pp.Country,
Miasto = pp.City,
Adres = pp.Address,
Telefon = pp.HomePhone,
WWW = pp.PhotoPath
});
}
return EmployeeList;
}
Then, with this returned List you can then do what ever you wanted with them.