public IQueryable <Product> PrepareQuery(CatalogSearchQuery searchQuery, IQueryable <Product> baseQuery = null) { return(GetProductQuery(searchQuery, baseQuery)); }
protected virtual IDictionary <string, FacetGroup> GetFacets(CatalogSearchQuery searchQuery, int totalHits) { var result = new Dictionary <string, FacetGroup>(); var storeId = searchQuery.StoreId ?? _services.StoreContext.CurrentStore.Id; var languageId = searchQuery.LanguageId ?? _services.WorkContext.WorkingLanguage.Id; foreach (var key in searchQuery.FacetDescriptors.Keys) { var descriptor = searchQuery.FacetDescriptors[key]; var facets = new List <Facet>(); var kind = FacetGroup.GetKindByKey("Catalog", key); switch (kind) { case FacetGroupKind.Category: case FacetGroupKind.Brand: case FacetGroupKind.DeliveryTime: case FacetGroupKind.Rating: case FacetGroupKind.Price: if (totalHits == 0 && !descriptor.Values.Any(x => x.IsSelected)) { continue; } break; } if (kind == FacetGroupKind.Category) { var categoryTree = _categoryService.GetCategoryTree(0, false, storeId); var categories = categoryTree.Flatten(false); if (descriptor.MaxChoicesCount > 0) { categories = categories.Take(descriptor.MaxChoicesCount); } var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "Category" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var category in categories) { names.TryGetValue(category.Id, out var label); facets.Add(new Facet(new FacetValue(category.Id, IndexTypeCode.Int32) { IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(category.Id)), Label = label.HasValue() ? label : category.Name, DisplayOrder = category.DisplayOrder })); } } else if (kind == FacetGroupKind.Brand) { var manufacturers = _manufacturerService.GetAllManufacturers(null, storeId); if (descriptor.MaxChoicesCount > 0) { manufacturers = manufacturers.Take(descriptor.MaxChoicesCount).ToList(); } var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "Manufacturer" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var manu in manufacturers) { names.TryGetValue(manu.Id, out var label); facets.Add(new Facet(new FacetValue(manu.Id, IndexTypeCode.Int32) { IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(manu.Id)), Label = label.HasValue() ? label : manu.Name, DisplayOrder = manu.DisplayOrder })); } } else if (kind == FacetGroupKind.DeliveryTime) { var deliveryTimes = _deliveryTimeService.GetAllDeliveryTimes(); var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "DeliveryTime" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var deliveryTime in deliveryTimes) { if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) { break; } names.TryGetValue(deliveryTime.Id, out var label); facets.Add(new Facet(new FacetValue(deliveryTime.Id, IndexTypeCode.Int32) { IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(deliveryTime.Id)), Label = label.HasValue() ? label : deliveryTime.Name, DisplayOrder = deliveryTime.DisplayOrder })); } } else if (kind == FacetGroupKind.Price) { var count = 0; var hasActivePredefinedFacet = false; var minPrice = _productRepository.Table.Where(x => x.Published && !x.Deleted && !x.IsSystemProduct).Min(x => (double)x.Price); var maxPrice = _productRepository.Table.Where(x => x.Published && !x.Deleted && !x.IsSystemProduct).Max(x => (double)x.Price); minPrice = FacetUtility.MakePriceEven(minPrice); maxPrice = FacetUtility.MakePriceEven(maxPrice); for (var i = 0; i < _priceThresholds.Length; ++i) { if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) { break; } var price = _priceThresholds[i]; if (price < minPrice) { continue; } if (price >= maxPrice) { i = int.MaxValue - 1; } var selected = descriptor.Values.Any(x => x.IsSelected && x.Value == null && x.UpperValue != null && (double)x.UpperValue == price); if (selected) { hasActivePredefinedFacet = true; } facets.Add(new Facet(new FacetValue(null, price, IndexTypeCode.Double, false, true) { DisplayOrder = ++count, IsSelected = selected })); } // Add facet for custom price range. var priceDescriptorValue = descriptor.Values.FirstOrDefault(); var customPriceFacetValue = new FacetValue( priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.Value : null, priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.UpperValue : null, IndexTypeCode.Double, true, true); customPriceFacetValue.IsSelected = customPriceFacetValue.Value != null || customPriceFacetValue.UpperValue != null; if (!(totalHits == 0 && !customPriceFacetValue.IsSelected)) { facets.Insert(0, new Facet("custom", customPriceFacetValue)); } } else if (kind == FacetGroupKind.Rating) { foreach (var rating in FacetUtility.GetRatings()) { var newFacet = new Facet(rating); newFacet.Value.IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(rating.Value)); facets.Add(newFacet); } } else if (kind == FacetGroupKind.Availability || kind == FacetGroupKind.NewArrivals) { var value = descriptor.Values.FirstOrDefault(); if (value != null) { if (kind == FacetGroupKind.NewArrivals && totalHits == 0 && !value.IsSelected) { continue; } var newValue = value.Clone(); newValue.Value = true; newValue.TypeCode = IndexTypeCode.Boolean; newValue.IsRange = false; newValue.IsSelected = value.IsSelected; facets.Add(new Facet(newValue)); } } if (facets.Any(x => x.Published)) { //facets.Each(x => $"{key} {x.Value.ToString()}".Dump()); result.Add(key, new FacetGroup( "Catalog", key, descriptor.Label, descriptor.IsMultiSelect, false, descriptor.DisplayOrder, facets.OrderBy(descriptor))); } } return(result); }
protected virtual IQueryable <Product> ApplySearchTerm(IQueryable <Product> query, CatalogSearchQuery searchQuery, out bool isGroupingRequired) { isGroupingRequired = false; var term = searchQuery.Term; var fields = searchQuery.Fields; var languageId = searchQuery.LanguageId ?? 0; if (term.HasValue() && fields != null && fields.Length != 0 && fields.Any(x => x.HasValue())) { isGroupingRequired = true; // SearchMode.ExactMatch doesn't make sense here if (searchQuery.Mode == SearchMode.StartsWith) { query = from p in query join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp from lp in plp.DefaultIfEmpty() where (fields.Contains("name") && p.Name.StartsWith(term)) || (fields.Contains("sku") && p.Sku.StartsWith(term)) || (fields.Contains("shortdescription") && p.ShortDescription.StartsWith(term)) || (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.StartsWith(term)) || (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.StartsWith(term)) select p; } else { query = from p in query join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp from lp in plp.DefaultIfEmpty() where (fields.Contains("name") && p.Name.Contains(term)) || (fields.Contains("sku") && p.Sku.Contains(term)) || (fields.Contains("shortdescription") && p.ShortDescription.Contains(term)) || (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.Contains(term)) || (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.Contains(term)) select p; } } return(query); }
protected virtual IDictionary <string, FacetGroup> GetFacets(CatalogSearchQuery searchQuery, int totalHits) { var result = new Dictionary <string, FacetGroup>(); var languageId = searchQuery.LanguageId ?? _services.WorkContext.WorkingLanguage.Id; foreach (var key in searchQuery.FacetDescriptors.Keys) { var descriptor = searchQuery.FacetDescriptors[key]; var facets = new List <Facet>(); var kind = FacetGroup.GetKindByKey(key); if (kind == FacetGroupKind.Category) { #region Category // order by product count var categoryQuery = from c in _categoryRepository.TableUntracked where !c.Deleted && c.Published join pc in _productCategoryRepository.TableUntracked on c.Id equals pc.CategoryId into pcm from pc in pcm.DefaultIfEmpty() group c by c.Id into grp orderby grp.Count() descending select new { Id = grp.FirstOrDefault().Id, Name = grp.FirstOrDefault().Name, DisplayOrder = grp.FirstOrDefault().DisplayOrder }; if (descriptor.MaxChoicesCount > 0) { categoryQuery = categoryQuery.Take(descriptor.MaxChoicesCount); } var categories = categoryQuery.ToList(); var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "Category" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var category in categories) { var selected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(category.Id)); if (totalHits == 0 && !selected) { continue; } string label = null; names.TryGetValue(category.Id, out label); facets.Add(new Facet(new FacetValue(category.Id, IndexTypeCode.Int32) { IsSelected = selected, Label = label.HasValue() ? label : category.Name, DisplayOrder = category.DisplayOrder })); } #endregion } else if (kind == FacetGroupKind.Brand) { #region Brand // order by product count var manufacturerQuery = from m in _manufacturerRepository.TableUntracked where !m.Deleted && m.Published join pm in _productManufacturerRepository.TableUntracked on m.Id equals pm.ManufacturerId into pmm from pm in pmm.DefaultIfEmpty() group m by m.Id into grp orderby grp.Count() descending select new { Id = grp.FirstOrDefault().Id, Name = grp.FirstOrDefault().Name, DisplayOrder = grp.FirstOrDefault().DisplayOrder }; if (descriptor.MaxChoicesCount > 0) { manufacturerQuery = manufacturerQuery.Take(descriptor.MaxChoicesCount); } var manufacturers = manufacturerQuery.ToList(); var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "Manufacturer" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var manu in manufacturers) { var selected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(manu.Id)); if (totalHits == 0 && !selected) { continue; } string label = null; names.TryGetValue(manu.Id, out label); facets.Add(new Facet(new FacetValue(manu.Id, IndexTypeCode.Int32) { IsSelected = selected, Label = label.HasValue() ? label : manu.Name, DisplayOrder = manu.DisplayOrder })); } #endregion } else if (kind == FacetGroupKind.DeliveryTime) { #region Delivery time var deliveryTimes = _deliveryTimeService.GetAllDeliveryTimes(); var nameQuery = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == "DeliveryTime" && x.LocaleKey == "Name" && x.LanguageId == languageId); var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); foreach (var deliveryTime in deliveryTimes) { if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) { break; } var selected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(deliveryTime.Id)); if (totalHits == 0 && !selected) { continue; } string label = null; names.TryGetValue(deliveryTime.Id, out label); facets.Add(new Facet(new FacetValue(deliveryTime.Id, IndexTypeCode.Int32) { IsSelected = selected, Label = label.HasValue() ? label : deliveryTime.Name, DisplayOrder = deliveryTime.DisplayOrder })); } #endregion } else if (kind == FacetGroupKind.Price) { #region Price var count = 0; var hasActivePredefinedFacet = false; var minPrice = _productRepository.Table.Where(x => !x.Deleted && x.Published).Min(x => (double)x.Price); var maxPrice = _productRepository.Table.Where(x => !x.Deleted && x.Published).Max(x => (double)x.Price); minPrice = FacetUtility.MakePriceEven(minPrice); maxPrice = FacetUtility.MakePriceEven(maxPrice); for (var i = 0; i < _priceThresholds.Length; ++i) { if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) { break; } var price = _priceThresholds[i]; if (price < minPrice) { continue; } if (price >= maxPrice) { i = int.MaxValue - 1; } var selected = descriptor.Values.Any(x => x.IsSelected && x.Value == null && x.UpperValue != null && (double)x.UpperValue == price); if (totalHits == 0 && !selected) { continue; } if (selected) { hasActivePredefinedFacet = true; } facets.Add(new Facet(new FacetValue(null, price, IndexTypeCode.Double, false, true) { DisplayOrder = ++count, IsSelected = selected })); } // Add facet for custom price range. var priceDescriptorValue = descriptor.Values.FirstOrDefault(); var customPriceFacetValue = new FacetValue( priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.Value : null, priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.UpperValue : null, IndexTypeCode.Double, true, true); customPriceFacetValue.IsSelected = customPriceFacetValue.Value != null || customPriceFacetValue.UpperValue != null; if (!(totalHits == 0 && !customPriceFacetValue.IsSelected)) { facets.Insert(0, new Facet("custom", customPriceFacetValue)); } #endregion } else if (kind == FacetGroupKind.Rating) { if (totalHits == 0 && !descriptor.Values.Any(x => x.IsSelected)) { continue; } foreach (var rating in FacetUtility.GetRatings()) { var newFacet = new Facet(rating); newFacet.Value.IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(rating.Value)); facets.Add(newFacet); } } else if (kind == FacetGroupKind.Availability || kind == FacetGroupKind.NewArrivals) { var value = descriptor.Values.FirstOrDefault(); if (value != null && !(totalHits == 0 && !value.IsSelected)) { var newValue = value.Clone(); newValue.Value = true; newValue.TypeCode = IndexTypeCode.Boolean; newValue.IsRange = false; newValue.IsSelected = value.IsSelected; facets.Add(new Facet(newValue)); } } if (facets.Any(x => x.Published)) { //facets.Each(x => $"{key} {x.Value.ToString()}".Dump()); result.Add(key, new FacetGroup( key, descriptor.Label, descriptor.IsMultiSelect, descriptor.DisplayOrder, facets.OrderBy(descriptor))); } } return(result); }
protected virtual IQueryable <Product> GetProductQuery(CatalogSearchQuery searchQuery, IQueryable <Product> baseQuery) { var ordered = false; var utcNow = DateTime.UtcNow; var categoryId = 0; var manufacturerId = 0; var query = baseQuery ?? _productRepository.Table; query = query.Where(x => !x.Deleted && !x.IsSystemProduct); query = ApplySearchTerm(query, searchQuery, out var isGroupingRequired); #region Filters var filters = new List <ISearchFilter>(); FlattenFilters(searchQuery.Filters, filters); var productIds = GetIdList(filters, "id"); if (productIds.Any()) { query = query.Where(x => productIds.Contains(x.Id)); } var categoryIds = GetIdList(filters, "categoryid"); if (categoryIds.Any()) { isGroupingRequired = true; categoryId = categoryIds.First(); if (categoryIds.Count == 1 && categoryId == 0) { // Has no category. query = query.Where(x => x.ProductCategories.Count == 0); } else { query = QueryCategories(query, categoryIds, null); } } var featuredCategoryIds = GetIdList(filters, "featuredcategoryid"); var notFeaturedCategoryIds = GetIdList(filters, "notfeaturedcategoryid"); if (featuredCategoryIds.Any()) { isGroupingRequired = true; categoryId = categoryId == 0 ? featuredCategoryIds.First() : categoryId; query = QueryCategories(query, featuredCategoryIds, true); } if (notFeaturedCategoryIds.Any()) { isGroupingRequired = true; categoryId = categoryId == 0 ? notFeaturedCategoryIds.First() : categoryId; query = QueryCategories(query, notFeaturedCategoryIds, false); } var manufacturerIds = GetIdList(filters, "manufacturerid"); if (manufacturerIds.Any()) { isGroupingRequired = true; manufacturerId = manufacturerIds.First(); if (manufacturerIds.Count == 1 && manufacturerId == 0) { // Has no manufacturer. query = query.Where(x => x.ProductManufacturers.Count == 0); } else { query = QueryManufacturers(query, manufacturerIds, null); } } var featuredManuIds = GetIdList(filters, "featuredmanufacturerid"); var notFeaturedManuIds = GetIdList(filters, "notfeaturedmanufacturerid"); if (featuredManuIds.Any()) { isGroupingRequired = true; manufacturerId = manufacturerId == 0 ? featuredManuIds.First() : manufacturerId; query = QueryManufacturers(query, featuredManuIds, true); } if (notFeaturedManuIds.Any()) { isGroupingRequired = true; manufacturerId = manufacturerId == 0 ? notFeaturedManuIds.First() : manufacturerId; query = QueryManufacturers(query, notFeaturedManuIds, false); } var tagIds = GetIdList(filters, "tagid"); if (tagIds.Any()) { isGroupingRequired = true; query = from p in query from pt in p.ProductTags.Where(pt => tagIds.Contains(pt.Id)) select p; } if (!QuerySettings.IgnoreAcl) { var roleIds = GetIdList(filters, "roleid"); if (roleIds.Any()) { isGroupingRequired = true; query = from p in query join acl in _aclRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = acl.EntityId, pname = acl.EntityName } into pacl from acl in pacl.DefaultIfEmpty() where !p.SubjectToAcl || roleIds.Contains(acl.CustomerRoleId) select p; } } var deliverTimeIds = GetIdList(filters, "deliveryid"); if (deliverTimeIds.Any()) { query = query.Where(x => x.DeliveryTimeId != null && deliverTimeIds.Contains(x.DeliveryTimeId.Value)); } var parentProductIds = GetIdList(filters, "parentid"); if (parentProductIds.Any()) { query = query.Where(x => parentProductIds.Contains(x.ParentGroupedProductId)); } var conditions = GetIdList(filters, "condition"); if (conditions.Any()) { query = query.Where(x => conditions.Contains((int)x.Condition)); } foreach (IAttributeSearchFilter filter in filters) { var rf = filter as IRangeSearchFilter; if (filter.FieldName == "id") { if (rf != null) { var lower = filter.Term as int?; var upper = rf.UpperTerm as int?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => x.Id >= lower.Value); } else { query = query.Where(x => x.Id > lower.Value); } } if (upper.HasValue) { if (rf.IncludesUpper) { query = query.Where(x => x.Id <= upper.Value); } else { query = query.Where(x => x.Id < upper.Value); } } } } else if (filter.FieldName == "categoryid") { if (rf != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rf.UpperTerm as int?) ?? 0)) { isGroupingRequired = true; // Has any category. query = query.Where(x => x.ProductCategories.Count > 0); } } else if (filter.FieldName == "manufacturerid") { if (rf != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rf.UpperTerm as int?) ?? 0)) { isGroupingRequired = true; // Has any manufacturer. query = query.Where(x => x.ProductManufacturers.Count > 0); } } else if (filter.FieldName == "published") { query = query.Where(x => x.Published == (bool)filter.Term); } else if (filter.FieldName == "availablestart") { if (rf != null) { var lower = filter.Term as DateTime?; var upper = rf.UpperTerm as DateTime?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc >= lower.Value); } else { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc > lower.Value); } } if (upper.HasValue) { if (rf.IncludesLower) { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc <= upper.Value); } else { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc < upper.Value); } } } } else if (filter.FieldName == "availableend") { if (rf != null) { var lower = filter.Term as DateTime?; var upper = rf.UpperTerm as DateTime?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc >= lower.Value); } else { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc > lower.Value); } } if (upper.HasValue) { if (rf.IncludesLower) { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc <= upper.Value); } else { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc < upper.Value); } } } } else if (filter.FieldName == "visibility") { var visibility = (ProductVisibility)filter.Term; switch (visibility) { case ProductVisibility.SearchResults: query = query.Where(x => x.Visibility <= visibility); break; default: query = query.Where(x => x.Visibility == visibility); break; } } else if (filter.FieldName == "showonhomepage") { query = query.Where(p => p.ShowOnHomePage == (bool)filter.Term); } else if (filter.FieldName == "download") { query = query.Where(p => p.IsDownload == (bool)filter.Term); } else if (filter.FieldName == "recurring") { query = query.Where(p => p.IsRecurring == (bool)filter.Term); } else if (filter.FieldName == "shipenabled") { query = query.Where(p => p.IsShipEnabled == (bool)filter.Term); } else if (filter.FieldName == "shipfree") { query = query.Where(p => p.IsFreeShipping == (bool)filter.Term); } else if (filter.FieldName == "taxexempt") { query = query.Where(p => p.IsTaxExempt == (bool)filter.Term); } else if (filter.FieldName == "esd") { query = query.Where(p => p.IsEsd == (bool)filter.Term); } else if (filter.FieldName == "discount") { query = query.Where(p => p.HasDiscountsApplied == (bool)filter.Term); } else if (filter.FieldName == "typeid") { query = query.Where(x => x.ProductTypeId == (int)filter.Term); } else if (filter.FieldName == "stockquantity") { if (rf != null) { var lower = filter.Term as int?; var upper = rf.UpperTerm as int?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => x.StockQuantity >= lower.Value); } else { query = query.Where(x => x.StockQuantity > lower.Value); } } if (upper.HasValue) { if (rf.IncludesUpper) { query = query.Where(x => x.StockQuantity <= upper.Value); } else { query = query.Where(x => x.StockQuantity < upper.Value); } } } else { if (filter.Occurence == SearchFilterOccurence.MustNot) { query = query.Where(x => x.StockQuantity != (int)filter.Term); } else { query = query.Where(x => x.StockQuantity == (int)filter.Term); } } } else if (filter.FieldName == "rating") { if (rf != null) { var lower = filter.Term as double?; var upper = rf.UpperTerm as double?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) >= lower.Value); } else { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) > lower.Value); } } if (upper.HasValue) { if (rf.IncludesUpper) { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) <= upper.Value); } else { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) < upper.Value); } } } else { if (filter.Occurence == SearchFilterOccurence.MustNot) { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) != (double)filter.Term); } else { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) == (double)filter.Term); } } } else if (filter.FieldName == "available") { query = query.Where(x => x.ManageInventoryMethodId == (int)ManageInventoryMethod.DontManageStock || (x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStock && (x.StockQuantity > 0 || x.BackorderModeId != (int)BackorderMode.NoBackorders)) || (x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStockByAttributes && x.ProductVariantAttributeCombinations.Any(pvac => pvac.StockQuantity > 0 || pvac.AllowOutOfStockOrders)) ); } else if (filter.FieldName.StartsWith("price")) { if (rf != null) { var lower = filter.Term as double?; var upper = rf.UpperTerm as double?; if (lower.HasValue) { var minPrice = Convert.ToDecimal(lower.Value); query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice >= minPrice)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price >= minPrice)) ); } if (upper.HasValue) { var maxPrice = Convert.ToDecimal(upper); query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice <= maxPrice)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price <= maxPrice)) ); } } else { var price = Convert.ToDecimal(filter.Term); if (filter.Occurence == SearchFilterOccurence.MustNot) { query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice != price)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price != price)) ); } else { query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice == price)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price == price)) ); } } } else if (filter.FieldName == "createdon") { if (rf != null) { var lower = filter.Term as DateTime?; var upper = rf.UpperTerm as DateTime?; if (lower.HasValue) { if (rf.IncludesLower) { query = query.Where(x => x.CreatedOnUtc >= lower.Value); } else { query = query.Where(x => x.CreatedOnUtc > lower.Value); } } if (upper.HasValue) { if (rf.IncludesLower) { query = query.Where(x => x.CreatedOnUtc <= upper.Value); } else { query = query.Where(x => x.CreatedOnUtc < upper.Value); } } } else { if (filter.Occurence == SearchFilterOccurence.MustNot) { query = query.Where(x => x.CreatedOnUtc != (DateTime)filter.Term); } else { query = query.Where(x => x.CreatedOnUtc == (DateTime)filter.Term); } } } else if (filter.FieldName == "storeid") { if (!QuerySettings.IgnoreMultiStore) { var storeId = (int)filter.Term; if (storeId != 0) { isGroupingRequired = true; query = from p in query join sm in _storeMappingRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = sm.EntityId, pname = sm.EntityName } into psm from sm in psm.DefaultIfEmpty() where !p.LimitedToStores || sm.StoreId == storeId select p; } } } } #endregion // Grouping is very slow if there are many products. if (isGroupingRequired) { query = from p in query group p by p.Id into grp orderby grp.Key select grp.FirstOrDefault(); } #region Sorting foreach (var sort in searchQuery.Sorting) { if (sort.FieldName.IsEmpty()) { // Sort by relevance. if (categoryId != 0) { query = OrderBy(ref ordered, query, x => x.ProductCategories.Where(pc => pc.CategoryId == categoryId).FirstOrDefault().DisplayOrder); } else if (manufacturerId != 0) { query = OrderBy(ref ordered, query, x => x.ProductManufacturers.Where(pm => pm.ManufacturerId == manufacturerId).FirstOrDefault().DisplayOrder); } else if (FindFilter(searchQuery.Filters, "parentid") != null) { query = OrderBy(ref ordered, query, x => x.DisplayOrder); } else { query = OrderBy(ref ordered, query, x => x.Name); } } else if (sort.FieldName == "createdon") { query = OrderBy(ref ordered, query, x => x.CreatedOnUtc, sort.Descending); } else if (sort.FieldName == "name") { query = OrderBy(ref ordered, query, x => x.Name, sort.Descending); } else if (sort.FieldName == "price") { query = OrderBy(ref ordered, query, x => x.Price, sort.Descending); } else { query = OrderBy(ref ordered, query, x => x.Name); } } if (!ordered) { if (FindFilter(searchQuery.Filters, "parentid") != null) { query = query.OrderBy(x => x.DisplayOrder); } else { query = query.OrderBy(x => x.Id); } } #endregion return(query); }
protected virtual IQueryable <Product> GetProductQuery(CatalogSearchQuery searchQuery, IQueryable <Product> baseQuery) { var ordered = false; var utcNow = DateTime.UtcNow; var query = baseQuery ?? _productRepository.Table; query = query.Where(x => !x.Deleted); query = ApplySearchTerm(query, searchQuery); #region Filters var filters = new List <ISearchFilter>(); FlattenFilters(searchQuery.Filters, filters); var productIds = GetIdList(filters, "id"); if (productIds.Any()) { query = query.Where(x => productIds.Contains(x.Id)); } var categoryIds = GetIdList(filters, "categoryid"); if (categoryIds.Any()) { if (categoryIds.Count == 1 && categoryIds.First() == 0) { // has no category query = query.Where(x => x.ProductCategories.Count == 0); } else { query = QueryCategories(query, categoryIds, null); } } query = QueryCategories(query, GetIdList(filters, "featuredcategoryid"), true); query = QueryCategories(query, GetIdList(filters, "notfeaturedcategoryid"), false); var manufacturerIds = GetIdList(filters, "manufacturerid"); if (manufacturerIds.Any()) { if (manufacturerIds.Count == 1 && manufacturerIds.First() == 0) { // has no manufacturer query = query.Where(x => x.ProductManufacturers.Count == 0); } else { query = QueryManufacturers(query, manufacturerIds, null); } } query = QueryManufacturers(query, GetIdList(filters, "featuredmanufacturerid"), true); query = QueryManufacturers(query, GetIdList(filters, "notfeaturedmanufacturerid"), false); var tagIds = GetIdList(filters, "tagid"); if (tagIds.Any()) { query = from p in query from pt in p.ProductTags.Where(pt => tagIds.Contains(pt.Id)) select p; } if (!QuerySettings.IgnoreAcl) { var roleIds = GetIdList(filters, "roleid"); if (roleIds.Any()) { query = from p in query join acl in _aclRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = acl.EntityId, pname = acl.EntityName } into pacl from acl in pacl.DefaultIfEmpty() where !p.SubjectToAcl || roleIds.Contains(acl.CustomerRoleId) select p; } } var deliverTimeIds = GetIdList(filters, "deliveryid"); if (deliverTimeIds.Any()) { query = query.Where(x => x.DeliveryTimeId != null && deliverTimeIds.Contains(x.DeliveryTimeId.Value)); } var parentProductIds = GetIdList(filters, "parentid"); if (parentProductIds.Any()) { query = query.Where(x => parentProductIds.Contains(x.ParentGroupedProductId)); } foreach (IAttributeSearchFilter filter in filters) { var rangeFilter = filter as IRangeSearchFilter; if (filter.FieldName == "id") { if (rangeFilter != null) { var lower = filter.Term as int?; var upper = rangeFilter.UpperTerm as int?; if (lower.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => x.Id >= lower.Value); } else { query = query.Where(x => x.Id > lower.Value); } } if (upper.HasValue) { if (rangeFilter.IncludesUpper) { query = query.Where(x => x.Id <= upper.Value); } else { query = query.Where(x => x.Id < upper.Value); } } } } else if (filter.FieldName == "categoryid") { if (rangeFilter != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rangeFilter.UpperTerm as int?) ?? 0)) { // has any category query = query.Where(x => x.ProductCategories.Count > 0); } } else if (filter.FieldName == "manufacturerid") { if (rangeFilter != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rangeFilter.UpperTerm as int?) ?? 0)) { // has any manufacturer query = query.Where(x => x.ProductManufacturers.Count > 0); } } else if (filter.FieldName == "published") { query = query.Where(x => x.Published == (bool)filter.Term); } else if (filter.FieldName == "availablestart") { if (rangeFilter != null) { var lower = filter.Term as DateTime?; var upper = rangeFilter.UpperTerm as DateTime?; if (lower.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc >= lower.Value); } else { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc > lower.Value); } } if (upper.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc <= upper.Value); } else { query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc < upper.Value); } } } } else if (filter.FieldName == "availableend") { if (rangeFilter != null) { var lower = filter.Term as DateTime?; var upper = rangeFilter.UpperTerm as DateTime?; if (lower.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc >= lower.Value); } else { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc > lower.Value); } } if (upper.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc <= upper.Value); } else { query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc < upper.Value); } } } } else if (filter.FieldName == "visibleindividually") { query = query.Where(x => x.VisibleIndividually == (bool)filter.Term); } else if (filter.FieldName == "showonhomepage") { query = query.Where(p => p.ShowOnHomePage == (bool)filter.Term); } else if (filter.FieldName == "typeid") { query = query.Where(x => x.ProductTypeId == (int)filter.Term); } else if (filter.FieldName == "stockquantity") { if (rangeFilter != null) { var lower = filter.Term as int?; var upper = rangeFilter.UpperTerm as int?; if (lower.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => x.StockQuantity >= lower.Value); } else { query = query.Where(x => x.StockQuantity > lower.Value); } } if (upper.HasValue) { if (rangeFilter.IncludesUpper) { query = query.Where(x => x.StockQuantity <= upper.Value); } else { query = query.Where(x => x.StockQuantity < upper.Value); } } } } else if (filter.FieldName == "rating") { query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) >= (double)filter.Term); } else if (filter.FieldName == "available") { query = query.Where(x => !( x.StockQuantity <= 0 && x.BackorderModeId == (int)BackorderMode.NoBackorders && (x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStock || x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStockByAttributes) )); } else if (filter.FieldName.StartsWith("price")) { if (rangeFilter != null) { var lower = filter.Term as double?; var upper = rangeFilter.UpperTerm as double?; if (lower.HasValue) { var minPrice = Convert.ToDecimal(lower.Value); query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice >= minPrice)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price >= minPrice)) ); } if (upper.HasValue) { var maxPrice = Convert.ToDecimal(upper); query = query.Where(x => ((x.SpecialPrice.HasValue && ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && (x.SpecialPrice <= maxPrice)) || ((!x.SpecialPrice.HasValue || ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && (x.Price <= maxPrice)) ); } } } else if (filter.FieldName == "createdon") { if (rangeFilter != null) { var lower = filter.Term as DateTime?; var upper = rangeFilter.UpperTerm as DateTime?; if (lower.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => x.CreatedOnUtc >= lower.Value); } else { query = query.Where(x => x.CreatedOnUtc > lower.Value); } } if (upper.HasValue) { if (rangeFilter.IncludesLower) { query = query.Where(x => x.CreatedOnUtc <= upper.Value); } else { query = query.Where(x => x.CreatedOnUtc < upper.Value); } } } } else if (filter.FieldName == "storeid") { if (!QuerySettings.IgnoreMultiStore) { var storeId = (int)filter.Term; if (storeId != 0) { query = from p in query join sm in _storeMappingRepository.Table on new { pid = p.Id, pname = "Product" } equals new { pid = sm.EntityId, pname = sm.EntityName } into psm from sm in psm.DefaultIfEmpty() where !p.LimitedToStores || sm.StoreId == storeId select p; } } } } #endregion query = query.GroupBy(x => x.Id).Select(x => x.FirstOrDefault()); #region Sorting foreach (var sort in searchQuery.Sorting) { if (sort.FieldName.IsEmpty()) { // sort by relevance if (categoryIds.Any()) { var categoryId = categoryIds.First(); query = OrderBy(ref ordered, query, x => x.ProductCategories.Where(pc => pc.CategoryId == categoryId).FirstOrDefault().DisplayOrder); } else if (manufacturerIds.Any()) { var manufacturerId = manufacturerIds.First(); query = OrderBy(ref ordered, query, x => x.ProductManufacturers.Where(pm => pm.ManufacturerId == manufacturerId).FirstOrDefault().DisplayOrder); } else if (searchQuery.Filters.OfType <IAttributeSearchFilter>().Any(x => x.FieldName == "parentid")) { query = OrderBy(ref ordered, query, x => x.DisplayOrder); } else { query = OrderBy(ref ordered, query, x => x.Name); } } else if (sort.FieldName == "createdon") { query = OrderBy(ref ordered, query, x => x.CreatedOnUtc, sort.Descending); } else if (sort.FieldName == "name") { query = OrderBy(ref ordered, query, x => x.Name, sort.Descending); } else if (sort.FieldName == "price") { query = OrderBy(ref ordered, query, x => x.Price, sort.Descending); } else { query = OrderBy(ref ordered, query, x => x.Name); } } if (!ordered) { query = query.OrderBy(x => x.Id); } #endregion return(query); }
/// <summary> /// Constructor for an instance without any search hits /// </summary> /// <param name="query">Catalog search query</param> public CatalogSearchResult(CatalogSearchQuery query) : this(null, query, 0, () => new List <Product>(), null, null) { }
public IQueryable <Product> PrepareQuery(CatalogSearchQuery searchQuery, IQueryable <Product> baseQuery = null) { var linqCatalogSearchService = _services.Container.ResolveNamed <ICatalogSearchService>("linq"); return(linqCatalogSearchService.PrepareQuery(searchQuery, baseQuery)); }
public CatalogSearchResult Search( CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None, bool direct = false) { Guard.NotNull(searchQuery, nameof(searchQuery)); Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take)); var provider = _indexManager.GetIndexProvider("Catalog"); if (!direct && provider != null) { var indexStore = provider.GetIndexStore("Catalog"); if (indexStore.Exists) { var searchEngine = provider.GetSearchEngine(indexStore, searchQuery); var stepPrefix = searchEngine.GetType().Name + " - "; int totalCount = 0; string[] spellCheckerSuggestions = null; IEnumerable <ISearchHit> searchHits; Func <IList <Product> > hitsFactory = null; IDictionary <string, FacetGroup> facets = null; _services.EventPublisher.Publish(new CatalogSearchingEvent(searchQuery, false)); if (searchQuery.Take > 0) { using (_services.Chronometer.Step(stepPrefix + "Search")) { totalCount = searchEngine.Count(); // Fix paging boundaries if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount) { searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take); } } if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) { using (_services.Chronometer.Step(stepPrefix + "Hits")) { searchHits = searchEngine.Search(); } using (_services.Chronometer.Step(stepPrefix + "Collect")) { var productIds = searchHits.Select(x => x.EntityId).ToArray(); hitsFactory = () => _productService.Value.GetProductsByIds(productIds, loadFlags); } } if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets)) { try { using (_services.Chronometer.Step(stepPrefix + "Facets")) { facets = searchEngine.GetFacetMap(); ApplyFacetLabels(facets); } } catch (Exception ex) { _logger.Error(ex); } } } if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions)) { try { using (_services.Chronometer.Step(stepPrefix + "Spellcheck")) { spellCheckerSuggestions = searchEngine.CheckSpelling(); } } catch (Exception ex) { // Spell checking should not break the search. _logger.Error(ex); } } var result = new CatalogSearchResult( searchEngine, searchQuery, totalCount, hitsFactory, spellCheckerSuggestions, facets); var searchedEvent = new CatalogSearchedEvent(searchQuery, result); _services.EventPublisher.Publish(searchedEvent); return(searchedEvent.Result); } else if (searchQuery.Origin.IsCaseInsensitiveEqual("Search/Search")) { IndexingRequiredNotification(_services, _urlHelper); } } return(SearchDirect(searchQuery)); }
public CatalogSearchingEvent(CatalogSearchQuery query) { Guard.NotNull(query, nameof(query)); Query = query; }