Skip to content

Commit d4fac8e

Browse files
Merge pull request #53 from dereklegenzoff/master
Updates to Facet Graph and Cleaning up JS dependencies
2 parents 076d2ee + a0afede commit d4fac8e

File tree

8 files changed

+462
-202
lines changed

8 files changed

+462
-202
lines changed

02 - Web UI Template/CognitiveSearch.UI/Controllers/HomeController.cs

+12-5
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public SearchResultViewModel SearchView([FromForm]SearchParameters searchParams)
120120
selectedFacets = searchParams.searchFacets,
121121
currentPage = searchParams.currentPage,
122122
searchId = searchidId ?? null,
123-
applicationInstrumentationKey = _configuration.GetSection("InstrumentationKey")?.Value
123+
applicationInstrumentationKey = _configuration.GetSection("InstrumentationKey")?.Value,
124+
facetableFields = _docSearch.Model.Facets.Select(k => k.Name).ToArray()
124125
};
125126
return viewModel;
126127
}
@@ -152,18 +153,24 @@ public IActionResult GetMapCredentials()
152153
}
153154

154155
[HttpPost]
155-
public ActionResult GetGraphData(string query)
156+
public ActionResult GetGraphData(string query, string[] fields, int maxLevels, int maxNodes)
156157
{
157-
string facetsList = _configuration.GetSection("GraphFacet")?.Value;
158+
string[] facetNames = fields;
158159

159-
string[] facetNames = facetsList.Split(new char[] {',',' '}, StringSplitOptions.RemoveEmptyEntries);
160+
if (facetNames == null || facetNames.Length == 0)
161+
{
162+
string facetsList = _configuration.GetSection("GraphFacet")?.Value;
163+
164+
facetNames = facetsList.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
165+
}
160166

161167
if (query == null)
162168
{
163169
query = "*";
164170
}
171+
165172
FacetGraphGenerator graphGenerator = new FacetGraphGenerator(_docSearch);
166-
var graphJson = graphGenerator.GetFacetGraphNodes(query, facetNames.ToList<string>());
173+
var graphJson = graphGenerator.GetFacetGraphNodes(query, facetNames.ToList<string>(), maxLevels, maxNodes);
167174

168175
return Content(graphJson.ToString(), "application/json");
169176
}

02 - Web UI Template/CognitiveSearch.UI/Models/SearchResultViewModel.cs

+2
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ public class SearchResultViewModel
1313
public string searchId { get; set; }
1414

1515
public string applicationInstrumentationKey { get; set; }
16+
17+
public string[] facetableFields { get; set; }
1618
}
1719
}

02 - Web UI Template/CognitiveSearch.UI/Search/FacetGraphGenerator.cs

+93-41
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ public NodeInfo(int index, int colorId)
1313
{
1414
Index = index;
1515
ColorId = colorId;
16+
LayerCornerStone = -1;
1617
}
1718
public int Index { get; set; }
1819
public int ColorId { get; set; }
20+
public int Layer { get; set; }
21+
public int Distance { get; set; }
22+
public int ChildCount { get; set; }
23+
public int LayerCornerStone { get; set; }
1924
}
2025

2126
public class FacetGraphGenerator
@@ -27,89 +32,135 @@ public FacetGraphGenerator(DocumentSearchClient searchClient)
2732
_searchHelper = searchClient;
2833
}
2934

