Codepoint

by Trentia Consulting

JavaScript : Carga dinámica de ficheros e inyección de dependencias.

Hoy en día las aplicaciones web cada vez gozan de más dinamismo al momento de interactuar con el usuario lo que implica más programación de Front-End.

Cada vez más se traslada parte de la lógica de negocio de las aplicaciones web para ejecutarse en la parte cliente de la aplicación. Esto es una carga para el ordenador del usuario cada vez mayor. Y cada vez es más necesario el considerar cargar de forma dinámica archivos JavaScript justo en el momento en que sea necesario para reservar los mínimos recursos que podamos y todo sea fluido y óptimo.

Si además combinamos este comportamiento añadiéndole un orden de carga y permitimos que se realicen ejecuciones concretas justo cuando se haya cargado el código, obtenemos una simple inyección de dependencias.

(function (window) {
    window.ModuleLoader = {
        _ModulesLength: 0,
        _ModulesLoaded: 0,
        AfterLoading: undefined,

        Load: function (data) {
            var _modules = data.Modules;
            this._ModulesLength = _modules.length;
            this.AfterLoading = data.AfterLoading;

            if (typeof data.BeforeLoading === 'function')
                data.BeforeLoading();

            for (var i = 0; i < this._ModulesLength; i++) {
                var script = document.createElement("script");
                script.type = "text/javascript";

                if (script.readyState) {
                    if (_modules[i].callback !== undefined)
                        script.onreadystatechange = this.Execution(_modules[i].callback);
                } else {
                    if (_modules[i].callback !== undefined)
                        script.onload = this.Execution(_modules[i].callback);
                }

                script.src = (_modules[i].host || "") + _modules[i].url;
                document.getElementsByTagName("head")[0].appendChild(script);
            }
        },
        Execution: function (callback) {
            return function () {
                ModuleLoader._ModulesLoaded++;
                callback();

                if (ModuleLoader._ModulesLoaded >= ModuleLoader._ModulesLength)
                    ModuleLoader.AfterLoading();
            };
        }
    };
})(window);

El código anterior añade al objeto Window un objeto en su miembro “ModuleLoader” el cual tendrá una función llamada Load que espera un solo parámetro que será un “object” con esta forma:

 

{
   Modules : [
        	          {
           	    Host: string,
           	    url: string,
           	    callback: function
       		} 
   	   ],
            AfterLoading : function ,
            BeforeLoading : function 
}

Esto definirá los módulos que queremos cargar, que se traducen en ficheros y las acciones que queremos realizar después de la carga de los mismos. Estos Callbacks son la clave para resolver las dependencias necesarias para los módulos que pretendamos cargar.

También incorpora 2 Callbacks adicionales para ejecutar antes y después de la carga completa de todos los módulos especificados.

Todo esto nos permite cargar los ficheros JavaScript que necesitemos, así como las librerías JavaScript que fueran necesarias para el funcionamiento de los mismos.

Ahora expondremos un ejemplo claro cargando dos ejemplos de modulo que dependen uno de otro y a la vez dependen de Jquery :

ModuleLoader.Load({
    Modules : [
            // Cargamos Jquery
            {
                host: "https://ajax.googleapis.com/",
                url: "ajax/libs/jquery/2.2.2/jquery.min.js",
                callback: function () {
                    //Acciones de inicialización cuando Jquery quede cargado.

                }
            },
            {
                host: "",//Dejar en blanco host es como usar el host actual
                url: "OtroFichero.js",//Este modulo tiene una dependencia, en su interior utiliza otro modulo que esta en otro fichero. 
                //Cargamos el modulo y cuando se haya acabado de cargar, cargamos su dependencia
                callback: function () {
                    //  Hemos cargado los modulos en orden y realizaremos aquí las operaciones necesarias.
                }
            }
        ],
  AfterLoading: function () {
            alert("foo");
        },
        BeforeLoading: function () {
            alert("bar");
        }
});


//Fichero OtroFichero.js
(function (namespace) {

    ModuleLoader.Load({
                //En este fichero se encuentra el modulo del que depende OtroFichero.js
                Modules: [ 
                    {
                        host: "",//Cargamos el modulo dependiente. Cuando hayamos cargamos todas las dependencias
                        url: "FicheroModulo2.js",
                        callback: function () {
                            namespace.ModuleInstance = new Module(window.Module2);
                            alert(namespace.ModuleInstance.Result);
                        }
                    }
                ]
            });

    function Module(dependency) {
        this.Result = dependency.Calculation();
    }

})(window);



