Nest .net client 使用简要说明

Nest .net client

--Quick Start--

NEST is a high level elasticsearch client that still maps very closely to the original elasticsearch API. Requests and Responses have been mapped to CLR objects and NEST also comes with a powerful strongly typed query dsl.

Installing
From the package manager console inside visual studio

PM > Install Package NEST -PreRelease
Or search in the Package Manager UI for NEST and go from there

Connecting
Assumming Elasticsearch is already installed and running on your machine, go to http://localhost:9200 in your browser. You should see a similar response to this:
{
  "status" : 200,
  "name" : "Sin-Eater",
  "version" : {
    "number" : "1.0.0",
    "build_hash" : "a46900e9c72c0a623d71b54016357d5f94c8ea32",
    "build_timestamp" : "2014-02-12T16:18:34Z",
    "build_snapshot" : false,
    "lucene_version" : "4.6"
  },
  "tagline" : "You Know, for Search"
}

To connect to your local node using NEST, simply:

var node = new Uri("http://localhost:9200");

var settings = new ConnectionSettings(
    node, 
    defaultIndex: "my-application"
);

var client = new ElasticClient(settings);
Here we create new a connection to our node and specify a default index to use when we don't explictly specify one. This can greatly reduce the places a magic string or constant has to be used.

NOTE: specifying defaultIndex is optional but NEST might throw an exception later on if no index is specified. In fact a simple new ElasticClient() is sufficient to chat with http://localhost:9200 but explicitly specifying connection settings is recommended.

node here is a Uri but can also be an IConnectionPool see the Elasticsearch.net section on connecting

Indexing
Now imagine we have a Person POCO

public class Person
{
    public string Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

That we would like to index in elasticsearch. Indexing is now as simple as calling.
var person = new Person
{
    Id = "1",
    Firstname = "Martijn",
    Lastname = "Laarman"
};

var index = client.Index(person);
This will index the object to /my-application/person/1. NEST is smart enough to infer the index and typename for the Person CLR type. It was also able to get the id of 1 through convention, by looking for an Id property on the specified object. Which property it will use for the Id can also be specified using the ElasticType attribute.

The default index and type names are configurable per type. See the nest section on connecting.

Image you want to override all the defaults for this one call, you should be able to do this with NEST and yes you can. NEST inferring is very powerful but if you want to pass explicit values you can always do so.

var index = client.Index(person, i=>i
    .Index("another-index")
    .Type("another-type")
    .Id("1-should-not-be-the-id")
    .Refresh()
    .Ttl("1m")
);

This will index the document using /another-index/another-type/1-should-not-be-the-id?refresh=true&&ttl=1m as the url.

Searching
Now that we have indexed some documents we can begin to search for them.

var searchResults = client.Search(s=>s
    .From(0)
    .Size(10)
    .Query(q=>q
         .Term(p=>p.Firstname, "martijn")
    )
);

searchResults.Documents now holds the first 10 people it knows whose first name is Martijn

Please see the section on writing queries for details on how NEST helps you write terse elasticsearch queries.

Again, the same inferring rules apply as this will hit /my-application/person/_search and the same rule that inferring can be overridden also applies.

// uses /other-index/other-type/_search
var searchResults = client.Search(s=>s
    .Index("other-index")
    .OtherType("other-type")
);


// uses /_all/person/_search
var searchResults = client.Search(s=>s
   .AllIndices()
);

// uses /_search
var searchResults = client.Search(s=>s
    .AllIndices()
    .AllTypes() 
);

Object Initializer Syntax
As you can see from the previous examples, NEST provides a terse, fluent syntax for constructing API calls to Elasticsearch. However, fear not if lambdas aren't your thing, you can now use the new object initializer syntax (OIS) introduced in 1.0.

The OIS is an alternative to the familair fluent syntax of NEST and works on all API endpoints. Anything that can be done with the fluent syntax can now also be done using the OIS.

For example, the earlier indexing example above can be re-written as:

var indexRequest = new IndexRequest(person)
{
    Index = "another-index",
    Type = "another-type",
    Id = "1-should-not-be-the-id",
    Refresh = true,
    Ttl = "1m"
};


var index = client.Index(indexRequest);
And searching...

QueryContainer query = new TermQuery
{
    Field = "firstName",
    Value = "martijn"
};

var searchRequest = new SearchRequest
{
    From = 0,
    Size = 10,
    Query = query
};

var searchResults = Client.Search(searchRequest);
Many of the examples throughout this documentation will be written in both forms.

--Connecting--

This section describes how to instantiate a client and have it connect to the server.

Choosing the right connection strategy
NEST follows pretty much the same design as Elasticsearch.Net when it comes to choosing the right connection strategy.

new ElasticClient();
will create a non failover client that talks to http://localhost:9200.

var uri = new Uri("http://mynode.somewhere.com/");
var settings = new ConnectionSettings(uri, defaultIndex: "my-application");
This will create a non-failover client that talks with http://mynode.somewhere.com and uses the default index name my-application for calls which do not explicitly state an index name. Specifying a default index is optional but very handy.

If you want a failover client, instead of passing a Uri, pass an IConnectionPool. See the Elasticsearch.Net documentation on cluster failover. All of its implementations can also be used with NEST.

Changing the underlying connection
By default NEST will use HTTP to chat with Elasticsearch, alternative implementation of the transport layer can be injected using the constructors optional second parameter:

var client = new ElasticClient(settings, new ThriftConnection(settings));
NEST comes with an Http Connection HttpConnection, Thrift Connection ThriftConnection and an In-Memory Connection InMemoryConnection, that nevers hits Elasticsearch.

You can also roll your own connection if desired by implementing the IConnection interface.

Subclassing existing connection implementations
In addition to implementing your own IConnection, the existing HttpConnection and ThriftConnection are extendible and can be subclassed in order to modify or extend their behavior.

For instance, a common use case is the ability to add client certificates to web requests. You can subclass HttpConnection and override the CreateHttpWebRequest method that creates the web request, and add certificates to it like so:

public class SignedHttpConnection : HttpConnection
{
    private readonly X509CertificateCollection _certificates;

