Can't get any documents with NEST from elasticsearch - c#

I use Searchblox to index and search my files, which itself calls ES 2.x to do the job. Searchblox uses a "mapping.json" file to initialize a mapping upon the creation of an index. Here's the link to that file. As "#Russ Cam" suggested here, I created my own class content with the following code (just like he did with the "questions" index and "Question" class):
public class Content
{
public string type { get; set; }
public Fields fields { get; set; }
}
public class Fields
{
public Content1 content { get; set; }
public Autocomplete autocomplete { get; set; }
}
public class Content1
{
public string type { get; set; }
public string store { get; set; }
public string index { get; set; }
public string analyzer { get; set; }
public string include_in_all { get; set; }
public string boost { get; set; }
} //got this with paste special->json class
These fields from the content class (type,store etc.) come from the mapping.json file attached above. Now, when I (just like you showed me) execute the following code:
var searchResponse = highLevelclient.Search<Content>(s => s.Query(q => q
.Match(m => m.Field(f => f.fields.content)
.Query("service")
All I get as a response on the searchResponse variable is:
Valid NEST response built from a successful low level call on POST: /idx014/content/_search
Audit trail of this API call:
-HealthyResponse: Node: http://localhost:9200/ Took: 00:00:00.7180404
Request:
{"query":{"match":{"fields.content":{"query":"service"}}}}
Response:
{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
And no documents in searchResponse.Documents. Contradictorily, when I search for the "service" query on Searchblox or make an API call to localhost:9200 with the Sense extension of Google Chrome, I get 2 documents. (the documents that I was looking for)
In brief, all I want is to be able to :
get all the documents (no criteria)
get all the documents within a time range and based upon keywords.. such as "service"
What am I doing wrong? I can provide with more information if needed.. Thank you all for your detailed answers.

Your C# POCO is not correct in regards to your mapping; your document type is "sdoc" and each of the properties under the "properties" property is a field on that document type; These fields map to properties on your C# POCO.
As an example to get you started
public class Document
{
[String(Name = "uid")]
public string UId { get; set; }
public string Content { get; set; }
}
NEST by default will camel case POCO property names, so "content" will be case correctly according to your mapping, however, we use attribute mapping for the "uid" field in order to name it to match the mapping (we can go further here and set additional attribute property values to fully match the mapping; see the automapping documentation).
Now, to search with the document, let's create the connection settings and a client to use
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool)
.InferMappingFor<Document>(t => t
// change the index name to the name of your index :)
.IndexName("index-name")
.TypeName("sdoc")
.IdProperty(p => p.UId)
);
var client = new ElasticClient(connectionSettings);
// do something with the response
var searchResponse = client.Search<Document>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.Content)
.Query("service")
)
)
);
}
We set up the client with some inference rules for the Document type which will be used when interacting with Elasticsearch. The above query emits the following query json
{
"query": {
"match": {
"content": {
"query": "service"
}
}
}
}
As an aside, I noticed that the mapping contained a multi_field type; multi_field types were removed in Elasticsearch 1.0 (multi fields are still there, just the actual type is not), so be sure that you're actually running Elasticsearch 2.x on Searchblox, as NEST 2.x is only supported against Elasticsearch 2.x.

Related

Sending The Content of a Document Type Via UmbracoApiController

How Can i OutPut the published Content of a Certain Document Type via Web Api?
Example:
I have a Document Type Called
Comment
its has three Properties "Name, Date, Text"
I Want To output the Values of those Properties to a UmbracoApiController So that I can Use it in other WebSites
any thoughts ? Thanks in Advance
public class publishedContentapiController : UmbracoApiController
{
//What Logic To Put Here In Order to get the Content OF published
// Document Types With the Alias "comment"
}
The below code outputs all documents of type "comment" through the webapi
public class publishedContentapiController : UmbracoApiController
{
public IHttpActionResult GetComments()
{
// Create an UmbracoHelper for retrieving published content
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
// Get all comments from the Umbraco tree (this is not a optimized way of doing this, since it queries the complete Umbraco tree)
var comments = umbracoHelper.TypedContentAtRoot().DescendantsOrSelf("comment");
// Map the found nodes from IPublishedContent to a strongly typed object of type Comment (defined below)
var mappedComments = comments.Select(x => new Comment{
Name = x.Name, // Map name of the document
Date = x.CreatedTime, // Map createdtime
Text = x.GetPropertyValue<string>("text") // Map custom property "text"
});
return Ok(mappedComments);
}
private class Comment
{
public string Name { get; set; }
public DateTime Date { get; set; }
public string Text { get; set; }
}
}
Diclaimer: Code is untested and obviously needs refactoring

