GithubHelp home page GithubHelp logo

plainelastic.net's Introduction

PlainElastic.Net

The really plain Elastic Search .Net client.

Plain Idea

Usually connectivity clients built using BLACK BOX principle: there is a client interface and some unknown magic behind it.
(call of the client method internally generate some commands and queries to external system, get responses, somehow process them and then retrieve result to user)
As the result user hardly can debug connectivity issues or extend client functional with missed features.

The main Idea of PlainElastic.Net is to be a GLASS BOX. e.g. provide a full control over connectivity process to user.

Installation

NuGet support

You can find PlainElastic.Net in NuGet Gallery or just install it using VS NuGet Packages Manager.
Or just type Install-Package PlainElastic.Net in Package Manager Console.

Building from Source

The easiest way to build PlainElastic.Net from source is to clone the git repository on GitHub and build the PlainElastic.Net solution.

git clone git://github.com/Yegoroff/PlainElastic.Net.git

The solution file PlainElastic.Net.sln is located in the root of the repo.

How Its works

  1. The only thing you need to connect to ES is a HTTP connection.
  var connection  = new ElasticConnection();
  1. Than you can declare sting with ES command
  string command = "http://localhost:9200/twitter/user/test";
  1. And JSON string with data
  string jsonData = "{ \"name\": \"Some Name\" }";
  1. And pass them using connection to ES.
  string response = connection.Put(command, jsonData);
  1. Get JSON string response and analyze it.
  if(response.Contains("\"ok\":true")) {
   ... // do something useful
  }

So, how PlainElastic can help you here?

  // 1. It provides ES HTTP connection
  var connection  = new ElasticConnection("localhost", 9200);
  
  // 2. And sophisticated ES command builders:
  string command = Commands.Index(index: "twitter", type: "user", id: test)
  
  // 3. And gives you the ability to serialize your objects to JSON:  
  var serializer = new JsonNetSerializer();
  var tweet = new Tweet { Name = "Some Name" };
  string jsonData = serializer.ToJson(tweet);
  
  // 4. Then you can use appropriate HTTP verb to execute ES command:
  string response = connection.Put(command, jsonData);
  
  // 5. And then you can deserialize operation response to typed object to easily analyze it:
  IndexResult indexResult = serializer.ToIndexResult(result);
  if(indexResult.ok) {
     ... // do something useful.
  }
  
  // 6. And even more: Typed mapping and condition-less query builders.

Concepts

No addition abstraction upon native Elastic Search query and mapping syntax.

This eliminates requirements to read both ES and driver's manuals, and also it allows you not to guess how driver will generate actual ES query when you construct it using driver's Query DSL.
So if you want to apply some ES query - all you need is to read ES Query DSL documentation

All you need is strings.

Let's take some ES query sample in a format that you will see in ES documentation:

$ curl -XGET http://localhost:9200/twitter/tweet/_search -d '{
     "query" : {
         "term" : { "User": "somebody" }
     }
}'

In PlainElastic.Net this could be done using:

var connection  = new ElasticConnection("localhost", 9200);
string command = new SearchCommand("twitter", "tweet"); // This will generate: twitter/tweet/_search
string query = new QueryBuilder<Tweet>()        // This will generate: 
          .Query(q => q                         // { "query": { "term": { "User": "somebody" } } }
            .Term(t => t
              .Field(tweet=> tweet.User).Value("somebody")
            )
          ).Build();
string result = connection.Get( command, query);

// Than we can convert search results to typed results
var serializer = new JsonNetSerializer();
var foundTweets = serializer.ToSearchResults<Tweet>(result);
foreach (Tweet tweet in  foundTweets.Documents)
{
  ...
}

As you can see all parameters passed to and returned from Get HTTP verb execution are just strings.
This gives us complete control over generated commands and queries. You can copy/paste and debug them in any ES tool that allows to execute JSON queries (e.g. CURL or ElasticHead ).

Command building

PlainElastic.Net commands represent URL part of ElasticSearch requests.
All commands have corresponding links to ES documentation in their XML comments, so you can use these links to access detailed command description.

Most of the commands have Index ,Type and Id constructor parameters, (these parameters forms address part) all other options could be set using fluent builder interface.

string indexCommand = new IndexCommand(index: "twitter", type: "tweet", id: "10")
               .Routing("route_value")
               .Refresh();

