我有这个非常奇怪的问题,我无法理解。也许有人可以指出我做错了什么。
基本上,我只是尝试使用Linq搜索项目到Sitecore。
所以,我的班级看起来像(我也在使用玻璃)
[SitecoreType(TemplateId = "{TEMPLATE_GIUD}")]
public class MyMappedClass : SharedFieldClass
{
[SitecoreField(FieldName = "mylist")]
public virtual IEnumerable<SharedFieldClass> MyMultilistField { get; set; }
[SitecoreField(FieldName = "field1")]
[IndexField("field1")]
public virtual MyKeyValue field1 { get; set; }
}
[SitecoreType]
public class MyKeyValue
{
public virtual Sitecore.Data.ID Id {get;set;}
public virtual string MyValue{get;set;}
}
因此,当我执行以下查询时,它可以正常运行。
var results = context.GetQueryable<SearchResultItem>()
.Where(c => ((string)c["field1"]) == "{GUID}").GetResults();
但是,当我执行以下操作时,它会返回0结果。
List<MyMappedClass> results = context.GetQueryable<MyMappedClass>()
.Where(c => c.field1.MyValue == "{GUID}").ToList();
我看过this link。我已经按照第2个流程描述了here Glass使用Sitecore7 Search(&#34; SharedFieldClass&#34;包含所有基本索引字段)。
这是一个非常明显的场景,我相信很多人已经做过了,我在这里做了些傻事。
提前致谢。
/ ##编辑## /
好的,所以我已经对此进行了更多的挖掘。不确定它是否是ContentSearch / Luncene.NET API中的一个错误,或者我遗漏了一些东西但是看起来发布的内容here可能不正确,如果你有一个复杂的字段类型(你会)你无法使用针对Lucene的映射类进行查询。 (不确定Solr是否也是如此)。如果您正在搜索像string和int这样的简单类型,那么它就像魅力一样。
所以我的调查结果如此:
context.GetQueryable<MyMappedClass>().Where(c => c.field1.MyValue == "{GUID}")
那样查询它被翻译成DEBUG Executing lucene query: field1.value:7e9ed2ae07194d83872f9836715eca8e
的内容,并且在名为&的索引中没有这样的内容#34; field1.value&#34;查询没有返回任何内容。索引的名称实际上只是&#34; field1&#34;。 这是一个错误吗? context.GetQueryable<SearchResultItem>() .Where(c => ((string)c["field1"]) == "{GUID}").GetResults();
这样的查询有效,因为它会被翻译为"DEBUG Executing lucene query: +field1:7e9ed2ae07194d83872f9836715eca8e"
。我也在我的映射类中编写了一个方法,如下所示:
public string this[string key]
{
get
{
return key.ToLowerInvariant();
}
set { }
}
这允许我使用MyMappedClass编写以下查询。
results2 = context.GetQueryable<MyMappedClass>().Where(c => c["filed1"]== "{GUID}").ToList();
此返回预期结果。 但是 MyMappedClass中的字段值未填充(实际上它们除了核心/共享值之外都是null,除了填充文档中填充的coreid,url等)。所以结果列表几乎没用。我可以循环遍历所有这些并手动获取填充的值,因为我有itemid。但想象一下大型结果集的成本。
最后我这样做了:
<fieldType fieldTypeName="droplink" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
这样就返回了填充的&#34; field1&#34;使用&#34; IndexViewer2.0&#34;在lucene查询中使用itemid。 但是这对于MyMappedClass来说也失败了&#34; field1&#34;在文档中是一个System.string ..但它被映射为&#34; MyKeyValue&#34;在MyMappedClass SO 中,它抛出以下异常
Exception: System.InvalidCastException
Message: Invalid cast from 'System.String' to 'MyLib.MyKeyValue'.
所以,最重要的问题是: 如何使用酷的ContentSearch API查询使用他/她的映射类?
答案 0 :(得分:6)
我进一步挖掘让我找到了一个有效的解决方案。在此处发布以防万一有人遇到此问题。
这就是我的&#34; SharedFieldClass&#34;看起来像(这有点不对)
public abstract class SharedFieldClass
{
[SitecoreId]
[IndexField("_id")]
[TypeConverter(typeof(IndexFieldIDValueConverter))]
public virtual ID Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Language)]
[IndexField("_language")]
public virtual string Language { get; set; }
[SitecoreInfo(SitecoreInfoType.Version)]
public virtual int Version
{
get
{
return Uri == null ? 0 : Uri.Version.Number;
}
}
[TypeConverter(typeof(IndexFieldItemUriValueConverter))]
[XmlIgnore]
[IndexField("_uniqueid")]
public virtual ItemUri Uri { get; set; }
}
还有Glass中的一个类来进行映射。如下所示:
var sitecoreService = new SitecoreService("web");
foreach (var r in results)
{
sitecoreService.Map(r);
}
对我来说,由于这一行,这堂课没有映射:
[SitecoreId]
[IndexField("_id")]
[TypeConverter(typeof(IndexFieldIDValueConverter))]
public virtual ID Id { get; set; }
它在sitecoreService.Map(r)上抛出一个NULL异常;线 所以我把它改成了以下:
[SitecoreId]
[IndexField("_group")]
[TypeConverter(typeof(IndexFieldIDValueConverter))]
public virtual ID Id { get; set; }
它有效。我不确定sitecore中ItemId的索引字段何时从&#34; _id&#34;到&#34; _group&#34;或者它是否总是那样。但它是&#34; _group&#34;在SearchResultItem类中。所以我使用它并且映射成功。
总而言之,所有解决方案都是这样的:
映射类:
[SitecoreType(TemplateId = "{TEMPLATE_GIUD}")]
public class MyMappedClass : SharedFieldClass
{
[SitecoreField(FieldName = "mylist")]
public virtual IEnumerable<SharedFieldClass> MyMultilistField { get; set; }
[SitecoreField(FieldName = "field1")]
[IndexField("field1")]
public virtual MyKeyValue field1 { get; set; }
// Will be set with key and value for each field in the index document
public string this[string key]
{
get
{
return key.ToLowerInvariant();
}
set { }
}
}
[SitecoreType]
public class MyKeyValue
{
public virtual Sitecore.Data.ID Id {get;set;}
public virtual string MyValue{get;set;}
}
查询是:
List<MyMappedClass> results = context.GetQueryable<MyMappedClass>()
.Where(c => c["field1"] == "{GUID}").ToList();
那就是它。
/ *编辑* /
OH !!等等,不是吗。 这仍然会失败来投射复杂类型&#34; MyKeyValue&#34;当&#34; field1&#34;在索引文档中使用guid填充。
所以为了避免这种情况,我必须像@Michael Edwards建议的那样编写我的自定义转换器。
我必须略微修改课程以满足我的需要..
public class IndexFieldKeyValueModelConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(sourceType, true);
if (config != null && sourceType == typeof(MyLib.IKeyValue))
{
return true;
}
else
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
else
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var scContext = new SitecoreContext();
Guid x = Guid.Empty;
if(value is string)
{
x = new Guid((string)value);
}
var item = scContext.Database.GetItem(x.ToString());
if (item == null)
return null;
return scContext.CreateType(typeof(MyLib.IKeyValue), item, true, false, new Dictionary<string, object>());
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(value.GetType(), true);
ID id = config.GetId(value);
return id.ToShortID().ToString().ToLowerInvariant();
}
}
不确定它是否是预期的行为..但由于某种原因,在convertfrom方法中,&#34;对象值&#34;参数是短字符串id格式。因此,对于scContext.Database.GetItem工作,我必须将其转换为正确的GUID,然后它开始返回正确的项而不是null。
然后我写了这样的查询:
results = context.GetQueryable<MyMappedGlassClass>().Where(c => c["field1"] == field1value && c["field2"] == field2value && c["_template"] == templateId).Filter(selector => selector["_group"] != currentId).ToList();
让它发挥作用似乎是一项相当大的工作。我想使用 LinqHelper.CreateQuery 方法是一种简单的方法..但正如Pope先生所建议的那样here不使用此方法,因为这是一种内部方法。
不确定这里的余额是什么。 / *结束编辑* /
请参阅我的问题描述部分,了解我为什么必须这样做。
此外,(我偏见意见)是描述的here可能不再有效的技巧(请参阅我的问题描述&编辑部分,了解其背后的原因)。
此外,Glass Mapper教程here中itemid的索引字段是错误的(除非另有证明)。
希望它有助于节省/浪费时间。
答案 1 :(得分:4)
您可以为lucene创建一个自定义字段映射器,它将从索引中的Guid转换为玻璃模型。我把它砍了,但我还没有测试过它:
public class IndexFieldDateTimeValueConverter : TypeConverter
{
public Type RequestedType { get; set; }
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(sourceType, true);
if (config != null)
{
RequestedType = sourceType;
return true;
}
else
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
else
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var scContext = new SitecoreContext();
return scContext.CreateType(RequestedType, scContext.Database.GetItem(value.ToString()),true, false, new Dictionary<string, object>());
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var config = Glass.Mapper.Context.Default.GetTypeConfiguration<SitecoreTypeConfiguration>(value.GetType(), true);
ID id =config.GetId(value);
return id.ToShortID().ToString().ToLowerInvariant();
}
我担心的是ConvertFrom方法没有传递请求的类型,所以我们必须将它作为属性存储在类中,以便将它从CanConvertFrom方法传递给ConvertFrom方法。这使得这个类不是线程安全的。
将其添加到sitecore配置的indexFieldStorageValueFormatter部分。
答案 2 :(得分:1)
此处的问题是SearchResultItem
实际上不是Item
,但确实有GetItem()
方法来获取Sitecore项。您需要做的是:
List<MyMappedClass> results = context.GetQueryable<SearchResultItem>()
.Select(sri => sri.GetItem())
.Where(i => i != null)
.Select(i => i.GlassCast<MyMappedClass>())
.Where(c => c.field1.MyValue == "{GUID}").ToList();
答案 3 :(得分:0)
我还没有特别与Glass合作,但是如果你将你的父类更改为SearchResultItem,它是否会开始工作?如果是这样,那将表明SharedFieldClass父类存在问题。