2sxc Blog app search only index page 1 - dotnetnuke

Using 2sxc Blog app, the DNN only indexes whatever is on first page of Blog page.
Second page onwards is not indexed, hence doesn't show in search results.
Can anyone help?

This looks like a good question, and it's probably something we haven't thought about yet. Google doesn't care, but the internal search would probably "respect" the paging and only pick up the first page.
I can think of a few quick-fixes but they would be tricky to explain here. Please open an issue on the blog app on github.

Thanks alot #iJungleBoy for help.
For anyone else encountering this issues here's the solution:
Amend the visual query to create another stream example "SearchIndex"
Once thats done, amend the query within your template which gets all the list items and has paging.
#functions{
// Prepare the data - get all categories through the pipeline
public override void CustomizeData()
{
}
/// <summary>
/// Populate the search - ensure that each entity has an own url/page
/// </summary>
/// <param name="searchInfos"></param>
/// <param name="moduleInfo"></param>
/// <param name="startDate"></param>
public override void CustomizeSearch(Dictionary<string, List<ISearchInfo>> searchInfos, ModuleInfo moduleInfo, DateTime startDate)
{
foreach (var si in searchInfos["SearchIndex"])
{
si.QueryString = "post=" + AsDynamic(si.Entity).UrlKey;
}
}
}

Related

How do I get DNN Search results to display links to articles with a 2sxc app?

I have a 2sxc app that is a list of resources. It has a listing and each item goes to a details view that has a unique URL based on the title. (The URL field is a field in the content type). Something like this domain.com/resources/details/my-amazing-resource.
When a user searches the site for "amazing", the core DNN search results module displays the results of the app, including the "My Amazing Resource" item, but it doesn't actually link to domain.com/resources/details/my-amazing-resource. It just links to domain.com/resources/.
How can I make it so the search results actually point to the unique URL of the item in the app? Is this possible? Would DNNSharp Search Boost be better for this than the core DNN search module?
Its been over a year since I tinkered with it, but what it sounds like you are looking for requires coding. Dnn Search will get what it can from the 2sxc module automatically, but if you need to customize or improve what is being returned, then you need to CustomizeSearch() or CustomizeData() - I am not sure I have seen any decent examples, but I do know the FAQs App does this and must have a working example in it of some kind. Here is a place to stat in the 2sxc Docs,
CustomizeSearch().
I suggest examples in the Blog or News App.
Using Jeremy and Daniel's suggestions, I ultimately updated my _resourcelist.cshtml file to have code that looks like this:
#inherits ToSic.Sxc.Dnn.RazorComponent
#using ToSic.Razor.Blade;
#using ToSic.Eav.Run;
#using ToSic.Sxc.Dnn.Run;
#using ToSic.Sxc.Search;
#functions
{
/// <summary>
/// Populate the search - ensure that each entity has an own url/page
/// </summary>
/// <param name="searchInfos"></param>
/// <param name="moduleInfo"></param>
/// <param name="startDate"></param>
public override void CustomizeSearch(Dictionary<string, List<ISearchItem>> searchInfos, IContainer moduleInfo, DateTime beginDate)
{
foreach (var si in searchInfos["Default"])
{
// tell the search system what url it should use in the result
si.QueryString = "resource/" + AsDynamic(si.Entity).Link;
}
}
}

Web API 2 Action with String array as GET parameter not working as expected

I have an action which looks like this:
/// <summary>
/// Search for customers/projects/tasks by title.
/// </summary>
/// <param name="q">The query to search by.</param>
/// <param name="filters">Extra filters for the search query.</param>
[Route("Search")]
public IHttpActionResult GetSearch(string[] filters, string q = "")
{
...
}
According to the information I could find, I should be able to pass values to the filters parameter by using a url similar to this:
http://application/controller/Search?q=query&filters=first&filters=second
Unfortunately for me, when I try to access the filters parameter inside this action, it is always null.
EDIT:
When viewing the api Help pages in my application, it is only showing q as URI parameter.
The filters parameter seems to be recognized as a body parameter.
Help page screenshot
Does anyone have an idea what could be causing this behavior and how I could fix it? (How I can make the filters parameter be recognized as a URI parameter.)
So apparently the solution here was to change the action to this:
public IHttpActionResult GetSearch([FromUri] string[] filters, string q = "")
{
...
}
The [FromUri] in front of a parameter forces the parameter to be taken from the URI, instead of the body.

2sxc: How to disable searching indexing