    public SignedHttpConnection(IConnectionConfigurationValues settings, X509CertificateCollection certificates)
        : base(settings)
    {
        _certificates = certificates;
    }

    protected override HttpWebRequest CreateHttpWebRequest(Uri uri, string method, byte[] data, IRequestConfiguration requestSpecificConfig)
    {
        var request = base.CreateHttpWebRequest(uri, method, data, requestSpecificConfig);
        request.ClientCertificates = _certificates;
        return request;
    }
}

Settings
The NEST client can be configured by passing in an IConnectionSettingsValues object, which is a sub-interface of Elasticsearch.Net's IConnectionConfigurationValues. Therefore all of the settings that can be used to configure Elasticsearch.Net also apply here, including the cluster failover settings

The easiest way to pass IConnectionSettingsValues is to instantiate ConnectionSettings is:

var settings = new ConnectionSettings(
        myConnectionPool, 
        defaultIndex: "my-application"
    )
    .PluralizeTypeNames();

IConnectionSettingsValues has the following options:

AddContractJsonConverters

Allows you to add a custom JsonConverter to the built-in JSON serialization by passing in a predicate for a type. This way they will be part of the cached Json.NET contract for a type.

settings.AddContractJsonConverters(t => 
    typeof (Enum).IsAssignableFrom(t) 
        ? new StringEnumConverter() 
        : null);

MapDefaultTypeIndices

Maps a CLR type to Elasticsearch indices. Takes precedence over SetDefaultIndex.

MapDefaultTypeNames

Maps Elasticsearch type names to a CLR type. Takes priority over the global SetDefaultTypeNameInferrer.

PluralizeTypeNames

This calls SetDefaultTypenameInferrer with an implementation that will pluralize type names. This used to be the default prior to Nest 1.0.

SetDefaultIndex

Sets the index to default to when no index is specified.

SetDefaultPropertyNameInferrer

By default NEST camelCases property names (EmailAddress => emailAddress) that do not have an explicit property name either via an ElasticProperty attribute or because they are part of a dictionary where the keys should be treated verbatim. Here you can register a function that transforms property names (default casing, pre- or suffixing).

SetDefaultTypeNameInferrer

Allows you to override how type names should be represented. The default will call .ToLowerInvariant() on the type's name.

SetJsonSerializerSettingsModifier

Allows you to update the internal Json.NET serializer settings to your liking. Do not use this to add custom JSON converters use AddContractJsonConverters instead.

--Inference--

Imagine we have a Person POCO
public class Person
{
    public string Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

That we would like to index in Elasticsearch

var person = new Person
{
    Id = "1",
    Firstname = "Martijn",
    Lastname = "Laarman"
};


var index = client.Index(person);
This will index the object to /my-default-index/person/1.

NEST is smart enough to infer the index and type name for the Person CLR type. It was also able to get the id of 1 by the convention of looking for Id property on the specified object. Where it will look for the Id can be specified using the ElasticType attribute.

As noted in the quick start you can always pass explicit values for inferred ones.

var index = client.Index(person, i=>i
    .Index("another-index")
    .Type("another-type")
    .Id("1-should-not-be-the-id")
    .Refresh()
    .Ttl("1m")
);

This will index the document using /another-index/another-type/1-should-not-be-the-id?refresh=true&ttl=1m as the URL.

There are a couple of places within NEST where inference comes in to play...

Index Name Inference
Whenever an explicit index name is not provided, NEST will look to see if the type has its own default index name on the connection settings.

