Hi All,
I am trying to index data of Grid editor in examine , my problem is i have complex data stored in my grid editor let say array of arrays , so how can i achieve this?. do i have just to make a model smiler to my grid data or anything addition i need to do?
Hi Anders,
can you provide me some pointers to achieve this? because if it not possible with the solution we have discussed i need to look for other options and i am running short of time :(
It is possible. I just haven't had the time to answer yet. I will look at it in a few hours when I'm back at my computer ;)
Anyways, can you provide some further information about the editor? Eg. an example of the JSON your editor is generating? That would help me give a better answer.
Hi Anders ,
my json is being generated is something like bellow
{
"name": "1 column layout",
"sections": [
{
"grid": 12,
"rows": [
{
"name": "Row",
"areas": [
{
"grid": 12,
"allowAll": false,
"allowed": [
"paragraphGroup"
],
"controls": [
{
"value": {
"focalPoint": {
"left": 0.5,
"top": 0.5
},
"id": 3071,
"image": "/media/1005/why-automate-testing.jpg",
"imageRowId": "pGropEditor2"
},
"editor": {
"name": "Paragraph Group",
"alias": "paragraphGroup",
"view": "/app_plugins/ParagraphGroupEditor/ParagraphGroup.html",
"render": null,
"icon": "icon-movie-alt",
"config": {}
},
"id": 4,
"tabelsData": [
{
"heading": "tab1",
"imagePath": "/media/1010/test.png",
"id": "pGropEditor1",
"content": "yogesh",
"revertOrder": false
},
{
"heading": "tab2",
"imagePath": "/media/1005/why-automate-testing.jpg",
"id": "pGropEditor2",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": true
},
{
"heading": "tab3",
"imagePath": null,
"id": "pGropEditor3",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": false
},
{
"heading": "tab4",
"imagePath": null,
"id": "pGropEditor4",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": true
}
],
"heading": "Main Paragraph Heading",
"tabData": [
{
"heading": "tab1",
"imagePath": "/media/1010/test.png",
"id": "pGropEditor1",
"content": "yogesh",
"revertOrder": false
},
{
"heading": "tab2",
"imagePath": "/media/1005/why-automate-testing.jpg",
"id": "pGropEditor2",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": true
},
{
"heading": "tab3",
"imagePath": null,
"id": "pGropEditor3",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": false
},
{
"heading": "tab4",
"imagePath": null,
"id": "pGropEditor4",
"content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i",
"revertOrder": true
}
],
"mainHeading": "Main Paragraph Heading"
}
]
}
],
"id": "59195606-2ce2-d82a-6178-12091aa8898b"
}
]
}
]
}
Tablesdata and mainHeading are the object i need to index
It is however worth mentioning that the way you're saving your data is a bit wrong. In the JSON you have added properties directly to the object representing the control, whereas they should ideally be added to the value object.
So rather than saving tabelsData to $scope.control.tabelsData, you should ideally save it to $scope.control.value.tabelsData.
Working with the existing editor / JSON value
My package has a converter system that lets you add new models for your custom editors. Since your data isn't saved as the value object, we can't utilize this system.
Instead we can work directly with the JObject's and JArray's representing the JSON. You can see this little example on how to read the different values:
using System;
using Newtonsoft.Json.Linq;
using Skybrud.Umbraco.GridData;
using Skybrud.Umbraco.GridData.Extensions.Json;
namespace ConsoleApplication1 {
public class Hest {
public static void Test(GridDataModel model) {
foreach (GridControl control in model.GetAllControls("paragraphGroup")) {
// Get the value of "mainHeading"
string mainHeading = control.JObject.GetString("mainHeading");
// Get the value of "heading"
string heading = control.JObject.GetString("heading");
// Write to the console (or add to index)
if (!String.IsNullOrWhiteSpace(mainHeading)) { Console.WriteLine(mainHeading); }
if (!String.IsNullOrWhiteSpace(heading)) { Console.WriteLine(heading); }
// Get the array representing "tabelsData"
JArray tabelsData = control.JObject.GetArray("tabelsData");
// Make sure to check whether the array is null
if (tabelsData != null) {
// Iterate through the objects in the array
foreach (JObject obj in tabelsData) {
// get the "content" property
string content = obj.GetString("content");
// Write to the console (or add to index)
if (!String.IsNullOrWhiteSpace(content)) { Console.WriteLine(content); }
}
}
}
}
}
}
My example simply writes the values to the console, but you could just as easily add them to the search index ;)
Using the build-in converter system
Creating a custom converter has a few steps, so it may take a bit more work to get implemented. A benefit is that it will make your code and views cleaner.
This also assumes that you have fixed your editor to save your data to $scope.control.value instead of just $scope.control.
First you need to implement a model representing your JSON data. I have done this for you with the ParagraphGroupControlValue and ParagraphGroupTableData classes. I haven't added all properties though.
ParagraphGroupControlValue.cs:
using Newtonsoft.Json.Linq;
using Skybrud.Umbraco.GridData;
using Skybrud.Umbraco.GridData.Extensions.Json;
using Skybrud.Umbraco.GridData.Interfaces;
using Skybrud.Umbraco.GridData.Json;
namespace ConsoleApplication1 {
public class ParagraphGroupControlValue : GridJsonObject, IGridControlValue {
#region Properties
public GridControl Control { get; private set; }
public string MainHeading { get; private set; }
public string Heading { get; private set; }
public ParagraphGroupTableData[] TableData { get; private set; }
#endregion
#region Constructors
protected ParagraphGroupControlValue(JObject obj) : base(obj) { }
#endregion
#region Static methods
public static ParagraphGroupControlValue Parse(GridControl control, JObject obj) {
if (obj == null) return null;
return new ParagraphGroupControlValue(obj) {
Control = control,
MainHeading = obj.GetString("mainHeading"),
Heading = obj.GetString("heading"),
TableData = obj.GetArray("tabelsData", ParagraphGroupTableData.Parse) ?? new ParagraphGroupTableData[0]
};
}
#endregion
}
}
ParagraphGroupTableData.cs:
using Newtonsoft.Json.Linq;
using Skybrud.Umbraco.GridData.Extensions.Json;
using Skybrud.Umbraco.GridData.Json;
namespace ConsoleApplication1 {
public class ParagraphGroupTableData : GridJsonObject {
#region Properties
public string Heading { get; private set; }
public string ImagePath { get; private set; }
public string Id { get; private set; }
public string Content { get; private set; }
public bool RevertOrder { get; private set; }
#endregion
#region Constructors
protected ParagraphGroupTableData(JObject obj) : base(obj) { }
#endregion
#region Static methods
public static ParagraphGroupTableData Parse(JObject obj) {
if (obj == null) return null;
return new ParagraphGroupTableData(obj) {
Heading = obj.GetString("heading"),
ImagePath = obj.GetString("imagePath"),
Id = obj.GetString("id"),
Content = obj.GetString("content"),
RevertOrder = obj.GetBoolean("revertOrder")
};
}
#endregion
}
}
The first class represents the object stored in the value property, while the second class represents each object in the tabelsData array.
The next step is to implement a custom converter - this is done by implementing the IGridConverter interface. For your editor, this could look like:
using Newtonsoft.Json.Linq;
using Skybrud.Umbraco.GridData;
using Skybrud.Umbraco.GridData.Extensions.Json;
using Skybrud.Umbraco.GridData.Interfaces;
using Skybrud.Umbraco.GridData.Rendering;
namespace ConsoleApplication1 {
public class ParagraphGroupGridConverter : IGridConverter {
public virtual bool ConvertControlValue(GridControl control, JToken token, out IGridControlValue value) {
value = null;
switch (control.Editor.Alias) {
case "paragraphGroup":
value = ParagraphGroupControlValue.Parse(control, token as JObject);
break;
}
return value != null;
}
public virtual bool ConvertEditorConfig(GridEditor editor, JToken token, out IGridEditorConfig config) {
config = null;
return false;
}
public virtual bool GetControlWrapper(GridControl control, out GridControlWrapper wrapper) {
wrapper = null;
switch (control.Editor.Alias) {
case "paragraphGroup":
wrapper = control.GetControlWrapper<ParagraphGroupControlValue>();
break;
}
return wrapper != null;
}
}
}
To quickly summarize the class, the ConvertControlValue method makes sure the value is properly converted - eg. so that you can call control.GetValue<ParagraphGroupControlValue>().
The ConvertEditorConfig method convert the configuration of the editor. Not sure whether you're using the configuration for anything, the converter does nothing at that point.
Finally the GetControlWrapper method tells my package how it should initialize a wrapper for the class. The wrapper is something that can be used in your views, so it isn't really necessary for indexing.
Since the both the model and the converter are now in place, the last step is to tell my package about the converter. The converted should be added during Umbraco startup, which can be done similar to:
using Skybrud.Umbraco.GridData;
using Umbraco.Core;
namespace ConsoleApplication1 {
public class Startup : IApplicationEventHandler {
public void OnApplicationStarted(UmbracoApplicationBase umbraco, ApplicationContext context) {
GridContext.Current.Converters.Add(new ParagraphGroupGridConverter());
}
public void OnApplicationStarting(UmbracoApplicationBase umbraco, ApplicationContext context) { }
public void OnApplicationInitialized(UmbracoApplicationBase umbraco, ApplicationContext context) { }
}
}
So rather than the first code example in the top of this post, the same class could now look like:
using System;
using Skybrud.Umbraco.GridData;
namespace ConsoleApplication1 {
public class Hest {
public static void Test(GridDataModel model) {
foreach (GridControl control in model.GetAllControls("paragraphGroup")) {
// Get the strongly typed value
ParagraphGroupControlValue value = control.GetValue<ParagraphGroupControlValue>();
// Write to the console (or add to index)
if (!String.IsNullOrWhiteSpace(value.MainHeading)) { Console.WriteLine(value.MainHeading); }
if (!String.IsNullOrWhiteSpace(value.Heading)) { Console.WriteLine(value.Heading); }
// Iterate through the table data in the array
foreach (ParagraphGroupTableData data in value.TableData) {
// Write to the console (or add to index)
if (!String.IsNullOrWhiteSpace(data.Content)) { Console.WriteLine(data.Content); }
}
}
}
}
}
I know that working with the grid can be a bit complex, so let me know if you have any further questions ;)
i am going to change my Grid Controller code (adding the value part) and then implementing This solution , i will get back to you by today EOD with more question ;)
Thanks for guiding me to clean my code it looks way better then it was :)
I Have implemented your solution and checked indexes by Luke.net and good news is it working :) , Indexes are being created for Paragraph Group editor grid control . Now i have few queries and to do implementation , for which i want to have suggestions from you before following any approach
I have my website structured in a way where i have two main nodes -1st one is My web site and second one is grid repository in grid repository i used to create Content using my grid editors and in my website node i used to pick those created content by multi node tree picker . i have followed this approach because i wanted to use my content to be reusable and can be picked up in container manner , so now the situation is my implementation indexes grid repository content one by one and will show results of same but what i want is it should index the grid repository node and in the search result page it should redirect user into my website node where the content actually picked (not where it is created which is currently the case) . so any suggestions to achieve this?
how can i create custom fields in External Indexer?
The way I understand you, you have some kind of grid picker on your regular pages? Then when these pages are being indexed, you could make lookups in the content cache for the selected grid pages. How to accomplish this depends a lot on your solution and current code, so I might not be able to help much with this one.
If you have a look at my example, I'm adding the combined search value fro the grid into a new field e.Fields["content"] = combined.ToString();. So to add a new field, you could jsut do something like e.Fields["myNewField"] = "Hello World";.
1-Yes i have grid picker in my regular pages , these pages are not getting index as per the data they show to the user because they contains only Id of content
2-Ok Thanks , i thought we have to sett this "Content" property on web config or somewhere
I am working on 1 point , i will post here if i will come across a solution
Hi Anders,
i m trying to create my own custom index set Instead of using External index set , but not able to get "content" field in it . my configuration is as bellow
Index complex data of grid editor
Hi All, I am trying to index data of Grid editor in examine , my problem is i have complex data stored in my grid editor let say array of arrays , so how can i achieve this?. do i have just to make a model smiler to my grid data or anything addition i need to do?
Yogesh
Hi Anders, can you provide me some pointers to achieve this? because if it not possible with the solution we have discussed i need to look for other options and i am running short of time :(
Yogesh
It is possible. I just haven't had the time to answer yet. I will look at it in a few hours when I'm back at my computer ;)
Anyways, can you provide some further information about the editor? Eg. an example of the JSON your editor is generating? That would help me give a better answer.
Hi Anders , my json is being generated is something like bellow
{ "name": "1 column layout", "sections": [ { "grid": 12, "rows": [ { "name": "Row", "areas": [ { "grid": 12, "allowAll": false, "allowed": [ "paragraphGroup" ], "controls": [ { "value": { "focalPoint": { "left": 0.5, "top": 0.5 }, "id": 3071, "image": "/media/1005/why-automate-testing.jpg", "imageRowId": "pGropEditor2" }, "editor": { "name": "Paragraph Group", "alias": "paragraphGroup", "view": "/app_plugins/ParagraphGroupEditor/ParagraphGroup.html", "render": null, "icon": "icon-movie-alt", "config": {} }, "id": 4, "tabelsData": [ { "heading": "tab1", "imagePath": "/media/1010/test.png", "id": "pGropEditor1", "content": "yogesh", "revertOrder": false }, { "heading": "tab2", "imagePath": "/media/1005/why-automate-testing.jpg", "id": "pGropEditor2", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": true }, { "heading": "tab3", "imagePath": null, "id": "pGropEditor3", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": false }, { "heading": "tab4", "imagePath": null, "id": "pGropEditor4", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": true } ], "heading": "Main Paragraph Heading", "tabData": [ { "heading": "tab1", "imagePath": "/media/1010/test.png", "id": "pGropEditor1", "content": "yogesh", "revertOrder": false }, { "heading": "tab2", "imagePath": "/media/1005/why-automate-testing.jpg", "id": "pGropEditor2", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": true }, { "heading": "tab3", "imagePath": null, "id": "pGropEditor3", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": false }, { "heading": "tab4", "imagePath": null, "id": "pGropEditor4", "content": "ndustry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. is simply dummy text of the printing and typesetting i", "revertOrder": true } ], "mainHeading": "Main Paragraph Heading" } ] } ], "id": "59195606-2ce2-d82a-6178-12091aa8898b" } ] } ] }
Tablesdata and mainHeading are the object i need to index
Hope it show better picture of my problem
yogesh
That helps a bit :D
It is however worth mentioning that the way you're saving your data is a bit wrong. In the JSON you have added properties directly to the object representing the control, whereas they should ideally be added to the value object.
So rather than saving tabelsData to
$scope.control.tabelsData
, you should ideally save it to$scope.control.value.tabelsData
.Working with the existing editor / JSON value
My package has a converter system that lets you add new models for your custom editors. Since your data isn't saved as the value object, we can't utilize this system.
Instead we can work directly with the JObject's and JArray's representing the JSON. You can see this little example on how to read the different values:
My example simply writes the values to the console, but you could just as easily add them to the search index ;)
Using the build-in converter system
Creating a custom converter has a few steps, so it may take a bit more work to get implemented. A benefit is that it will make your code and views cleaner.
This also assumes that you have fixed your editor to save your data to
$scope.control.value
instead of just$scope.control
.First you need to implement a model representing your JSON data. I have done this for you with the
ParagraphGroupControlValue
andParagraphGroupTableData
classes. I haven't added all properties though.ParagraphGroupControlValue.cs:
ParagraphGroupTableData.cs:
The first class represents the object stored in the
value
property, while the second class represents each object in thetabelsData
array.The next step is to implement a custom converter - this is done by implementing the
IGridConverter
interface. For your editor, this could look like:To quickly summarize the class, the
ConvertControlValue
method makes sure the value is properly converted - eg. so that you can callcontrol.GetValue<ParagraphGroupControlValue>()
.The
ConvertEditorConfig
method convert the configuration of the editor. Not sure whether you're using the configuration for anything, the converter does nothing at that point.Finally the
GetControlWrapper
method tells my package how it should initialize a wrapper for the class. The wrapper is something that can be used in your views, so it isn't really necessary for indexing.Since the both the model and the converter are now in place, the last step is to tell my package about the converter. The converted should be added during Umbraco startup, which can be done similar to:
So rather than the first code example in the top of this post, the same class could now look like:
I know that working with the grid can be a bit complex, so let me know if you have any further questions ;)
Thank you so much for the detailed explanation :)
i am going to change my Grid Controller code (adding the value part) and then implementing This solution , i will get back to you by today EOD with more question ;)
Thanks
Yogesh
Hi Anders,
Thanks for guiding me to clean my code it looks way better then it was :)
I Have implemented your solution and checked indexes by Luke.net and good news is it working :) , Indexes are being created for Paragraph Group editor grid control . Now i have few queries and to do implementation , for which i want to have suggestions from you before following any approach
I have my website structured in a way where i have two main nodes -1st one is My web site and second one is grid repository in grid repository i used to create Content using my grid editors and in my website node i used to pick those created content by multi node tree picker . i have followed this approach because i wanted to use my content to be reusable and can be picked up in container manner , so now the situation is my implementation indexes grid repository content one by one and will show results of same but what i want is it should index the grid repository node and in the search result page it should redirect user into my website node where the content actually picked (not where it is created which is currently the case) . so any suggestions to achieve this?
how can i create custom fields in External Indexer?
Thanks
Yogesh
The way I understand you, you have some kind of grid picker on your regular pages? Then when these pages are being indexed, you could make lookups in the content cache for the selected grid pages. How to accomplish this depends a lot on your solution and current code, so I might not be able to help much with this one.
If you have a look at my example, I'm adding the combined search value fro the grid into a new field
e.Fields["content"] = combined.ToString();
. So to add a new field, you could jsut do something likee.Fields["myNewField"] = "Hello World";
.1-Yes i have grid picker in my regular pages , these pages are not getting index as per the data they show to the user because they contains only Id of content 2-Ok Thanks , i thought we have to sett this "Content" property on web config or somewhere
I am working on 1 point , i will post here if i will come across a solution
Thanks for help :)
Hi Anders, I have a razor macro with static content (i.e Addressees) but i am not able to search this content with examine any help on this?
thanks
Yogesh
This is a bit out of the scope of my package, and again, it depends a lot on your solution.
If the addresses are static (hardcoded?) you can just add new field(s) for these when the relevant nodes are being indexed.
ok Thanks :)
Yogesh
Hi Anders, i m trying to create my own custom index set Instead of using External index set , but not able to get "content" field in it . my configuration is as bellow
can you help?
Thanks
Yogesh
is working on a reply...