//Fichero FicheroModulo2.js
(function (parentObject) {
    parentObject.Module2 = { 
        Calculation : function () {
            return 2 + 2;
        }
    };
})(window);

 

Es muy importante comprender el orden en el que se ejecutan las cargas y cómo reacciona el código escrito en los módulos cargados. Por ejemplo no sería correcto intentar utilizar un modulo que no este cargado previamente, eso conduciría a errores en el código.

Así logramos cargar de forma dinámica todos los módulos necesarios para el buen funcionamiento de nuestra aplicación web. Esta implementación no utiliza ningún tipo de librería.

Es importante observar que los módulos que se cargan están implementados de una manera en concreto para que se comuniquen entre ellos correctamente.

Esta manera de implementar módulos permite agrupar los objetos en algo similar a los espacios de nombres conocidos en otros lenguajes.

SharePoint 2013 : Evitando el error ‘The Maximum allowed value is 4096’

Recientemente trabajando en el desarrollo de una aplicación web con base de SharePoint 2013, se me ordenó el desarrollo de un centro de búsqueda y de descarga de contenidos en SharePoint, así que me puse manos a la obra.

Esta parte de la aplicación permite al usuario recibir gran cantidad de contenidos por lo que decidimos atacar al servicio de búsqueda de SharePoint 2013 utilizando la librería CSOM 15, dado que las búsquedas sería mucho más optimas y mejoraría la experiencia de usuario. Así mismo existen gran cantidad de filtros que permite al usuario realizar una criba muy especifica de los contenidos que desea recibir. En este proceso se construye una consulta en Keyword Query Languaje (KQL en adelante) dependiendo de los datos que el usuario haya seleccionado en la sección de filtros.

Así pues el código que genera la consulta tiene un aspecto similar a este :

/// <summary>
/// Searches al documents that maches the specified metadata and retrieve all specified properties
/// </summary>
/// <param name="Filters">Filters to add</param>
/// <param name="PropertiesName">Properties to retrieve</param>
/// <returns></returns>
public IEnumerable<IDictionary<string, object>> SearchDocuments(IDictionary<string, object> Filters, IEnumerable<string> PropertiesName)
{
    using (var context = new ClientContext(_host))
    {

        context.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
        context.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo(_user, _password);

        KeywordQuery query = new KeywordQuery(context);
        if(PropertiesName != null)
        {
            foreach (var PropertyName in PropertiesName)
            {
                query.SelectProperties.Add(PropertyName);
            }
        }

        query.QueryText = "IsDocument=true";

        if (Filters != null && Filters.Any())
            query.QueryText = query.QueryText + " AND ";

        foreach (var filter in Filters)
        {
            if (filter.Value is Array)
            {
                query.QueryText = query.QueryText + "( ";
                foreach (var valueItem in filter.Value as IEnumerable<object>)
                {
                    query.QueryText = query.QueryText + filter.Key + ":*" + valueItem.ToString() + "*";
                    if (((IEnumerable<object>)filter.Value).Last() != valueItem)
                        query.QueryText = query.QueryText + " OR ";
                    else
                        query.QueryText = query.QueryText + " ) ";
                }
            }
            else
            {
                if (filter.Value is bool)
                {
                    query.QueryText = query.QueryText + filter.Key + "=" + filter.Value.ToString().ToLower();
                }
                else
                {
                    query.QueryText = query.QueryText + filter.Key + ":*" + filter.Value.ToString() + "*";
                }
            }
            if (Filters.Last().Key != filter.Key)
                query.QueryText = query.QueryText + " AND ";
        }

        SearchExecutor executor = new SearchExecutor(context);
        ClientResult<ResultTableCollection> results = executor.ExecuteQuery(query);

        context.ExecuteQuery();
        return results.Value[0].ResultRows;
    }
}

 

Habiendo visto el código que genera las consultas, es fácil imaginar que es posible que la consulta KQL alcance grandes tamaños sobretodo si añadimos que muchos de los valores que se usan en la búsqueda son identificadores globales únicos (GUID por sus siglas en inglés).

Si construimos pues, una consulta excesivamente larga y la lanzamos contra el servicio de búsqueda de SharePoint 2013, recibiremos esta excepción : ( y ningún resultado )