 settings.MapDefaultTypeIndices(d=>d
    .Add(typeof(MyType), "my-type-index")
 );

 client = new ElasticClient(settings, defaultIndex: "my-default-index");

 // searches in /my-type-index/mytype/_search
 client.Search()

 // searches in /my-default-index/person/_search
 client.Search()

MyType defaults to my-type-index because it is explicitly configured, but Person will default to the global fallback my-default-index.

Type Name Inference
Whenever NEST needs a type name but is not given one explicitly, it will use the given CLR type to infer it's Elasticsearch type name.

settings.MapDefaultTypeNames(d=>d
    .Add(typeof(MyType), "MY_TYPO")
);

// searches in /inferred-index/MY_TYPO/_search
client.Search();


// searches in /inferred-index/person/_search
client.Search();

Another way of setting an explicit inferred value for a type is through setting an attribute:

[ElasticType(Name="automobile")]
public class Car {} 
As you can also see in the search example, NEST by default lowercases type names that do not have a configured inferred value.

settings.SetDefaultTypeNameInferrer(t=>t.Name.ToUpperInvariant());
Now all type names that have not been explictly specified or have not been explicitly configured will be uppercased.

Prior to NEST 1.0 type names were by default lowercased AND pluralized, if you want this behavior back use:

settings.PluralizeTypeNames();
Property Name Inference
In many places NEST allows you to pass property names and JSON paths as C# expressions, i.e:

.Query(q=>q
    .Term(p=>p.Followers.First().FirstName, "martijn"))

NEST by default will camelCase properties. So the FirstName property above will be translated to "followers.firstName".
This can be configured by setting

settings.SetDefaultPropertyNameInferrer(p=>p);
This will leave property names untouched.

Properties marked with [ElasticAttibute(Name="")] or [JsonProperty(Name="")] will pass the configured name verbatim.

Id Inference
Whenever an object is passed that needs to specify an id (i.e index, bulk operations) the object is inspected to see if it has an Id property and if so, that value will be used.

This inspection happens once per type. The result of the function call that returns the id for an object of type T is cached; therfore, it is only called once per object of type T throughout the applications lifetime.

An example of this is at the top of this documentation where the Index() call could figure out the object's id was 1.

You can control which propery holds the Id:

[ElasticType(IdProperty="CrazyGuid")]
public class Car 
{
    public Guid CrazyGuidId { get; set; }
}

This will cause the the id inferring to happen on CrazyGuid instead of Id.

--Handling Responses--

All the return objects from API calls in NEST implement:
public interface IResponse
{
    bool IsValid { get; }
    IElasticsearchResponse ConnectionStatus { get; }
    ElasticInferrer Infer { get; }
}

IsValid will return whether a response is valid or not. A response is usually only valid when an HTTP return result in the 200 range was returned. Some calls allow for 404 to be a valid response too, however.

If a response returns 200 in Elasticsearch, sometimes it will contain more information on the validity of the call inside its response. It's highly recommended to read the Elasticsearch documentation for a call and check for these properties on the response as well.

ConnectionStatus is the response as it was returned by Elasticsearch.Net. Its section on handling responses applies here as well.

--Writing Queries--

One of the most important things to grasp when using NEST is how to write queries. NEST offers you several possibilities.
All the examples in this section are assumed to be wrapped in:

var result = client.Search(s=>s
    .From(0)
    .Size(10)
    // Query here
);

Or if using the object initializer syntax:

var result = new SearchRequest
{
    From = 0,
    Size = 10,
    // Query here

};

Raw Strings
Although not preferred, many folks like to build their own JSON strings and just pass that along:

.QueryRaw("\"match_all\" : { }")
.FilterRaw("\"match_all\" : { }")

NEST does not modify this in anyway and just writes this straight into the JSON output.

Query DSL
The preferred way to write queries, since it gives you alot of cool features.

Lambda Expressions

.Query(q=>q
    .Term(p=>p.Name, "NEST")
)
Here you'll see we can use expressions to address properties in a type safe matter. This also works for IEnumerable types e.g.

.Query(q=>q
    .Term(p=>p.Followers.First().FirstName, "NEST")
)

Because these property lookups are expressions you don't have to do any null checks. The previous would expand to the followers.firstName property name.

Of course if you need to pass the property name as string NEST will allow you to do so:

.Query(q=>q
    .Term("followers.firstName", "martijn")
)

This can be alternatively written using the object initializer syntax:

QueryContainer query = new TermQuery
{
    Field = "followers.firstName",
    Value = "NEST"
};

Query = query
Static Query/Filter Generator

Sometimes you'll need to reuse a filter or query often. To aid with this you can also write queries like this:

var termQuery = Query
    .Term(p=>p
        .Followers.First().FirstName, "martijn");


.Query(q=>q
    .Bool(bq=>bq
        .Must(
            mq=>mq.MatchAll()
            , termQuery
        )
    )
)

Similarly Filter.[Filter]() methods exist for filters.

Boolean Queries

As can be seen in the previous example writing out boolean queries can turn into a really tedious and verbose effort. Luckily, NEST supports bitwise operators, so we can rewrite the previous as such:

.Query(q=>q.MatchAll() && termQuery)
Note how we are mixing and matching the lambda and static queries here.
We can also do the same thing using the OIS:

QueryContainer query = new MatchAllQuery() && new TermQuery
{
    Field = "firstName",
    Value = "martijn"
};

Similary an OR looks like this:

Fluent...

.Query(q=>q
    q.Term("name", "Nest")
    || q.Term("name", "Elastica")
)


QueryContainer query1 = new TermQuery
{
    Field = "name",
    Value = "Nest"
};
QueryContainer query2 = new TermQuery
{
    Field = "name",
    Value = "Elastica"
};

Query = query1 || query2
NOT's are also supported:


.Query(q=>q
    q.Term("language", "php")
    && !q.Term("name", "Elastica")
)


Query = query1 && !query2
This will query for all the php clients except Elastica.

You can mix and match this to any level of complexity until it satisfies your query requirements.

.Query(q=>q
    (q.Term("language", "php")
        && !q.Term("name", "Elastica")
    )
    ||
    q.Term("name", "NEST")
)



Query = (query1 && !query2) || query3
Will query all php clients except Elastica or where the name equals NEST.

Clean Output Support

Normally writing three boolean must clauses looks like this (psuedo code)

must
    clause1
    clause2
    clause3
A naive implemenation of the bitwise operators would make all the queries sent to Elasticsearch look like

must
    must
        clause1
        clause2
    clause3
This degrades rather rapidly and makes inspecting generated queries quite a chore. NEST does its best to detect these cases and will always write them in the first, cleaner form.

Conditionless Queries
Writing complex boolean queries is one thing, but more often then not you'll want to make decisions on how to query based on user input.

public class UserInput
{
    public string Name { get; set; }
    public string FirstName { get; set; }
    public int? LOC { get; set; }
}

and then

.Query(q=> {
        BaseQuery query = null;
        if (!string.IsNullOrEmpty(userInput.Name))
            query &= q.Term(p=>p.Name, userInput.Name);
        if (!string.IsNullOrEmpty(userInput.FirstName))
            query &= q
                .Term("followers.firstName", userInput.FirstName);
        if (userInput.LOC.HasValue)
            query &= q.Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc.Value))
        return query;
    })