Serializing published content in umbraco

I am currently writing an api for a custom application my company is writing. Part of this involves getting published content out in JSON format. When I try serializing ipublishedcontent directly it obviously attempts to serialize all of the umbraco data and relations that I simply don't need (in fact it fails with a stack overflow). Is there a way to get just the custom properties from an item of content without specifying the fields?
I am using webapi and passing it objects to serialize itself and I'm using a dynamic to manually specify the fields. The Product type which I'm initially selecting into is from modelsbuilder. My code currently looks a little like this:
public object Get(string keywords = "")
{
// Get Data from Umbraco
var allProducts = Umbraco.TypedContent(1100).Children.Select(x => new Product(x));
if (keywords != "")
{
allProducts = allProducts.Where(x => x.Name.Contains(keywords));
}
return allProducts.Select(x => new
{
id = x.Id,
name = x.Name,
price = x.Price
});
}
It seems to me that there should be a simple way to do this without having to create a dynamic with just the fields I want but I can't work it out. I just don't want to have to change my code every time the document type in umbraco changes!
You can use Ditto to map your data into an object.
Create an object with properties that match the alias's of your fields (case insensitive)
public class Product{
public int id {get;set;}
public string name {get;set;}
public string price {get;set;}
}
Then map a single or collection of IPublishedContent objects using .As
return allProducts.As<Product>();
You can use the UmbracoProperty attribute to specify the alias too if it is different than you need for your json or use the JsonProperty attribute to change the name on serialize.
Take a look at the code in the MemberListView - it does a similar thing while retrieving Members without knowing in advance what the properties on the MemberType will be:
https://github.com/robertjf/umbMemberListView/blob/master/MemberListView/Models/MemberListItem.cs
For example:
[DataContract(Name = "content", Namespace = "")]
public class MemberListItem
{
// The following properties are "known" - common to all IPublishedContent
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "contentType")]
public IContentType ContentType { get; set; }
// This one contains a list of all other custom properties.
private Dictionary<string, string> properties;
[DataMember(Name = "properties")]
public IDictionary<string, string> Properties
{
get
{
if (properties == null)
properties = new Dictionary<string, string>();
return properties;
}
}
}
MemberListView converts to this from a list of SearchResult using AutoMapper, but you could just as easily map it from IPublishedContent.

How to ignore a property based on a runtime condition?

