public IEnumerable <CardScore> Search(SearchViewModel model) { var filters = new List <SearchFilter>(); var results = new Dictionary <string, CardScore>(); if (model.HasQuery() && !string.IsNullOrEmpty(model.BasicQuery())) { var queryFilters = new List <SearchFilter>(); queryFilters.Add(new SearchFilter((s, c) => { return(c.Title.IsEqualToLower(s.BasicQuery())); }, 100, "Title matches with '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.NormalizedTitle.IsEqualToLower(s.BasicQuery())); }, 100, "Title matches with '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Title.StartsWithLower(s.BasicQuery())); }, 90, "Title starts with '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.NormalizedTitle.StartsWithLower(s.BasicQuery())); }, 90, "Title starts with '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Title.ContainsLower(s.BasicQuery())); }, 70, "Title includes '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.NormalizedTitle.ContainsLower(s.BasicQuery())); }, 70, "Title includes '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Text.ContainsLower(s.BasicQuery())); }, 50, "Card Text includes '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.OppositeText.ContainsLower(s.BasicQuery())); }, 45, "Card Text includes '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Shadow.ContainsLower(s.BasicQuery())); }, 45, "Shadow Text includes '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Traits.Any(t => t.ToLowerSafe().Equals(s.BasicQuery() + "."))); }, 30, "Trait matches '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.NormalizedTraits.Any(t => t.ToLowerSafe().Equals(s.BasicQuery() + "."))); }, 30, "Trait matches '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Traits.Any(t => t.ToLowerSafe().Contains(s.BasicQuery()))); }, 25, "Trait contains '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.NormalizedTraits.Any(t => t.ToLowerSafe().Contains(s.BasicQuery()))); }, 25, "Trait contains '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Keywords.Any(k => k.ToLowerSafe().Equals(s.BasicQuery()))); }, 15, "Keyword matches '" + model.Query + "'")); queryFilters.Add(new SearchFilter((s, c) => { return(c.Keywords.Any(k => k.ToLowerSafe().Contains(s.BasicQuery()))); }, 15, "Keyword contains '" + model.Query + "'")); filters.Add(new SearchFilter(queryFilters)); } if (model.HasCardType()) { filters.Add(new SearchFilter((s, c) => { return(s.CardTypeMatches(c)); }, 100, "Card Type matches '" + model.CardType.ToEnumDisplayString() + "'")); } if (model.HasCardSubtype()) { filters.Add(new SearchFilter((s, c) => { return(s.CardSubtype == c.CardSubtype); }, 100, "Card Subtype matches '" + model.CardSubtype.ToEnumDisplayString() + "'")); } if (model.HasDeckType()) { filters.Add(new SearchFilter((s, c) => { return(s.DeckType == c.GetDeckType()); }, 100, "Deck Type matches '" + model.DeckType.ToEnumDisplayString() + "'")); } if (model.HasCardSet()) { filters.Add(new SearchFilter((s, c) => { return(s.CardSetMatches(c) || (!string.IsNullOrEmpty(c.CardSet.Cycle) && s.CardSet.ToUpper() == c.CardSet.Cycle.ToUpper())); }, 100, "Card Set matches '" + model.CardSet + "'")); } if (model.HasScenario()) { filters.Add(new SearchFilter((s, c) => { return(scenarioService.BelongsToScenario(c.Slug, c.CardType, s.Scenario)); }, 100, "Scenario matches '" + model.Scenario + "'")); } if (model.HasTrait()) { filters.Add(new SearchFilter((s, c) => { return(c.HasTrait(s.Trait)); }, 100, "Has Trait '" + model.Trait + "'")); } if (model.HasKeyword()) { filters.Add(new SearchFilter((s, c) => { return(c.HasKeyword(s.Keyword)); }, 100, "Has Keyword '" + model.Trait + "'")); } if (model.HasSphere()) { filters.Add(new SearchFilter((s, c) => { return(s.Sphere == c.Sphere); }, 100, "Has Sphere '" + model.Sphere.ToEnumDisplayString() + "'")); } if (model.HasCategory()) { filters.Add(new SearchFilter((s, c) => { return(c.Categories.Any(x => x == s.GetCategory())); }, 100, "Has Category '" + model.Category + "'")); } if (model.HasEncounterCategory()) { filters.Add(new SearchFilter((s, c) => { return(c.EncounterCategories.Any(x => x == s.GetEncounterCategory())); }, 100, "Has Encounter Category '" + model.EncounterCategory + "'")); } if (model.HasQuestCategory()) { filters.Add(new SearchFilter((s, c) => { return(c.QuestCategories.Any(x => x == s.GetQuestCategory())); }, 100, "Has Quest Category '" + model.QuestCategory + "'")); } if (model.HasResourceCost()) { if (model.Cost == "-") { filters.Add(new SearchFilter((s, c) => { return(c.ResourceCost.HasValue && c.ResourceCost.Value == byte.MaxValue); }, 100, "Cost is '-'")); } else if (model.Cost != "X") { filters.Add(new SearchFilter((s, c) => { return(c.ResourceCost.CompareTo(s.CostOperator, s.Cost)); }, 100, "Cost " + model.CostOperator.ToEnumDisplayString() + " '" + model.Cost + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(s.Cost == c.ResourceCostLabel); }, 100, "Cost is 'X'")); } } if (model.Attack.IsDefinedFilter()) { if (model.Attack == "-") { filters.Add(new SearchFilter((s, c) => { return(c.Attack.HasValue && c.Attack.Value == byte.MaxValue); }, 100, "Attack is '-'")); } else if (model.Attack != "X") { filters.Add(new SearchFilter((s, c) => { return(c.Attack.HasValue && c.Attack.Value != byte.MaxValue && c.Attack.CompareTo(s.AttackOp, s.Attack)); }, 100, "Attack " + model.AttackOp.ToEnumDisplayString() + " '" + model.Attack + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.Attack.HasValue && c.Attack.Value != byte.MaxValue && c.IsVariableAttack); }, 100, "Attack is 'X'")); } } if (model.Defense.IsDefinedFilter()) { if (model.Defense == "-") { filters.Add(new SearchFilter((s, c) => { return(c.Defense.HasValue && c.Defense.Value == byte.MaxValue); }, 100, "Defense is '-'")); } else if (model.Defense != "X") { filters.Add(new SearchFilter((s, c) => { return(c.Defense.HasValue && c.Defense.Value != byte.MaxValue && c.Defense.CompareTo(s.DefenseOp, s.Defense)); }, 100, "Defense " + model.DefenseOp.ToEnumDisplayString() + " '" + model.Defense + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.Defense.HasValue && c.Defense.Value != byte.MaxValue && c.IsVariableDefense); }, 100, "Defense is 'X'")); } } if (model.HitPoints.IsDefinedFilter()) { if (model.HitPoints == "-") { filters.Add(new SearchFilter((s, c) => { return(c.HitPoints.HasValue && c.HitPoints.Value == byte.MaxValue); }, 100, "Hit Points are '-'")); } else if (model.HitPoints != "X") { filters.Add(new SearchFilter((s, c) => { return(c.HitPoints.HasValue && c.HitPoints.Value != byte.MaxValue && c.HitPoints.CompareTo(s.HitPointsOp, s.HitPoints)); }, 100, "Hit Points " + model.HitPointsOp.ToEnumDisplayString() + " '" + model.HitPoints + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.HitPoints.HasValue && c.HitPoints.Value != byte.MaxValue && c.IsVariableHitPoints); }, 100, "Hit Points are 'X'")); } } if (model.Willpower.IsDefinedFilter()) { if (model.Willpower == "-") { filters.Add(new SearchFilter((s, c) => { return(c.Willpower.HasValue && c.Willpower.Value == byte.MaxValue); }, 100, "Willpower is '-'")); } else if (model.Willpower != "X") { filters.Add(new SearchFilter((s, c) => { return(c.Willpower.HasValue && c.Willpower.Value != byte.MaxValue && !c.IsVariableWillpower && c.Willpower.CompareTo(s.WillpowerOp, s.Willpower)); }, 100, "Willpower " + model.WillpowerOp.ToEnumDisplayString() + " '" + model.Willpower + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.Willpower.HasValue && c.Willpower.Value != byte.MaxValue && c.IsVariableWillpower); }, 100, "Willpower is 'X'")); } } if (model.Threat.IsDefinedFilter()) { if (model.Threat == "-") { filters.Add(new SearchFilter((s, c) => { return(c.Threat.HasValue && c.Threat.Value == byte.MaxValue); }, 100, "Threat is '-'")); } else if (model.Threat != "X") { filters.Add(new SearchFilter((s, c) => { return(c.Threat.HasValue && c.Threat.Value != byte.MaxValue && !c.IsVariableThreat && c.Threat.CompareTo(s.ThreatOp, s.Threat)); }, 100, "Threat " + model.ThreatOp.ToEnumDisplayString() + " '" + model.Threat + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.Threat.HasValue && c.Threat.Value != byte.MaxValue && c.IsVariableThreat); }, 100, "Threat is 'X'")); } } if (model.QuestPoints.IsDefinedFilter()) { if (model.QuestPoints == "-") { filters.Add(new SearchFilter((s, c) => { return(c.QuestPoints.HasValue && c.QuestPoints.Value == byte.MaxValue); }, 100, "Quest Points are '-'")); } else if (model.QuestPoints != "X") { filters.Add(new SearchFilter((s, c) => { return(c.QuestPoints.HasValue && c.QuestPoints.Value != byte.MaxValue && !c.IsVariableQuestPoints && c.QuestPoints.CompareTo(s.QuestPointsOp, s.QuestPoints)); }, 100, "Quest Points " + model.QuestPointsOp.ToEnumDisplayString() + " '" + model.QuestPoints + "'")); } else { filters.Add(new SearchFilter((s, c) => { return(c.QuestPoints.HasValue && c.QuestPoints.Value != byte.MaxValue && c.IsVariableQuestPoints); }, 100, "Quest Points are 'X'")); } } if (model.Popularity.IsDefinedFilter()) { filters.Add(new SearchFilter((s, c) => { var pop = c.Popularity(getPopularity); return(pop > 0 && pop.CompareTo(s.PopularityOp, s.Popularity)); }, 100, "Popularity " + model.PopularityOp.ToEnumDisplayString() + " '" + model.Popularity + "'")); } if (model.Errata.HasValue && model.Errata.Value != ErrataVersion.Any) { Func <string, double, bool> hasErrata = (slug, version) => { return(noteService.HasErrata(slug, version)); }; var errataFilter = FilterBuilder.ErrataFilter(model.Errata.Value, hasErrata); if (errataFilter != null) { filters.Add(errataFilter); } } if (model.HasThreatCost()) { filters.Add(new SearchFilter((s, c) => { return(c.ThreatCost.CompareTo(s.ThreatCostOperator, s.ThreatCost)); }, 100, "Threat Cost" + model.ThreatCostOperator.ToEnumDisplayString() + " '" + model.ThreatCost + "'")); } if (model.HasEngagementCost()) { filters.Add(new SearchFilter((s, c) => { return(c.EngagementCost.CompareTo(s.EngagementCostOperator, s.EngagementCost)); }, 100, "Engagement Cost" + model.EngagementCostOperator.ToEnumDisplayString() + " '" + model.EngagementCost + "'")); } if (model.HasArtist()) { filters.Add(new SearchFilter((s, c) => { return(s.Artist == c.Artist.Name); }, 100, "Has Artist '" + model.Artist + "'")); } if (model.HasEncounterSet()) { filters.Add(new SearchFilter((s, c) => { return(s.EncounterSet == c.EncounterSet || s.EncounterSet == c.AlternateEncounterSet); }, 100, "Has Encounter Set '" + model.EncounterSet + "'")); } if (model.HasVictoryPoints()) { filters.Add(new SearchFilter((s, c) => { return(s.VictoryPointsMatch(c)); }, 100, "Has Victory Points '" + model.VictoryPoints + "'")); } if (model.HasShadow.HasValue && model.HasShadow.Value != HasShadow.Any) { filters.Add(new SearchFilter((s, c) => { return(s.HasShadow == HasShadow.Yes && !string.IsNullOrEmpty(c.Shadow) || s.HasShadow == HasShadow.No && string.IsNullOrEmpty(c.Shadow)); }, 50, model.HasShadow == HasShadow.Yes ? "Has Shadow Effect" : "Does Not Have Shadow Effect")); } if (model.IsUnique.HasValue && model.IsUnique.Value != Uniqueness.Any) { filters.Add(new SearchFilter((s, c) => { return((s.IsUnique == Uniqueness.Yes && c.IsUnique) || (s.IsUnique == Uniqueness.No && !c.IsUnique)); }, 50, model.IsUnique == Uniqueness.Yes ? "Is Unique" : "Is Not Unique")); } if (model.HasQuest()) { var scenario = scenarioService.GetScenario(model.Quest.ToUrlSafeString()); filters.Add(new SearchFilter((s, c) => { return(!string.IsNullOrEmpty(c.EncounterSet) && scenario.QuestCards.Any(x => (x.Quest != null && x.Quest.EncounterSet != null && x.Quest.EncounterSet == c.EncounterSet) || (x.IncludedEncounterSets != null && x.IncludedEncounterSets.Any(y => y != null && y.Name == c.EncounterSet)))); }, 50, "Has Quest '" + model.Quest + "'")); } var officialSetFilter = false; if (model.SetType.HasValue) { if (model.SetType.Value != SetType.None) { if (model.SetType.Value == SetType.Non_Nightmare) { filters.Add(new SearchFilter((s, c) => { return(c.CardSet.SetType != SetType.Nightmare_Expansion); }, 50f, 0f, "Is Not From A Nightmare Set")); } else if (model.SetType.Value != SetType.ALL_SETS) { filters.Add(new SearchFilter((s, c) => { return((s.SetType == SetType.OFFICIAL && c.CardSet.SetType != SetType.CUSTOM) || (s.SetType.Value == c.CardSet.SetType)); }, 50f, 0f, "Has Set Type '" + model.SetType + "'")); } } else { officialSetFilter = true; } } else { officialSetFilter = true; } var productFilter = FilterBuilder.ProductFilter(model); if (productFilter != null) { filters.Add(productFilter); } if (filters.Count == 0 && !model.IsAdvancedSearch()) { filters.Add(new SearchFilter((s, c) => { return(c.CardType == CardType.Hero && c.CardSet.Name == "Core Set"); }, 100f, "Default Search - Core Set Heroes")); } if (filters.Count > 0) { if (officialSetFilter) { filters.Add(new SearchFilter((s, c) => { return(c.CardSet.SetType != SetType.CUSTOM); }, 100f, 0f, "From an Official release")); } foreach (var card in cards) { foreach (var filter in filters) { var score = filter.Score(model, card); if (results.ContainsKey(card.Slug)) { results[card.Slug].AddDescription(filter.Description(model, card)); results[card.Slug].AddScore(score); /* * var existing = results[card.Slug].Score; * if (score == 0 || existing > 0 && score > existing) * { * results[card.Slug].Score = score; * }*/ } else { results[card.Slug] = new CardScore(card, score, filter.Description(model, card)); } } } } else { foreach (var card in cards) { results[card.Slug] = new CardScore(card, SearchFilter.WeightedScore(card, 1), string.Empty); } } /* * if (!model.IsAdvancedSearch() && isOfficialOnly) * { * //var setNames = cards.Select(x => x.CardSet.Name).Distinct().OrderBy(y => y).ToList(); * //var sn = setNames; * * //If this is not an advanced search and there are no filters then return only the heroes from the Core Set * foreach (var item in cards.Where(x => x.CardSet.Name == "Core Set" && x.Number < 13)) * { * results[item.Slug] = new CardScore(item, 13 - item.Number, string.Empty); * } * }*/ /* * if (!model.Custom && model.SetType != SetType.CUSTOM) * { * var official = new Dictionary<string, CardScore>(); * * foreach (var score in results) * { * if (!model.CardIsCustom(score.Value.Card)) * official.Add(score.Key, score.Value); * } * * results = official; * }*/ if (model.IsRandom()) { var total = results.Values.Where(x => x.Score() > 0).Count(); if (total > 1) { var random = new Random(); var choice = random.Next(0, total - 1); var score = results.Values.Where(x => x.Score() > 0).ToList()[choice]; results = new Dictionary <string, CardScore>(); results[score.Card.Slug] = score; } return(results.Values.ToList()); } var sortedResults = sortService.Sort(model, filters, results); if (model.IsAdvancedSearch()) { sortedResults = advancedSearchService.Search(model, sortedResults); } return(sortedResults); }