This again becomes tedious and verbose rather quickly as well. Therefore, NEST allows you to write the previous query as:

.Query(q=>
    q.Term(p=>p.Name, userInput.Name);
    && q.Term("followers.firstName", userInput.FirstName)
    && q.Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc))
)

If any of the queries would result in an empty query they won't be sent to Elasticsearch.
So if all the terms are null (or empty string) on userInput except userInput.Loc it wouldn't even wrap the range query in a boolean query but just issue a plain range query.
If all of them are empty it will result in a match_all query.
This conditionless behavior is turned on by default but can be turned of like so:

 var result = client.Search(s=>s
    .From(0)
    .Size(10)
    .Strict() //disable conditionless queries by default
    ...
);

However queries themselves can opt back in or out.

.Query(q=>
    q.Strict().Term(p=>p.Name, userInput.Name);
    && q.Term("followers.firstName", userInput.FirstName)
    && q.Strict(false).Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc))
)

In this example if userInput.Name is null or empty it will result in a DslException. The range query will use conditionless logic no matter if the SearchDescriptor uses .Strict() or not.

Please note that conditionless query logic propagates:

q.Strict().Term(p=>p.Name, userInput.Name);
&& q.Term("followers.firstName", userInput.FirstName)
&& q.Filtered(fq => fq
    .Query(qff => 
        qff.Terms(p => p.Country, userInput.Countries)
        && qff.Terms(p => p.Loc, userInput.Loc)
    )
)