30-
public JObject GetFacetGraphNodes(string q, List<string> facetNames)
35+
public JObject GetFacetGraphNodes(string q, List<string> facetNames, int maxLevels, int maxNodes)
3136
{
32-
// Calculate nodes for 3 levels
37+
// Calculate nodes for N levels
3338
JObject dataset = new JObject();
34-
int MaxEdges = 50;
35-
int MaxLevels = 3;
36-
int CurrentLevel = 1;
3739
int CurrentNodes = 0;
40+
int originalDistance = 100;
3841

3942
List<FDGraphEdges> FDEdgeList = new List<FDGraphEdges>();
4043
// Create a node map that will map a facet to a node - nodemap[0] always equals the q term
4144

42-
Dictionary<string, NodeInfo> NodeMap = new Dictionary<string, NodeInfo>();
43-
44-
NodeMap[q] = new NodeInfo(CurrentNodes, 0);
45+
var NodeMap = new Dictionary<string, NodeInfo>();
46+
47+
NodeMap[q] = new NodeInfo(CurrentNodes, 0)
48+
{
49+
Distance = originalDistance,
50+
Layer = 0
51+
};
4552

4653
// If blank search, assume they want to search everything
4754
if (string.IsNullOrWhiteSpace(q))
4855
{
4956
q = "*";
5057
}
5158

59+
List<string> currentLevelTerms = new List<string>();
60+
5261
List<string> NextLevelTerms = new List<string>();
5362
NextLevelTerms.Add(q);
5463

55-
// Iterate through the nodes up to 3 levels deep to build the nodes or when I hit max number of nodes
56-
while ((NextLevelTerms.Count() > 0) && (CurrentLevel <= MaxLevels) && (FDEdgeList.Count() < MaxEdges))
64+
// Iterate through the nodes up to MaxLevels deep to build the nodes or when I hit max number of nodes
65+
for (var CurrentLevel = 0; CurrentLevel < maxLevels && maxNodes > 0; ++CurrentLevel, maxNodes /= 2)
5766
{
58-
q = NextLevelTerms.First();
59-
NextLevelTerms.Remove(q);
60-
if (NextLevelTerms.Count() == 0)
61-
{
62-
CurrentLevel++;
63-
}
64-
DocumentSearchResult<Document> response = _searchHelper.GetFacets(q, facetNames, 10);
65-
if (response != null)
67+
currentLevelTerms = NextLevelTerms.ToList();
68+
NextLevelTerms.Clear();
69+
var levelNodeCount = 0;
70+
71+
NodeInfo densestNodeThisLayer = null;
72+
var density = 0;
73+
74+
foreach (var k in NodeMap)
75+
k.Value.Distance += originalDistance;
76+
77+
foreach (var t in currentLevelTerms)
6678
{
67-
int facetColor = 0;
68-
69-
foreach (var facetName in facetNames)
79+
if (levelNodeCount >= maxNodes)
80+
break;
81+
82+
int facetsToGrab = 10;
83+
if (maxNodes < 10)
7084
{
71-
IList<FacetResult> facetVals = (response.Facets)[facetName];
72-
facetColor++;
85+
facetsToGrab = maxNodes;
86+
}
87+
DocumentSearchResult<Document> response = _searchHelper.GetFacets(t, facetNames, facetsToGrab);
88+
if (response != null)
89+
{
90+
int facetColor = 0;
7391

74-
foreach (FacetResult facet in facetVals)
92+
foreach (var facetName in facetNames)
7593
{
76-
NodeInfo nodeInfo = new NodeInfo(-1,-1);
77-
if (NodeMap.TryGetValue(facet.Value.ToString(), out nodeInfo) == false)
78-
{
79-
// This is a new node
80-
CurrentNodes++;
81-
int node = CurrentNodes;
82-
NodeMap[facet.Value.ToString()] = new NodeInfo(node, facetColor);
83-
}
94+
var facetVals = (response.Facets)[facetName];
95+
facetColor++;
8496

85-
// Add this facet to the fd list
86-
if (NodeMap[q] != NodeMap[facet.Value.ToString()])
97+
foreach (FacetResult facet in facetVals)
8798
{
88-
FDEdgeList.Add(new FDGraphEdges { source = NodeMap[q].Index, target = NodeMap[facet.Value.ToString()].Index });
89-
if (CurrentLevel < MaxLevels)
99+
var facetValue = facet.Value.ToString();
100+
NodeInfo nodeInfo = new NodeInfo(-1, -1);
101+
if (NodeMap.TryGetValue(facetValue, out nodeInfo) == false)
90102
{
91-
NextLevelTerms.Add(facet.Value.ToString());
103+
// This is a new node
104+
++levelNodeCount;
105+
NodeMap[facetValue] = new NodeInfo(++CurrentNodes, facetColor)
106+
{
107+
Distance = originalDistance,
108+
Layer = CurrentLevel + 1
109+
};
110+
111+
if (CurrentLevel < maxLevels)
112+
{
113+
NextLevelTerms.Add(facetValue);
114+
}
115+
}
116+
117+
// Add this facet to the fd list
118+
var newNode = NodeMap[facetValue];
119+
var oldNode = NodeMap[t];
120+
if (oldNode != newNode)
121+
{
122+
oldNode.ChildCount += 1;
123+
if (densestNodeThisLayer == null || oldNode.ChildCount > density)
124+
{
125+
density = oldNode.ChildCount;
126+
densestNodeThisLayer = oldNode;
127+
}
128+
129+
FDEdgeList.Add(new FDGraphEdges
130+
{
131+
source = oldNode.Index,
132+
target = newNode.Index,
133+
distance = newNode.Distance
134+
});
92135
}
93136
}
94137
}
95138
}
96139
}
140+
141+
if (densestNodeThisLayer != null)
142+
densestNodeThisLayer.LayerCornerStone = CurrentLevel;
97143
}
98144

99145
// Create nodes
100146
JArray nodes = new JArray();
101-
int nodeNumber = 0;
102147
foreach (KeyValuePair<string, NodeInfo> entry in NodeMap)
103148
{
104-
nodes.Add(JObject.Parse("{name: \"" + entry.Key.Replace("\"", "") + "\"" + ", id: " + entry.Value.Index + ", color: " + entry.Value.ColorId + "}"));
105-
nodeNumber++;
149+
nodes.Add(JObject.FromObject(new
150+
{
151+
name = entry.Key,
152+
id = entry.Value.Index,
153+
color = entry.Value.ColorId,
154+
layer = entry.Value.Layer,
155+
cornerStone = entry.Value.LayerCornerStone
156+
}));
106157
}
107158

108159
// Create edges
109160
JArray edges = new JArray();
110161
foreach (FDGraphEdges entry in FDEdgeList)
111162
{
112-
edges.Add(JObject.Parse("{source: " + entry.source + ", target: " + entry.target + "}"));
163+
edges.Add(JObject.FromObject(entry));
113164
}
114165

115166
dataset.Add(new JProperty("links", edges));
@@ -122,6 +173,7 @@ public class FDGraphEdges
122173
{
123174
public int source { get; set; }
124175
public int target { get; set; }
176+
public int distance { get; set; }
125177
}
126178
}
127179
}