System.ArgumentOutOfRangeException : The Maximum allowed value is 4096, Parameter Name ‘QueryText’

   ¿Por que es lanzada esta excepción ? SharePoint 2013 tiene un limite de caracteres en la búsqueda cuando se usa KQL, este por defecto está establecido en 4096 bytes lo que no permite realizar consultas muy largas o muy complejas. Pero esto tiene solución o por lo menos para la mayoría de los casos, pues este límite se puede incrementar. lo que en la mayoría de casos nos permitirá realizar las consultas que antes no nos permitía el límite de caracteres.

¿ Por que digo que se puede solucionar para la mayoría de los casos ? Pues por que el límite “sólo” se puede incrementar hasta 20480 bytes, lo que en la mayoría de casos nos permitirá realizar las consultas que antes no nos permitía el límite de caracteres. Esta tarea es sencilla pero no se puede realizar desde la administración central de SharePoint 2013 si no que tendremos que establecer el nuevo valor desde la “SharePoint 2013 Management Shell”. El valor que tenemos que modificar es una propiedad llamada MaxKeywordQueryTextLength del servicio de búsqueda. Para realizar eso abriremos como administradores una “SharePoint 2013 Management Shell” e introduciremos los siguientes comandos en la consola :

PS C:\Users\trentia> $search = Get-SPServiceApplication | where {$_.displayname -contains "search"}
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength
4096
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength = 20480
PS C:\Users\trentia> $search.Update()
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength
20480
## Quizás no sea necesario pero cerciorémonos que todo quede intacto
 
PS C:\Users\trentia> Restart-Service spsearchhostcontroller
PS C:\Users\trentia> IISRESET

Y con este sencillo proceso el límite queda incrementado hasta 20 kb lo que nos permitirá realizar consultas muchísimo más complejas.

Espero que sea de ayuda !

Projection y Query en Orchard CMS

El módulo Projector nos permite crear instancias de Query y Projection, consultas de datos y páginas que muestran el resultado de estas.

En este artículo crearemos una consulta que devuelva todos nuestros productos y una página que muestre el listado de estos. Suponemos que inicialmente en nuestro proyecto ya existen diferentes Content Types que tienen asociado un ProductPart.
public class ProductPart : ContentPart<ProductPartRecord>
{
  public string SerialNumber
  {
    get { return Retrieve(r => r.SerialNumber); }
    set { Store(r => r.SerialNumber, value); }
  }

  public decimal Price
  {
    get { return Retrieve(r => r.Price); }
    set { Store(r => r.Price, value); }
  }
}

public class ProductPartRecord : ContentPartRecord
{
  public virtual string SerialNumber { get; set; }
  public virtual decimal Price { get; set; }
}
Necesitamos una Query que devuelva todos los elementos cuyo tipo de contenido contenga un ProductPart adjunto. En el framework no tenemos un filtro que permita esta funcionalidad, así que creamos una nueva clase que herede de IFilterProvider:
public class ProductPartFilter : IFilterProvider
{
  public void Describe(DescribeFilterContext describe)
  {
    describe.For("Content","Content","Content")
        .Element("ProductPartFilter", "ProductPartFilter", "ProductPartFilter", ApplyFilter, DisplayFilter);
  }

  private void ApplyFilter(FilterContext context)
  {
    context.Query = context.Query.Join(x => x.ContentPartRecord(typeof(ProductPartRecord)));
  }
}
Ahora ya podemos añadir la nueva Query desde la interfaz web de administración asignándole el título "Productos". La editamos y hacemos clic en "Add a new Filter" para seleccionar el filtro que hemos creado anteriormente.

Es posible que por alguna razón específica de nuestro proyecto necesitemos realizar lo mismo programando directamente, en este caso podemos utilizar el siguiente código:
string name = "Productos";
string type = "ProductPartFilter";

QueryPart query = _queryService.CreateQuery(name);

var form = new Form { ContentTypes = type };
var serializer = new XmlSerializer(form.GetType());
StringWriter sw = new StringWriter();
XmlWriter writer = XmlWriter.Create(sw);
serializer.Serialize(writer, form);
var state = sw.ToString();

query.FilterGroups[0].Filters.Add(new FilterRecord
{
  Category = "Content",
  Description = "ProductPartFilter",
  Position = 0,
  State = state,
  Type = type
});