I have a simple pair of classes which for I've set up a mapping at initialization time.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>();
Now at a certain point I need to map an Order to an OrderDTO. BUT depending on some circumstances, I might need to ignore Foo during mapping. Let's also assume that I cannot "store" the condition in the source or destination object.
I know how I can configure the ignored properties at initialization time, but I have no idea how I could achieve such a dynamic runtime behavior.
Any help would be appreciated.
UPDATE
My use case for this behaviour is something like this. I have an ASP.NET MVC web grid view which displays a list of OrderDTOs. The users can edit the cell values individually. The grid view sends the edited data back to the server like a collection of OrderDTOs, BUT only the edited field values are set, the others are left as default. It also sends data about which fields are edited for each primary key. Now from this special scenario I need to map these "half-empty" objects to Orders, but of course, skip those properties which were not edited for each object.
The other way would be to do the manual mapping, or use Reflection somehow, but I was just thinking about if I could use AutoMapper in some way.
I've digged into the AutoMapper source code and samples, and found that there is a way to pass runtime parameters at mapping time.
A quick example setup and usage looks like this.
public class Order {
public int ID { get; set; }
public string Foo { get; set; }
}
public class OrderDTO {
public int ID { get; set; }
public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>()
.ForMember(e => e.Foo, o => o.Condition((ResolutionContext c) => !c.Options.Items.ContainsKey("IWantToSkipFoo")));
...
var target = new Order();
target.ID = 2;
target.Foo = "This should not change";
var source = new OrderDTO();
source.ID = 10;
source.Foo = "This won't be mapped";
Mapper.Map(source, target, opts => { opts.Items["IWantToSkipFoo"] = true; });
Assert.AreEqual(target.ID, 10);
Assert.AreEqual(target.Foo, "This should not change");
In fact this looks quite "technical", but I still think there are quite many use cases when this is really helpful. If this logic is generalized according to application needs, and wrapped into some extension methods for example, then it could be much cleaner.
Expanding on BlackjacketMack's comment for others:
In your MappingProfile, add a ForAllMaps(...) call to your constructor.
using AutoMapper;
using System.Collections.Generic;
using System.Linq;
public class MappingProfile : Profile
{
public MappingProfile()
{
ForAllMaps((typeMap, mappingExpression) =>
{
mappingExpression.ForAllMembers(memberOptions =>
{
memberOptions.Condition((o1, o2, o3, o4, resolutionContext) =>
{
var name = memberOptions.DestinationMember.Name;
if (resolutionContext.Items.TryGetValue(MemberExclusionKey, out object exclusions))
{
if (((IEnumerable<string>)exclusions).Contains(name))
{
return false;
}
}
return true;
});
});
});
}
public static string MemberExclusionKey { get; } = "exclude";
}
Then, for ease of use, add the following class to create an extension method for yourself.
public static class IMappingOperationOptionsExtensions
{
public static void ExcludeMembers(this AutoMapper.IMappingOperationOptions options, params string[] members)
{
options.Items[MappingProfile.MemberExclusionKey] = members;
}
}
Finally, tie it all together: var target = mapper.Map<Order>(source, opts => opts.ExcludeMembers("Foo"));

Get only specified fields from MongoDB C#

I have the following POCO class in my app -
public class Course
{
public String Title { get; set; }
public String Description { get; set; }
}
But the Course collection in mongodb has some other fields also including those. I am trying to get data as follows-
var server = MongoServer.Create(connectionString);
var db = _server.GetDatabase("dbName");
db.GetCollection("users");
var cursor = Photos.FindAs<DocType>(Query.EQ("age", 33));
cursor.SetFields(Fields.Include("a", "b"));
var items = cursor.ToList();
I have got that code from this post in stackoverflow.
But it throws an exception-
"Element '_id' does not match any field or property of class"
I don't want '_id' field in my POCO. Any help?
_id is included in Fields by default.
You can exclude it by using something like:
cursor.SetFields(Fields.Exclude("_id"))

How to create an index returning the input document type?

I have a Raven database which contains a document collection. I would like to retrieve a subset of the documents in that collection. Only documents fulfilling certain criteria would be retrieved. However, for each document retrieved, the entire document must be retrieved.
Consider the following document type:
public class MyDocument {
public string Id { get; set; }
public string Name { get; set; }
public int Foo { get; set; }
public string Bar { get; set; }
}
Let's say I would like to retrieve all documents where the Foo property is greater than a given value (unknown at compile/index creation time). Using dynamic indexes, this could be done like:
IList<MyDocument> FindMyDocuments(int minFooValue) {
using(IDocumentSession session = _store.OpenSession()) {
return session.Query<MyDocument>().Where(d => d.Foo > minFooValue).ToList();
}
}
However, as I understand it, there are benefits to using predefined indexes instead of dynamic indexes. So I would like to define an index for this operation up front. How would an implementation of AbstractIndexCreationTask< MyDocument, MyDocument > look like?
The following doesn't seem to work as Raven wants the Map to select a new anonymous type:
class MyDocumentIndex: AbstractIndexCreationTask<MyDocument, MyDocument> {
public MyDocumentIndex() {
Map = docs => from doc from docs
select doc;
}
}
And shouldn't there be a Reduce part as well?
As you probably noticed, I'm rather new to this Map/Reduce concept :-).
David,
You do it like this:
public class MyDocumentIndex: AbstractIndexCreationTask<MyDocument> {
public MyDocumentIndex() {
Map = docs => from doc from docs
select new { doc.Foo };
}
}
And then you query it with:
session.Query<MyDocument, MyDocumentIndex().Query(x=>x.Foo > minValue).ToArray();

Categories

Resources