There is also a Commands class that represents a command registry and allows you to easily build commands, without necessity to remember command class name.

string searchCommand = Commands.Index(index: "twitter", type: "tweet", id: "10")
               .Routing("route_value")
               .Refresh();

Indexing

ES documentation: http://www.elasticsearch.org/guide/reference/api/index_.html

The easiest way to index document is to serialize your document object to JSON and pass it to PUT index command:

var connection  = new ElasticConnection("localhost", 9200);
var serializer = new JsonNetSerializer();

var tweet = new Tweet { User = "testUser" };
string tweetJson = serializer.ToJson(tweet);

string result = connection.Put(new IndexCommand("twitter", "tweet", id: "10"), tweetJson);

// Convert result to typed index result object. 
var indexResult = serializer.ToIndexResult(result);

Note: You can specify additional indexing parameters such as Parent or Refresh in IndexCommand builder.

string indexCommand = new IndexCommand("twitter", "tweet", id: "10").Parent("5").Refresh();

Bulk Operations

ES documentation: http://www.elasticsearch.org/guide/reference/api/bulk.html

There are two options to build Bulk operations JSONs. First is to build all Bulk operations at once:

IEnumerable<Tweet> tweets = new List<Tweet>();

string bulkCommand = new BulkCommand(index: "twitter", type: "tweet");

string bulkJson = 
    new BulkBuilder(serializer)
       .BuildCollection(tweets,
            (builder, tweet) => builder.Index(data: tweet,  id: tweet.Id)
                       // You can apply any custom logic here
                       // to generate Indexes, Creates or Deletes.
);

string result = connection.Post(bulkCommand, bulkJson);

//Parse bulk result;
BulkResult bulkResult = serializer.ToBulkResult(result);
...

Second allows you to build Bulk operations in batches of desired size.
This will prevent from constructing huge in-memory strings, and allows to process input collection on-the-fly, without enumerating them to the end.

IEnumerable<Tweet> tweets = new List<Tweet>();

string bulkCommand = new BulkCommand(index: "twitter", type: "tweet");

IEnumerable<string> bulkJsons = 
    new BulkBuilder(serializer)
        .PipelineCollection(tweets,
            (builder, tweet) => builder.Index(data: tweet,  id: myObject.Id))
        .JoinInBatches(batchSize: 10); // returns deferred IEnumerable of JSONs  
                            // with at most 10 bulk operations in each element,
                            // this will allow to process input elements on-the-fly
                            // and not to generate all bulk JSON at once

foreach(string bulk in bulkJsons )
{
  // Send bulk batch.
  string result = connection.Post(bulkCommand, bulk);

  // Parse bulk batch result.
  BulkResult bulkResult = serializer.ToBulkResult(result);
  ...
}

Note: You can build not only Index Bulk operations but also Create and Delete.

IEnumerable<string> bulkJsons = 
  new BulkBuilder(serializer)
     .PipelineCollection(tweets,
            (builder, tweet) => {
              switch (tweet.State) {
                case State.Added: 
                  builder.Create(data: tweet,  id: myObject.Id))
                case State.Updated: 
                  builder.Index(data: tweet,  id: myObject.Id))
                case State.Deleted:
                  builder.Delete(id: myObject.Id))
              }
            });

Queries

ES documentation: http://www.elasticsearch.org/guide/reference/query-dsl/

The main idea of QueryBuilder is to repeat JSON syntaxes of ES queries.
Besides this it provides intellisense with fluent builder interface
and property references:

for single property .Field(tweet => tweet.Name)
for collection type property .FieldOfCollection(collection: user => user.Tweets, field: tweet => tweet.Name)

So let’s see how it works.

We have http://localhost:9200/twitter index with type user. Below we add sample "user" document to it:

PUT http://localhost:9200/twitter/user/1
{
    "Id": 1,
    "Active": true,
    "Name": "John Smith",
    "Alias": "Johnnie"
}

Now let's create some synthetic JSON query to get this document:

POST http://localhost:9200/twitter/user/_search
{
    "query": {
        "bool": {
            "must": [
                {
                   "query_string": {
                      "fields": ["Name","Alias"], "query" : "John" 
                    }
                },
                {
                   "prefix" : {
                      "Alias": { "prefix": "john" } 
                   }
                }
            ]
        }
    },
    "filter": {
        "term": { "Active": "true" }
    }
}