_contentManager.Publish(query.ContentItem);

[Serializable]
public class Form
{
  public string Description { get; set; }
  public string ContentTypes { get; set; }
}
Por último, para crear la página que muestra el listado de productos, hacemos clic en New Projection. Asignamos el título "Listado Productos", la url "productos" y seleccionamos en el desplegable la Query "Productos".

Una vez más, si necesitamos crear la instancia de Projection programando, tenemos el código equivalente:
var projection = _contentManager.New("ProjectionPage");
projection.As<TitlePart>().Title = "Listado Productos";
projection.As<AutoroutePart>().DisplayAlias = "productos";
projection.As<ProjectionPart>().Record.QueryPartRecord = query.Record;

string ownerName = _siteService.GetSiteSettings().SuperUser;
var owner = _membershipService.GetUser(ownerName);
projection.As<ICommonPart>().Owner = owner;

_contentManager.Create(projection, VersionOptions.Published);
Projection - Orchard Documentation

Actualización de la URL de alertas en SharePoint

Desde hace varios días estoy realizando una migración cautelosa y paso a paso de un portal complejo en SharePoint al cual se estaba accediendo via protocolo HTTP y al cual se deberá acceder a partir de ahora por HTTPS, o sea, con encriptación SSL. Uno de los usuarios ha detectado un curioso efecto y es que los correos de alerta que le están llegando incluye enlaces a HTTP, cuando en realidad este acceso ya no existe. Por lo visto es un problema bastante común y tiene fácil solución, no solo para este sino para otros escenarios.

He dado con un script de Powershell y algunos articulos muy útiles para reparar estas URLs de alerta de SharePoint. El script en cuestión se llama Invoke-AlertFixup, que como veis tiene un nombre suficientemente explícito. Nos permite indicar la URL base de alertas de las que nos queremos deshacer y sustituirla por la nueva URL o la URL en vigor del portal. En mi caso seria:

> Invoke-AlertFixup –site https://miportal –url http://miportal

Cómo podéis ver estoy realizando el cambio de URLs. El proceso es rápido y efectivo. Os dejo los materiales:

Script de PoweShell en Script Center de MS: Update alerts by using Windows PowerShell

Artículos de Technet sobre el tema:

Solución a la hora de filtrar Foreign Keys con identificadores Guid en la grid de la suite de controles Telerik MVC Extensions

Seguramente los que hayáis trabajado con la suite de controles de Telerik para MVC os hayáis dado cuenta que determinados operaciones con los controles Grid no funcionan como deberían. Entre ellas, la más destacada y a la que no hemos encontrado solución hasta ahora, se trata del error que se producía a la hora de filtrar una grid con Foreign Keys donde la Primary Key eran identificadores únicos (Guid o Uniqueidentifier).

Parece ser que Telerik no ha querido darle la solución a esta problemática ya que este error se arrastra desde versiones muy iniciales de esta suite de controles y estoy seguro que no hemos sido los únicos que nos hemos encontrado con este problema.

Pues bien, después de darle vueltas al asunto,en Trentia Consulting no hemos tenido más remedio que darle solución y para ello hemos descargado el último código fuente y lo hemos modificado para dar solución al problema.

Aunque al final de esta entrada acompañamos el código fuente modificado para que los podáis compilar y actualizar a vuestra solución, hemos querido resaltar algunos pasos que creemos que son los más importantes.

Vamos a ello, principalmente cuando filtrábamos por una FK, la llamada AJAX que se encargaba de dibujar el GRID nos daba el siguiente error en la pila del Stack Trace

Invalid cast from "System.String" to "System.Guid" 
System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) +10578274 
	
System.String.System.IConvertible.ToType(Type type, IFormatProvider provider) +8 
	
System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) +10625268 

Con este error, deducimos que la la conversión de String a Guid no estaba implementada. Así que después de investigar, lo primero que tuvimos que tocar fue la clase FilterLexer ubicada dentro del namespace Telerik.Web.Mvc.Infrastructure.Implementation.