02 - Web UI Template/CognitiveSearch.UI/Views/Home/Search.cshtml

+35-1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
<span class="input-group-btn">
102102
<button class="btn btn-default" onclick="SearchEntities();"><span class="ms-Icon ms-Icon--Search"></span></button>
103103
</span>
104+
104105
<div style="display:inline;position: relative;top:-10px;left:10px;">
105106
<span id="entity-loading-indicator" class="ms-Spinner ms-Spinner--large" style="display:none;"></span>
106107
</div>
@@ -109,7 +110,40 @@
109110
<br />
110111
<button style="width:100%" class="btn btn-default" onclick="EntityMapClick()">Return to Search Results</button>
111112
</div>
112-
<svg width="1430" height="600"></svg>
113+
<div class="col-md-5"></div>
114+
<div class="col-md-2">
115+
<label for="slider-currentMaxNodes">Max Levels</label>
116+
<label id="lbl-currentMaxLevels" style="float:right">0</label>
117+
<input type="range" class="custom-range" min="1" max="5" id="slider-currentMaxLevels" oninput="changeMaxLevels(this.value, false)" onchange="changeMaxLevels(this.value, true)">
118+
</div>
119+
<div class="col-md-2">
120+
<label for="slider-currentMaxNodes">Max Nodes</label>
121+
<label id="lbl-currentMaxNodes" style="float:right">0</label>
122+
<input type="range" class="custom-range" min="3" max="30" id="slider-currentMaxNodes" oninput="changeMaxNodes(this.value, false)" onchange="changeMaxNodes(this.value, true)">
123+
</div>
124+
<div class="col-md-1">
125+
<button class="btn btn-default dropdown-toggle" type="button"
126+
id="dropdownMenu1" data-toggle="dropdown"
127+
aria-haspopup="true" aria-expanded="true"
128+
float="right">
129+
<strong>Select Facets</strong>
130+
<span class="caret"></span>
131+
</button>
132+
133+
<ul class="dropdown-menu checkbox-menu allow-focus dropdown-graphfields">
134+
@foreach (var f in Model.facetableFields)
135+
{
136+
<li>
137+
<label>
138+
<input value="@f" type="checkbox"> @f
139+
</label>
140+
</li>
141+
}
142+
</ul>
143+
</div>
144+
<div class="col-md-10">
145+
<svg height="600px"></svg>
146+
</div>
113147
</div>
114148
</div>
115149