Assuming that we have defined class User:

class User
{
    public int Id { get; set; }
    public bool Active { get; set; }
    public string Name { get; set; }
    public string Alias { get; set; }
}

This query could be constructed using:

string query = new QueryBuilder<User>()
    .Query(q => q
        .Bool(b => b
           .Must(m => m
               .QueryString(qs => qs
                   .Fields(user => user.Name, user => user.Alias).Query("John")
               )
               .Prefix(p => p
                    .Field(user => user.Alias).Prefix("john")
               )
           )
        )
    )
    .Filter(f => f
        .Term(t => t 
            .Field(user=> user.Active).Value("true")
        )
    )
    .BuildBeautified();

And then to execute this query we can use the following code:

var connection = new ElasticConnection("localhost", 9200);
var serializer = new JsonNetSerializer();

string result = connection.Post(Commands.Search("twitter", "user"), query);
User foundUser = serializer.ToSearchResult<User>(result).Documents.First();

See Query Builder Gist for complete sample.

Condition-less Queries:

Its usual case when you have a bunch of UI filters to define full-text query, price range filter, category filter etc.
None of these filters are mandatory, so when you construct final query you should use only defined filters. This brings ugly conditional logic to your query-building code.

So how PlainElastic.Net addresses this?

The idea behind is really simple:
If provided condition value is null or empty - the corresponding query or filter will not be generated.

Expression

string query = new QueryBuilder<User>()
    .Query(q => q
        .QueryString(qs => qs
           .Fields(user => user.Name, user => user.Alias).Query("")
        )
    )
    .Filter(f => f
        .Term(t => t 
            .Field(user=> user.Active).Value(null)
        )
    )
    .Build();

will generate "{}" string that will return all documents from the index.

The real life usage sample:
Let's say we have criterion object that represents UI filters:

class Criterion
{
    public string FullText { get; set; }
    public double? MinPrice { get; set; }
    public double? MaxPrice { get; set; }
    public bool? Active { get; set; }
}

So our query builder could look like this:

public string BuildQuery(Criterion criterion)
{
    string query = new QueryBuilder<Item>()
        .Query(q => q
            .QueryString(qs => qs
                .Fields(item => item.Name, item => item.Description)
                .Query(criterion.FullText)
            )
        )
        .Filter(f => f
            .And(a => a
                .Range(r => r
                    .Field(item => item.Price)                           
                    // AsString extension allows to convert nullable values to string or null
                    .From(criterion.MinPrice.AsString())
                    .To(criterion.MaxPrice.AsString())
                )
                .Term(t => t
                    .Field(user => user.Active).Value(criterion.Active.AsString())
                )
            )
        ).BuildBeautified();
}

And that's all - no ugly ifs or switches.
You just write query builder using most complex scenario, and then it will build only defined criterions.

If we call this function with BuildQuery( new Criterion { FullText = "text" }) then it will generate:

{
    "query": {
        "query_string": {
            "fields": ["Name", "Description"],
            "query": "text"
        }
    }
}

so it omits all not defined filters.

See Condion-less Query Builder Gist for complete sample.

Facets

ES documentation: http://www.elasticsearch.org/guide/reference/api/search/facets/index.html

For now only Terms facet, Terms Stats facet, Statistical facet, Range facet and Filter Facet supported.

You can construct facet queries using the following syntax:

public string BuildFacetQuery(Criterion criterion)
{
  return new QueryBuilder<Item>()
        .Query(q => q
            .QueryString(qs => qs
                .Fields(item => item.Name, item => item.Description)
                .Query(criterion.FullText)
            )
        )

        // Facets Part
        .Facets(facets => facets
            .Terms(t => t
                .FacetName("ItemsPerCategoryCount")
                .Field(item => item.Category)
                .Size(100)
                )
        )
        .BuildBeautified();
}

To read facets result you need to deserialize it to SearchResults and access its .facet property:

  // Build faceted query with FullText criterion defined.
  string query = BuildFacetQuery(new Criterion { FullText = "text" });
  string result = connection.Post(Commands.Search("store", "item"), query);

  // Parse facets query result 
  var searchResults = serializer.ToSearchResult<Item>(result);
  var itemsPerCategoryTerms = searchResults.facets.Facet<TermsFacetResult>("ItemsPerCategoryCount").terms;

  foreach (var facetTerm in itemsPerCategoryTerms)
  {
      Console.WriteLine("Category: {0}  Items Count: {1}".F(facetTerm.term, facetTerm.count));
  }