If both userInput.Countries and userInput.Loc are null or empty the entire filtered query will not be issued.

--Tips & Tricks--

This page lists some general tips and tricks provided by the community

Posting Elasticsearch queries from javascript
Consider a scenario where you are using client side libraries like elasticjs
but want security to be provided by server side business logic. Consider this example using WebAPI

NOTE make sure dynamic scripting is turned off if you decide to open the full query DSL to the client!

    [RoutePrefix("api/Search")]
    public class SearchController : ApiController
    {
        [ActionName("_search")]
        public IHttpActionResult Post([FromBody]SearchDescriptor query)
        {;
            var client = new ElasticClient();

            //Your server side security goes here

            var result = client.Search(q => query);
            return Ok(result);
        }
    }

The fragments [RoutePrefix("api/Search")] and [ActionName("_search")] will let you change your elastic search Url from http://localhost:9200/_search to http://yourwebsite/api/Search/_search and let things work as normal. The fragment [FromBody]SearchDescriptor query will convert the JSON query into NEST SearchDescriptor. The fragment client.Search(q => query) will execute the query.

--
官网导航 core 部分 有有关 seting,index,mapping的说明

--官网地址--

from http://nest.azurewebsites.net/nest/writing-queries.html

你可能感兴趣的:(搜索引擎)