Revisando el método Tokenize vimos que el parse de parámetros Guid no estaba implementado, así que tuvimos que añadir lo siguiente:

 public IList Tokenize()
        {
            List tokens = new List();

            while (currentCharacterIndex < input.Length)
            {
                string result;

                if (TryParseGuid(out result))
                {
                    tokens.Add(UniqueIdentifier(result));
                }
                else if (TryParseIdentifier(out result))
                {
                    tokens.Add(Identifier(result));
                }
	...
}


Aquí hemos implementado el método TryParseGuid, que se encargará de detectar y formatear el string del identificador del filtro a Guid.

El código es el siguiente

private bool TryParseGuid(out string guid)
{
            SkipSeparators();

            int longitud=36;

            StringBuilder result = new StringBuilder();
            for (int i = 0; i < longitud; i++)
            {
                if((i + currentCharacterIndex)<input.Length)
                    result.Append(input[i + currentCharacterIndex]);
            }

          
            Guid outGuid = Guid.Empty;

            if (Guid.TryParse(result.ToString(), out outGuid))
            {
                guid = result.ToString();
                currentCharacterIndex+=longitud;
                return true;
            }
            else
            {
                guid = null;
                return false;
            }
}

Una vez implementada la detección del Guid, ya sólo nos queda ir al resto de clases que integran las funcionalidades de filtrado de la Grid de Telerik como la clase FilterParser.cs, GuidNode.cs y FilterTokenType.cs del mismo namespace.

Aquí os dejo el enlace de descarga del artículo que hemos redactado en CodeProject con el fin de compartir este problema con el mayor número de personas.

Descarga de la solución

Espero que os sea de ayuda.

Proyecto Orchard: CMS en ASP.NET MVC

Orchard es un gestor de contenidos web o CMS que parece contar con la bendición de Microsoft. Recientemente hemos estado desarrollando con este CMS, como alternativa a Umbraco, otro conocido CMS de .NET.


Orchard nos ha gustado especialmente por sus características funcionales y técnicas.

Orchard cubre bastante bien aspectos que otros CMS quizás no tengan demasiado presente, como el soporte multiidioma y multicultural, que acostumbra a ser un problema. También dispone de una buena extensibilidad a muy diversos niveles, que nos permite trabajar con vistas de MVC para definir hasta el más mínimo aspecto de la web. Tenemos control absoluto del HTML resultante y eso es muy positivo. También contempla bien aspectos de SEO, tan necesarios hoy en día. Y finalmente, ofrece un buen rendimiento, aspecto que también puede resultar complejo en CMS basados en Microsoft .NET.

Nuestro último proyecto en Orchard es el desarrollo de una web con CMS, haciendo uso deFoundation Zurb como framework de diseño responsable. Estamos realmente satisfechos con el resultado y con Orchard. Os recomiendo que lo tengáis presente en vuestro portfolio de soluciones CMS.

Web essentials, indispensable para nuestro Visual Studio

Si trabajáis en entornos web y diseñáis con el entorno de Visual Studio, os habréis dado cuenta que el entorno no nos ayuda demasiado a la hora de realizar nuestras tareas de diseño. Y si nos empeñamos hacer compatible nuestro desarrollo con múltiples plataformas se nos complica mucho más el trabajo ya que hemos de estar comprobando por cada propiedad CSS la compatibilidad con los diferentes motores de navegación.

Para estas problemáticas, tenemos un extensión muy interesante para Visual Studio 2012 (también las hay disponibles para VS 2010) y que casi se va a convertir en imprescindible cuando vayamos a diseñar con VS. Se trata de Web Essentials para Visual Studio.

Esta extensión nos proporciona las siguientes funcionalidades:

TypeScript
Para quien no lo conozcáis, TypeScript es un superset tipado de JavaScript (typed superset) y es muy útil para implementar aplicaciones escalables en desarrollos JavaScript. Pues bien, con la extensión de VS en el momento de guardar el fichero X.ts, web essentials compila el fichero y genera el código JavaScript correspondiente.

Edición CSS

Con la aparición de CSS 3 tenemos multitud de propiedades específicas por cada navegador. Con la extensión instalada nos añade los tags –moz, –webkit, –s y –o automáticamente. Con el siguiente ejemplo veremos el resultado equivalente de border-radius para cada navegador. Cuando escribimos border-radius se generán automáticamente los equivalentes para cada navegador y los valores de las propiedades se modificarán automáticamente al resto cuando modifiquemos una de ellas.

image_thumb_1

Otra ayuda visual que disponemos es la de poder ver con un sólo golpe de vista la compatibilidad de todas las propiedades o la posibilidad de poder ver el color correspondiente de una color hexadecimal por ejemplo

image_thumb_2

image_thumb_3

Intellisense
Tenemos Intellisense para añadir regiones dentro del CSS

image_thumb_4

Intellisense para añadir fuentes personalizadas

image_thumb_5

Estas son sólo algunas de las funcionalidades que aporta Web essentials, os recomiendo seguir el siguiente enlace para que descubráis toda las posibilidades que os aporta esta extensión.

http://visualstudiogallery.msdn.microsoft.com/07d54d12-7133-4e15-becb-6f451ea3bea6

Wizard para generar expresiones de Set Analysis en QlikView

Este asistente ayuda a generar rápidamente expresiones de “Set Analysis” para Qlikview.

Se puede aplicar a conjuntos estándares, selecciones anteriores y siguientes, marcadores usando las funciones de agregación básicas, sobre cadenas de caracteres, contadores y agregaciones estadísticas.

También dispone de una lista de ejemplos de expresiones más utilizadas.

clip_image001_77e476f3-7f11-40d1-a49d-972be60316db

Autenticación Windows en MVC 4 redirige a página de login

En mi caso ha sido a Login.aspx, pero podría haber sido /Account/Login. Por alguna razón las llamadas a controladores que requieren autorización me llevaban a la página de inicio de sesión, cuando en realidad en web.config tengo configurada la autenticación Windows.

Después de investigar algo he visto un artículo en Stackoverflow que me ha ayudado a resolver el problema. Se trata de deshabilitar SimpleMembership de la aplicación. Esto se consigue añadiendo esta entrada en la clave <appSettings> de web.config.


Por cierto, un artículo sobre cómo configurar SimpleMembership: http://www.mono-software.com/blog/post/Mono/226/Adding-ASP-NET-SimpleMembership-to-an-existing-MVC-4-application/

SharePoint 2013: Cambios en los servicios de búsqueda mediante programación

Después de llevar varios meses trabajando en diferentes proyectos sobre la nueva plataforma SharePoint 2013, creemos que va siendo hora de compartir con vosotros nuestras experiencias.

En el siguiente artículo, vamos a comentar los cambios en los servicios de búsqueda y como afectan a la programación. Como sabréis los servicios de búsqueda FAST se han incorporado al servicio de búsqueda estándar. Esto provoca que los antiguos modelos de programación, como el FullTextSqlQuery, estén marcados como deprecated en la actual versión y por tanto no los podremos utilizar.

En primer lugar, hemos de incoporar a nuestro proyecto las siguientes referencias Microsoft.Office.Server y Microsoft.Office.Server.Search

Con la nueva versión aparece en escena SearchExecutor, que será nuestro objeto principal para la búsqueda.

Utilizaremos el siguiente código para devolver resultados de búsqueda a partir de una o varias condiciones

public static DataTable ExecuteKeyWordSearch(string queryText) 
  { 

  ResultTableCollection rtc = null; 

   DataTable retResults = new DataTable();


   Dictionary queries = new Dictionary(); 

 using (SPSite site = new SPSite(SPContext.Current.Site.ID)) 
   { 

   using (KeywordQuery query = new KeywordQuery(site)) 

   { 

   query.QueryText = queryText; 

   query.KeywordInclusion = KeywordInclusion.AllKeywords; 

   query.RowLimit = 500; 

   query.SelectProperties.Add("Path"); 

    SearchExecutor se = new SearchExecutor(); 

   rtc = se.ExecuteQuery(query);


   if (rtc.Count > 0) 

   { 

   var results = rtc.Filter("TableType", KnownTableTypes.RelevantResults); 

Etiquetas de Technorati:
//results = rtc.Filter("TRENTIAContentType", "CV"); if (results != null && results.Count() >= 1) retResults.Load(results.First(), LoadOption.OverwriteChanges); } } } return retResults; }

 

Con FullTextSqlQuery hacíamos servir una sintaxis similar a SQL para realizar búsquedas e incluir campos a los resultados de la búsqueda. Actualmente esto ya no funciona así. La sintaxis que tendremos que hacer servir a partir de ahora será la siguiente:

En el caso que deseamos filtrar por algún tipo de contenido, le pasaremos por parámetro (queryText) un string con la siguiente sintaxis: ContentType:CV

En fin, espero os sirva de utilidad a la hora de introduciros en el nuevo modelo de búsquedas de SharePoint 2013.