We are using a 2sxc module on an DNN Evoq install, there are multiple instances of 2sxc module app on a page which are inserted in lot of pages.
Can we disable search from indexing the content of one particular 2sxc module through its template file using razor code?
Yes you can :)
It's a bit tricky but each razor can modify what / how something is indexed, this is often needed when indexing List/Details-pages. Here's the starting point in the docs: https://github.com/2sic/2sxc/wiki/Razor-SexyContentWebPage.CustomizeSearch
I would try the following (haven't tried it myself, but should work)
#functions
{
/// <summary>
/// Populate the search - ensure that each entity has an own url/page
/// </summary>
/// <param name="searchInfos"></param>
/// <param name="moduleInfo"></param>
/// <param name="startDate"></param>
public override void CustomizeSearch(Dictionary<string, List<ToSic.SexyContent.Search.ISearchInfo>> searchInfos, DotNetNuke.Entities.Modules.ModuleInfo moduleInfo, DateTime startDate)
{
// clear the search-infos
searchInfos["Default"] = new List<ToSic.SexyContent.Search.ISearchInfo>();
}
}

WPF MVVM Opening one View from another

I am just getting started with WPF + MVVM. I think I’ve got the hang of the basics. However, I have a question (hopefully not a stupid one).
I have a View showing a list of customers. I want to edit one of these customers. How do I load the edit view with its separate ViewModel from the List ViewModel.
I’m sure this is a fairly standard scenario, with a fairly straightforward answer, but I’ve spent a chunk of time googling and come up with nothing concrete. Can someone point me in the direction of a straightforward example?
If I’m wrong and it’s not straightforward, what is the best way to do this type of thing?
A common way of doing this (not only in MVVM but it applies well) is to give your list VM access to a so-called service. This service then implements creating and showing the editor (for which it probably uses yet another service).
Example:
/// Always use an interface for the service: it will make it a breeze
/// to test your VM as it decouples it from the actual service implmentation(s)
interface ICustomerEditorService
{
/// Do whatever needed to get the user to edit the Customer passed in,
/// and return the updated one or null if nothing changed.
/// Customer here is likeyly your customer model, or whatever is neede
/// to represent the editable data
Customer EditCustomer( Customer toEdit );
}
class ListViewModel
{
/// service gets passed to constructor, you can use dependency injection
/// like MEF to get this handled easily;
/// when testing, pass a mock here
public ListViewModel( ...., ICustomerEditorService editorService )
{
....
}
private void OnEditButtonClicked()
{
var editedCustomer = editorService.EditCustomer( GetSelectedCustomer() );
//do stuff with editedCustomer
}
}
/// A real implementation
class CustomerEditorService
{
public Customer EditCustomer( Customer toEdit )
{
var vm = new CustomerEditorViewModel( toEdit );
var view = new CustomerEditorView( vm );
if( myWindowManager.ShowDialog( view ) == Cancel )
return null;
return vm.Customer;
}
}

Amazon SimpleDB Query to Find "Post By Friends"

I have been developing an iPhone app which queries a server that relays data I store in Amazon SimpleDB. I have a database table of "Submissions" by various users. I am interfacing with Facebook to retrieve Facebook Friends and wish to make a query to "Submissions" to find posts by friends - like:
SELECT * FROM submissions WHERE userID = '00123' OR userID = '00124' OR ....
(through complete list of friends)
I think this will run into an Amazon query limit with this kind of select statement -
[Maximum number of comparisons per Select expression: 20]
Can you think of a way to elegantly pull this off with SimpleDB?
I'd rather not have to do a bunch of 20 person queries.
Or, do I need to move to a different database package and then do cross-table queries?
Thanks!
There is a way to do it with SimpleDB but it isn't elegant, it's more of a hack since it requires you to artificially duplicate the userid attribute in your submission items.
It's based on the fact that although you can only have 20 comparisons per IN predicate, you can have 20 IN predicates if they each name different attributes. So add additional synthetic attributes to your submission items of the form:
userID='00123' userID_2='00123' userID_3='00123' userID_4='00123' ... userID_20='00123'
They all have the identical value for a given submission. Then you can fetch the submission of up to 400 friends with a single query:
SELECT * FROM submissions
WHERE userID IN('00120','00121',...,'00139') OR
`userID_2` IN('00140','00141',...,'00159') OR
`userID_3` IN('00160','00161',...,'00179') OR
`userID_4` IN('00180','00181',...,'00199') OR
...
`userID_20` IN('00300','00301',...,'00319')
You can populate the 19 extra attributes at the time the submission is created (if you have the attributes to spare) and it doesn't sound like a submission's user would ever change. Also you may want to explicitly name the attributes to be returned (instead of using * ) since you would now have 19 of them that you don't care about in the return data set.
From the data model point of view, this is clearly a hack. But having said that, it gives you exactly what you would want, for users with 400 friends or less: a single query so you can restrict by date or other criteria, sort by most recent, page through results, etc. Unfortunately, a capacity of 400 won't accommodate the friend lists of all facebook users. So you may still need to implement a multi-query solution for large friend lists just the same.
My suggestion is that if SimpleDB suits the needs of your app with the exception of this issue, then consider using the hack. But if you need to do things like this repeatedly then SimpleDB is probably not the best choice.
You're needing either an IN clause or a join to a temp table. Unfortunately AmazonSimpleDB has its limitations. We abandoned it on a promising project for this very reason. We went down the path of multithreading and using the NextToken functionality before we switched gears.
You could execute parallel (multithreaded) queries to SimpleDB to get submissions, each query looking for up to 20 user IDs and then merging the results into one list. Still, it's probably time to consider a switch to MySQL or SQL Server to be able to upload a list of IDs as a temp table and then do a simple join to get the results.
I created the Simple Savant .NET library for SimpleDB, and I happen to have some utility code lying around for splitting and running in parallel multiple select queries, while limiting the IN clause of each select to 20 values. I'll probably roll this code into the next Savant release, but here it is for anyone who finds it useful:
/// <summary>
/// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
/// across multiple invocations that are invoked in parallel.
/// </summary>
/// <typeparam name="T">The item type</typeparam>
/// <typeparam name="P">The select parameter type</typeparam>
/// <param name="savant">The savant instance.</param>
/// <param name="command">The command.</param>
/// <param name="paramValues">The param values.</param>
/// <param name="paramName">Name of the param.</param>
/// <returns></returns>
public static List<T> SelectWithList<T,P>(ISimpleSavantU savant, SelectCommand<T> command, List<P> paramValues, string paramName)
{
var allValues = SelectAttributesWithList(savant, command, paramValues, paramName);
var typedValues = new List<T>();
foreach (var values in allValues)
{
typedValues.Add((T)PropertyValues.CreateItem(typeof (T), values));
}
return typedValues;
}
/// <summary>
/// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
/// across multiple invocations that are invoked in parallel.
/// </summary>
/// <typeparam name="P">The select parameter type</typeparam>
/// <param name="savant">The savant instance.</param>
/// <param name="command">The command.</param>
/// <param name="paramValues">The param values.</param>
/// <param name="paramName">Name of the param.</param>
/// <returns></returns>
public static List<PropertyValues> SelectAttributesWithList<P>(ISimpleSavantU savant, SelectCommand command, List<P> paramValues, string paramName)
{
Arg.CheckNull("savant", savant);
Arg.CheckNull("command", command);
Arg.CheckNull("paramValues", paramValues);
Arg.CheckNullOrEmpty("paramName", paramName);
var allValues = new List<PropertyValues>();
if (paramValues.Count == 0)
{
return allValues;
}
var results = new List<IAsyncResult>();
do
{
var currentParams = paramValues.Skip(results.Count * MaxValueTestsPerSimpleDbQuery).Take(MaxValueTestsPerSimpleDbQuery).ToList();
if (!currentParams.Any())
{
break;
}
var currentCommand = Clone(command);
currentCommand.Reset();
var parameter = currentCommand.GetParameter(paramName);
parameter.Values.Clear();
parameter.Values.AddRange(currentParams.Select(e => (object)e));
var result = savant.BeginSelectAttributes(currentCommand, null, null);
results.Add(result);
} while (true);
foreach (var result in results)
{
var values = ((ISimpleSavant2)savant).EndSelectAttributes(result);
allValues.AddRange(values);
}
return allValues;
}
private static SelectCommand Clone(SelectCommand command)
{
var newParameters = new List<CommandParameter>();
foreach (var parameter in command.Parameters)
{
var newParameter = new CommandParameter(parameter.Name, parameter.PropertyName, null);
newParameter.Values.Clear();
newParameters.Add(newParameter);
}
var newCommand = new SelectCommand(command.Mapping, command.CommandText, newParameters.ToArray())
{
MaxResultPages = command.MaxResultPages
};
return newCommand;
}

Resources