02 - Web UI Template/CognitiveSearch.UI/Views/Shared/_Layout.cshtml

+7-37
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,24 @@
1616
<link rel="stylesheet" href="~/lib/fabric-ui/fabric-9.4.0.scoped.css" />
1717
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
1818

19-
<environment exclude="Development">
20-
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
21-
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
22-
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
23-
crossorigin="anonymous"
24-
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" />
25-
</environment>
19+
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
20+
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css">
21+
2622
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
2723
<link rel="stylesheet" href="~/css/navbar.css" asp-append-version="true" />
24+
<link rel="stylesheet" href="~/css/colors.css" />
25+
2826
<script src="~/lib/jquery/dist/jquery.js"></script>
2927
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
30-
<environment exclude="Development">
31-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
32-
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
33-
asp-fallback-test="window.jQuery"
34-
crossorigin="anonymous"
35-
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
36-
</script>
37-
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
38-
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
39-
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
40-
crossorigin="anonymous"
41-
integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o">
42-
</script>
43-
</environment>
28+
4429
<script src="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.2.0/js/fabric.min.js"></script>
4530
<script src="https://d3js.org/d3.v4.min.js"></script>
4631
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
4732
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
4833
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
4934
<script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
50-
51-
<link rel="stylesheet" href="~/css/colors.css" />
52-
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
53-
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.2.0/css/fabric.components.min.css" />
54-
55-
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css">
56-
35+
5736
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
58-
59-
<script src="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-js/1.2.0/js/fabric.min.js"></script>
60-
<script src="https://d3js.org/d3.v4.min.js"></script>
61-
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
6237
</head>
6338
<body>
6439
@await Html.PartialAsync("_Header")
@@ -69,17 +44,13 @@
6944

7045
@await Html.PartialAsync("_Footer")
7146

72-
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
73-
7447
<script type="text/javascript">
7548
// Change JQueryUI plugin names to fix name collision with Bootstrap.
7649
$.widget.bridge('uitooltip', $.ui.tooltip);
7750
$.widget.bridge('uibutton', $.ui.button);
7851
</script>
7952
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
8053

81-
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
82-
<script src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js"></script>
8354
<script src="~/js/tags.js"></script>
8455
<script src="~/js/details.js"></script>
8556
<script src="~/js/appInsights.js"></script>
@@ -101,7 +72,6 @@
10172
});
10273
})
10374
104-
apiUrl = '@AppConfig.ApiConfig.Url';
10575
customizable = '@AppConfig.Customizable';
10676
</script>
10777

02 - Web UI Template/CognitiveSearch.UI/wwwroot/css/site.css

+4-1
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,6 @@ ul.breadcrumb li {
609609

610610
.ms-Icon {
611611
position: relative;
612-
top: 1px;
613612
display: inline-block;
614613
font-style: normal;
615614
font-weight: normal;
@@ -669,4 +668,8 @@ body {
669668
font-size: 14px;
670669
font-weight: bold;
671670
margin-bottom: 5px;
671+
}
672+
673+
.dropdown-graphfields {
674+
width: 200px;
672675
}

0 commit comments

Comments
 (0)