See Facet Query Builder Gist for complete sample.

Highlighting

ES documentation: http://www.elasticsearch.org/guide/reference/api/search/highlighting/

You can construct highlighted queries using the following syntax:

string query = new QueryBuilder<Note>()
    .Query(q => q
        .QueryString(qs => qs
            .Fields(c => c.Caption)
            .Query("Note")
        )
     )
     .Highlight(h => h
        .PreTags("<b>")
        .PostTags("</b>")
        .Fields(
             f => f.FieldName(n => n.Caption).Order(HighlightOrder.score),
             f => f.FieldName("_all")
        )
     )
    .BuildBeautified();

To get highlighted fragments you need to deserialize results to SearchResult<T> and access highlight property of each hit:

// Execute query and deserialize results.
string results = connection.Post(Commands.Search("notes", "note"), query);
var noteResults = serializer.ToSearchResult<Note>(results);

// Array of higlighted fragments for Caption field for the first hit.
var hit = noteResults.hits.hits[0];
string[] fragments = hit.highlight["Caption"];

See Highlighting Gist for complete sample.

Scrolling

ES documentation: http://www.elasticsearch.org/guide/reference/api/search/scroll/

You can construct scrolling search request by specifing scroll keep alive time in SearchCommand:

string scrollingSearchCommand = new SearchCommand(index:"notes", type:"note")
                                      .Scroll("5m")
                                      .SearchType(SearchType.scan);

To scroll found documents you need to deserialize results to SearchResult<T> and get the _scroll_id field. Then you should execute SearchScrollCommand with acquired scroll_id

// Execute query and deserialize results.
string results = connection.Post(scrollingSearchCommand, queryJson);
var noteResults = serializer.ToSearchResult<Note>(results);

// Get the initial scroll ID
string scrollId = scrollResults._scroll_id;

// Execute SearchScroll request to scroll found documents.
results = connection.Get(Commands.SearchScroll(scrollId).Scroll("5m"));

See Scrolling Gist for complete sample.

Mapping

ES documentation: http://www.elasticsearch.org/guide/reference/mapping/

Mapping of core and object types could be performed in the following manner:

private static string BuildCompanyMapping()
    {
        return new MapBuilder<Company>()
            .RootObject(typeName: "company",
                        map: r => r
                .All(a => a.Enabled(false))
                .Dynamic(false)
                .Properties(pr => pr
                    .String(company => company.Name, f => f.Analyzer(DefaultAnalyzers.standard).Boost(2))
                    .String(company => company.Description, f => f.Analyzer(DefaultAnalyzers.standard))
                    .String(company => company.Fax, f => f.Analyzer(DefaultAnalyzers.keyword))

                    .Object(company => company.Address, address => address
                        .Properties(ap => ap
                            .String(addr => addr.City)
                            .String(addr => addr.State)
                            .String(addr => addr.Country)
                        )
                    )

                    .NestedObject(company => company.Contacts, o => o
                        .Properties(p => p
                            .String(contact => contact.Name)
                            .String(contact => contact.Department)
                            .String(contact => contact.Email)

                            // It's unnecessary to specify opt.Type(NumberMappingType.Integer)
                            // cause it will be inferred from property type.
                            // Showed here only for educational purpose.
                            .Number(contact => contact.Age, opt => opt.Type(NumberMappingType.Integer))

                            .Object(ct => ct.Address, oa => oa
                                .Properties( pp => pp
                                    .String(a => a.City)
                                    .String(a => a.State)
                                    .String(a => a.Country)
                                )
                            )
                        )
                    )
                )
          )
          .BuildBeautified();

To apply mapping you need to use PutMappingCommand:

var connection = new ElasticConnection("localhost", 9200);
string jsonMapping = BuildCompanyMapping();

connection.Put(new PutMappingCommand("store", "company"), jsonMapping);

See Mapping Builder Gist for complete sample.

Index Settings

ES documentation: http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings.html

You can build index settings by using IndexSettinsBuilder:

private static string BuildIndexSettings()
{
    return new IndexSettingsBuilder()
        .Analysis(als => als
            .Analyzer(a => a
                .Custom("lowerkey", custom => custom
                    .Tokenizer(DefaultTokenizers.keyword)
                    .Filter(DefaultTokenFilters.lowercase)
                )
                .Custom("fulltext", custom => custom
                    .CharFilter(DefaultCharFilters.html_strip)
                    .Tokenizer(DefaultTokenizers.standard)
                    .Filter(DefaultTokenFilters.word_delimiter,
                            DefaultTokenFilters.lowercase,
                            DefaultTokenFilters.stop,
                            DefaultTokenFilters.standard)
                )
            )
        )
        .BuildBeautified();
}

You can put index settings to index by UpdateSettingsCommand or by passing settings to index creation command:

var connection = new ElasticConnection("localhost", 9200);

var settings = BuildIndexSettings();

if (IsIndexExists("store", connection))
{
    // We can't update settings on active index.
    // So we need to close it, then update settings and then open index back.
    connection.Post(new CloseCommand("store"));

    connection.Put(new UpdateSettingsCommand("store"), settings);

    connection.Post(new OpenCommand("store"));
}
else
{
    // Create Index with settings.
    connection.Put(Commands.Index("store").Refresh(), settings);
}

See Index Settings Gist for complete sample.

Special thanks to devoyster (Andriy Kozachuk) for providing Index Settings support.

Samples

If something is missed

In case you need ElasticSearch feature that not yet covered by PlainElastic.Net, just remember that everything passed to ES connection is a string, so you can add missed functionality using .Custom(string) function, that exists in every builder.

return new QueryBuilder<Item>()
    .Query(q => q
        .Term(t => t
              .Field(user => user.Active)
              .Value(true.ToString())

              // Custom string representing boost part.
              .Custom("\"boost\": 3")
          )
    )
    .BuildBeautified();

or even more - just pass you string with JSON to ES connection.

Also don't forget to add an issue to PlainElastic.Net github repository PlainElastic Issues so I can add this functionality to the future builds.

License

PlainElastic.Net is free software distributed under the terms of MIT License (see LICENSE.txt) these terms don’t apply to other 3rd party tools, utilities or code which may be used to develop this application.

plainelastic.net's People

Contributors

angrymango avatar deaquino avatar devoyster avatar gjstockham avatar kolesnick avatar o-e avatar philo avatar turch avatar vitalyal avatar yegoroff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plainelastic.net's Issues

Sorting with Script does not work

Hello guys, I believe that the Script function of Sort.cs does not generate the right JSON.

Current:
var expression = "'_script' : {0}, 'type': {1}, 'order': {2}, 'params': {3} " ......

It should be (I think):
var expression = "'_script' : { 'script':{0}, 'type': {1}, 'order': {2}, 'params': {3} }" ......

based on http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_script_based_sorting.

I do not know if I missing something.
Many Thanks,
Ioannis

Bulk operations: does not support UPDATE

Hi,

First of all, nice project, well rounded. You work helped me a lot on my dev around ES.

Secondly, I would like to know if you are planning on an implementation of the UPDATE operation on the bulk API call ? This feature would be very helpful for me.

Here the quote from ElasticSearch website:

The possible actions are index, create, delete and update. index and create expect a source on the
next line, and have the same semantics as the op_type parameter to the standard index API
(i.e. create will fail if a document with the same index and type exists already, whereas index will
add or replace a document as necessary). delete does not expect a source on the following line,
and has the same semantics as the standard delete API. update expects that the partial doc,
upsert and script and its options are specified on the next line.
-- http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html

Thanks,
Benjamin

Allow bulk indexing with array of json strings or similiar

We don't want to index our entire objects, because less than 10% of it is "searchable". So we take the object and build json from scratch. This worked well for us, and was faster than creating another object and serializing it. Because of that, we're left with json (which is perfect for IndexCommand). I'm curious if we could get the BulkBuilder to take this raw json for each indexable item... or if the builder.Index command could support it in lieu of the current object which it deserializes.

Issue with quotation in Sort Script

Not sure if this is a bug, or if I'm doing this wrong...

If I try to create a sort script (http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_script_based_sorting) using this format:

intermediate.Sort(s => s.Script(
"doc['testfield'].value * testparam",
"number",
"{'testparam': 1.1}",
SortDirection.asc
));

The resulting query has incorrect quotes around the doc which means parsing failed:

"sort": [{ "_script": { "script": "doc["testfield"].value * testparam","type": "number","params": {"testparam": 1.1},"order": "asc" } }]

It seems to be changing the single quotes to double quotes and I get an error from elasticsearch about unexpected token 't'

How to do a nested filter?

I'm trying to build a query with a nested filter, but the NestedFilter class doesn't have a Filter method. Am I missing something or is this an oversight?

I've pushed a commit f316055 with the function added, that seems to work in my specific query but I'm new to the ElasticSearch DSL so I don't know if that's appropriate in all cases.

Pull request with the commit: #62

How to create a range facet with QueryBuilder?

Could you please provide an example on how to make a range facet with QueryBuilder? I was able to use the QueryBuilder with regular term facets, but I'm having trouble figuring out how to add the Range facets. Here's what I have so far:

var query = new QueryBuilder<ElasticCollection>()
            .Query(q => q
                .MatchAll()
            )
            .Facets(facets => facets
                .Range(r => r.FacetName("startRange").Field("start_year").Ranges())

but I'm stuck on how to specify the ranges in this format.

Also, is there a way to have multiple fields in the range facet, so that I can get a count of all items that either field A or field B within the range?

Set Newtonsoft.Json reference properties Specific Version to False

There's an easy enough work-around for us where we can map old versions forward in our app.configs, but we could save all of the build warnings by either updating which newtonsoft you require to >= 5.0.3 (tons of performance updates) or by simply marking it as "Specific Version": false in the reference properties of the project.

ShardsResult needs 'failures' property in ES 0.90.2

Not sure if this is brand new with 0.90.2, but this is the version I'm now using.

The response from a search request returned the following json:

{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 3,
"failed": 2,
"failures": [
{
"index": "test-inmemory-093d8fe9-rule",
"shard": 3,
"status": 503,
"reason": "No active shards"
},
{
"index": "test-inmemory-093d8fe9-rule",
"shard": 4,
"status": 503,
"reason": "No active shards"
}
]
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "test-inmemory-093d8fe9-rule",
"_type": "rule",
"_id": "rule1",
"_score": 1,
"_source": {
"_id": "rule1",
"Name": "testme"
}
}
]
}
}

So obviously my cluster isn't healthy, but I get a deserialization error as ShardsResult doesn't have a corresponding 'failures' property in it.

Match and multi_match

Hi,

I think it could be a good idea if multi_match were integrated to the library. It's a very usefull feature of Elasticsearch and I think it's bad it's not (yet) in the library.

Thanks,
Loïc Wenkin

Highlighting

Would love to see support for the highlighting API.

Provide some method to do cluster failover

Several of the other modern clustered technologies we use allow for the definition of a cluster when connecting, which is then either balanced or simply failed over if a connection is lost. Is there any way this could be done with elastic connections? An array of connections ordered by priority?

Sample for nested document and inner objects

HI Alexander,

You put a sample of mapping on the gist. Can you please put a sample for indexing of those classes probably there are company address and contact classes. this could really be helpful for those who are new like me

How to get all the data from elasticsearch?

Like match_all function in Elasticsearch, I want to fetch all the data from elasticsearch to my datagridview using Searchcommand. I only get 10 results but not whole data. what query should I use?

ElasticConnection timout

Recently, I have had several issues connected to exceeding request timeout:

Failure to complete bulk indexing operation. at ElasticSearchEngine.EsIndexBuilder.BulkIndexRequest(String requestContent, String documentType)
at ElasticSearchEngine.EsIndexBuilder.IndexDatabaseTable(Int32 tableIndex, IDataReader reader, String messageSuffix, Int32 messageModule)
at ElasticSearchEngine.EsIndexBuilder.IndexData()
The operation has timed out.. Stack trace: at PlainElastic.Net.ElasticConnection.ExecuteRequest(String method, String command, String jsonData)
at PlainElastic.Net.ElasticConnection.Put(String command, String jsonData)
at ElasticSearchEngine.EsIndexBuilder.BulkIndexRequest(String requestContent, String documentType)
The operation has timed out.. Stack trace: at System.Net.ConnectStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.ReadToEnd()
at PlainElastic.Net.ElasticConnection.ExecuteRequest(String method, String command, String jsonData)

After looking closely into the code, I have discovered that there is no way to set the HttpWebRequest timeout, which is default to 60 sec. Could it be possible to specify timeout when creating ElasticConnection object?

Connection.post issue

Hi,
I am trying to query elastic search as shown below.

string command = new SearchCommand("customer_index", "Customer");
string result = connection.Post(Commands.Search("customer_index", "Customer"), jsonQuery);

jsonQuery is a param and the value passed to is

{
"query":
{
"bool":{
"should":[
{"match": {"name" : "Dave"}}
]
}
}
}

the result always returned is
{"took":2,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}

not sure how to fix it. I tried the sample queries you mentioned using query builder but the result always same.

Could you please help me to fix it?

Regards,
Sony Arouje

And Or nested filter

I am having trouble implementing nested and-or filter comprising many fields. Suppose I have field 1,2,3,4,5,6 and I want to implement filter like 1 and 2 and {3 or 4 or 5} and 6. Nothing is working for this kind of situation.

ES 1.2 : MapperParsingException when create root object mapping

When I post this:

PUT /manu/dataset/_mapping
{
   "dataset": {
      "type": "object",
      "enabled": true,
      "dynamic": "strict",
      "properties": {
         "type": {
            "type": "string",
            "analyzer": "keyword"
         }
      }
   }
}

generated by

MapBuilder<Dataset>()
  .RootObject(typeName: "dataset",
    map: r => r
    .Enabled(true)
    .All(a => a.Enabled(true))
    .Custom("\"dynamic\": \"{0}\"", "strict")
    .Properties(pr => pr
    .String("type", f => f.Analyzer(DefaultAnalyzers.keyword))).Build();

ES 1.2 returns this error

org.elasticsearch.index.mapper.MapperParsingException: Root type mapping not empty after parsing! Remaining fields: [type : object]

whilst it was working well with ES 1.1

Now this works

PUT /manu/dataset/_mapping
{
   "dataset": {
      "type": "object",
      "enabled": true,
      "dynamic": "strict",
      "properties": {
         "type": {
            "type": "string",
            "analyzer": "keyword"
         }
      }
   }
}

So probably the method RootObject() should be changed to remove the "type": "object" in root object type.

Pipelined Bulk with Empty Data throws OperationException

I ran into an issue today which is on a kind of unlucky use case. To reduce concern, with this issue I did not experience any data loss.

When using the following snippet of code

 var bulkCommand = new BulkCommand(searchableIndex, typedEntities.Key);
 var bulkJsons =
                    new BulkBuilder(serializer)
                    .PipelineCollection(entities, (builder, entity) => builder.Index(entity, id: Utility.GenerateId(entity)))
                    .JoinInBatches(batchSize: 100);

The following error is thrown when entities.length is greater than batchSize and entities.length MOD batchSize equals 0. So for this case if there are 1000 entities this issue occurs, but 1001 is fine.

PlainElastic.Net.OperationException: {"error":"Failed to de
rive xcontent from org.elasticsearch.common.bytes.BytesArray@0"}

The reason for this, is I suspect that the Pipeline is providing an Empty array as the last list to come out of its enumerable.

As a side bar, thank you for maintaining a really excellent library. Up until now its done everything I could want and been really effective at doing it.

Missing and Exists not rendered by Build

        var query = new QueryBuilder<ElasticName>()
            .Query(q => q
                .Filtered(fd => fd
                    .Filter(f => f
                       .Missing(m => m.Field(col => col.first_name))
                    )
                )
            )
            .BuildBeautified();          

Returns an empty string. Same with Exists in place of Missing. Current workaround:

        var query = new QueryBuilder<ElasticName>()
            .Query(q => q
                .Filtered(fd => fd
                    .Filter(f => f
                        .Custom(@"{""exists"": {""field"": ""first_name""}}")
                    )
                )
            )
            .BuildBeautified();

It seems like Missing and Exists result in an empty query string wherever they're placed. I tried a constantScore function with those functions nested and had the same issue.

Support status in responses for 1.3.x and beyond

ES no longer returns the ok bool in responses, instead the success or failure of the response is depicted by the status code / status entry in bulk results.

Without this functionality the library is largely unusable as we can no longer check for command success.

No support for Update queries

There is no update command specified. Neither in PlainElastic.Net.Commands, not can you do a ElasticConnection.Update or something like this. If you call POST or PUT command on any object/document, it gets replaced completely, that is the _source field gets replaced with the one you specified in the POST/PUT command.

As specified in

http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/partial-updates.html

you must issue a

_update

at the end of url so that it knows that we are partially updating the document and not completely replacing it, I have found no support for this.

Fields not Rendered on build inside Facets

            .Facets(facets => facets
                .Terms(t => t
                    .FacetName("NamesPerDatabasePerCategory")
                    .Fields("category.raw", "database_name.raw")
                )
            )

Results in a query without a Facets section. Replacing the argument in Fields with an array, or proper Funcs produces the same error (facet-less query).

Like my other issue, workaround using .Custom in place now. Would also much prefer the power of Aggregations to be adopted in PlainElastic

All properties of SearchResult<T> not available in a vb.net Project.

I am able to perform a search successfully in my project, in order to get total number of results count I am trying to access the "hits" property which is unavailable in a vb.net project but if I do the same same in "c#" I am able to access it.
C# object browser view for SearchResult
c net

Vb.net object browser view for SearchResult(of T)
vb net

Can you please help me out.

Delete command returns ok = false

Delete command returns DeleteResult object with ".ok = false" when the ES actually return result like this:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 21

{"acknowledged":true}

The case is when I'm trying to delete the whole index. Workaround is to check "result.acknowledged" in addition to "result.ok".

Also, this is running against elasticsearch 1.1.1.

Probably related to this: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/_return_values.html

ok has been removed from all the responses

DeleteResult needs _version property too

The JSON returned from a DELETE request looks like this (version 20.6 of ES):

{
"ok":true,
"found":true,
"_index":"test-inmemory-docrepo_2013-04-16t15-33-59-812z_es_item",
"_type":"video",
"_id":"testcusomer/testservice/video(1234)",
"_version":2
}

This currently fails deserialization to DeleteResult - at least with the Json serializer I'm using. Haven't tried it yet with Json.NET.

Support the /_search/scroll endpoint to scan entire indices more efficiently

I'm evaluating your client for my company and have found one ElasticSearch feature that's missing: the scroll endpoint (at the bottom of the page).
http://www.elasticsearch.org/guide/reference/api/search/scroll/

Ideally, this new feature would add a new ScrollCommand class that would accept a _scroll_id. In addition, SearchCommand would have to support extracting the _scroll_id field for when SearchType is set to SearchType.scan.

While I would be ecstatic if one of you pushed a fix for this, feel free to let me know if you would like my assistance. Time permitting, I would be happy to create a patch to add this support in the coming months. Thanks for taking the time to look over this request!

Facet Filter

The following code seem to produce wrong json for the ES as it is missing facet name for the facet_filter.

        Facets<ESDocument> facets = new Facets<ESDocument>();
        facets.Terms(t => t.FacetName("brand").Field("brand"));

        FacetFilter<ESDocument> filter = new FacetFilter<ESDocument>();
        filter.Range(r => r.IncludeLower(false).IncludeUpper(true).From("Lower").To("Upper"));
        facets.FacetFilter(f => filter);

        builder.Facets(f => facets);

        json = builder.ToString();

...
},
"facets": {
"brand": {
"terms": {
"field": "brand"
}
},
"facet_filter": {
"range": {
"include_lower": false,
"include_upper": true,
"from": "Lower",
"to": "Upper"
}
}
}
...

There is also no way in API to define a simple filter so it produces the json like this (not counting .Custom()):
...
"facets" : {
"brand" : { "terms" : {"field" : "brand"} },
"color" : { "terms" : {"field" : "color"} },
"price" : {
"filter" : {
"bool" :
{
"should" :
{
"range" : {
"price_usd_default" : { "to" : 100 }
}
},
...

How do I access aggregations after serializing the result?

I am using the plainelastic version 1.1.53.0. I am unable to use the geo_distance and date_range aggregates of elasticsearch. I am working around by using custom options instead. Though I would prefer to use their power instead of custom option.

Also I am unable to access the aggregation field after deserializing the result. I only get the option to use facets here and not aggregations.

var serializer = new JsonNetSerializer();
var facets=serializer.ToSearchResult<>(result).facets;

Parent Child Indexing Help

HI,
I am new to Plainelastic. I am very much confused that how we can put parent child indexing. I need a simple and brief example.

I was trying to use the parent method while creating the child index. I first created the parent index then passed its id to the child index as you wrote in the documentation. but it generates the error that the index specified as parent doesnot exists.

Please help me

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.