Giter Club home page Giter Club logo

elasticsearch's Introduction

Latest Stable Version Total Downloads License

Laravel, Lumen and Native php elasticseach query builder to build complex queries using an elegant syntax

  • Keeps you away from wasting your time by replacing array queries with a simple and elegant syntax you will love.
  • Elasticsearch data model for types and indices inspired from laravel eloquent.
  • Feeling free to create, drop, mapping and reindexing through easy artisan console commands.
  • Lumen framework support.
  • Native php and composer based applications support.
  • Can be used as a laravel scout driver.
  • Dealing with multiple elasticsearch connections at the same time.
  • Awesome pagination based on LengthAwarePagination.
  • Caching queries using a caching layer over query builder built on laravel cache.

Requirements

  • php >= 5.6.6

    See Travis CI Builds.

  • laravel/laravel >= 5.* or laravel/lumen >= 5.* or composer application

Documentation

See Full Documentation.

Installation

Laravel Installation

1) Install package using composer.
$ composer require basemkhirat/elasticsearch
2) Add package service provider (< laravel 5.5).
Basemkhirat\Elasticsearch\ElasticsearchServiceProvider::class
3) Add package alias (< laravel 5.5).
'ES' => Basemkhirat\Elasticsearch\Facades\ES::class
4) Publishing.
$ php artisan vendor:publish --provider="Basemkhirat\Elasticsearch\ElasticsearchServiceProvider"

Lumen Installation

1) Install package using composer.
$ composer require basemkhirat/elasticsearch
2) Add package service provider in bootstrap/app.php.
$app->register(Basemkhirat\Elasticsearch\ElasticsearchServiceProvider::class);
3) Copy package config directory vendor/basemkhirat/elasticsearch/src/config to root folder alongside with app directory.
4) Making Lumen work with facades by uncommenting this line in bootstrap/app.php.
$app->withFacades();

If you don't want to enable working with Lumen facades you can access the query builder using app("es").

app("es")->index("my_index")->type("my_type")->get();

# is similar to 

ES::index("my_index")->type("my_type")->get();

Composer Installation

You can install package with any composer-based applications

1) Install package using composer.
$ composer require basemkhirat/elasticsearch
2) Creating a connection.
require "vendor/autoload.php";

use Basemkhirat\Elasticsearch\Connection;

$connection = Connection::create([
    'servers' => [
        [
            "host" => '127.0.0.1',
            "port" => 9200,
            'user' => '',
            'pass' => '',
            'scheme' => 'http',
        ],
    ],
    
	// Custom handlers
	// 'handler' => new MyCustomHandler(),

    'index' => 'my_index',
    
    'logging' => [
        'enabled'   => env('ELASTIC_LOGGING_ENABLED',false),
        'level'     => env('ELASTIC_LOGGING_LEVEL','all'),
        'location'  => env('ELASTIC_LOGGING_LOCATION',base_path('storage/logs/elasticsearch.log'))
    ],  
]);


# access the query builder using created connection

$documents = $connection->search("hello")->get();

Configuration (Laravel & Lumen)

After publishing, two configuration files will be created.

  • config/es.php where you can add more than one elasticsearch server.
# Here you can define the default connection name.

'default' => env('ELASTIC_CONNECTION', 'default'),

# Here you can define your connections.

'connections' => [
	'default' => [
	    'servers' => [
	        [
	            "host" => env("ELASTIC_HOST", "127.0.0.1"),
	            "port" => env("ELASTIC_PORT", 9200),
	            'user' => env('ELASTIC_USER', ''),
	            'pass' => env('ELASTIC_PASS', ''),
	            'scheme' => env('ELASTIC_SCHEME', 'http'),
	        ]
	    ],
	    
		// Custom handlers
		// 'handler' => new MyCustomHandler(),
		
		'index' => env('ELASTIC_INDEX', 'my_index')
	]
],
 
# Here you can define your indices.
 
'indices' => [
	'my_index_1' => [
	    "aliases" => [
	        "my_index"
	    ],
	    'settings' => [
	        "number_of_shards" => 1,
	        "number_of_replicas" => 0,
	    ],
	    'mappings' => [
	        'posts' => [
                'properties' => [
                    'title' => [
                        'type' => 'string'
                    ]
                ]
	        ]
	    ]
	]
]
  • config/scout.php where you can use package as a laravel scout driver.

Working with console environment (Laravel & Lumen)

With some artisan commands you can do some tasks such as creating or updating settings, mappings and aliases.

Note that all commands are running with --connection=default option, you can change it through the command.

These are all available commands:

List All indices on server

$ php artisan es:indices:list

+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+
| configured (es.php)  | health | status | index    | uuid                   | pri | rep | docs.count | docs.deleted | store.size | pri.store.size |
+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+
| yes                  | green  | open   | my_index | 5URW60KJQNionAJgL6Q2TQ | 1   | 0   | 0          | 0            | 260b       | 260b           |
+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+

Create indices defined in es.php config file

Note that creating operation skips the index if exists.

# Create all indices in config file.

$ php artisan es:indices:create

# Create only 'my_index' index in config file

$ php artisan es:indices:create my_index 

Update indices defined in es.php config file

Note that updating operation updates indices setting, aliases and mapping and doesn't delete the indexed data.

# Update all indices in config file.

$ php artisan es:indices:update

# Update only 'my_index' index in config file

$ php artisan es:indices:update my_index 

Drop index

Be careful when using this command, you will lose your index data!

Running drop command with --force option will skip all confirmation messages.

# Drop all indices in config file.

$ php artisan es:indices:drop

# Drop specific index on sever. Not matter for index to be exist in config file or not.

$ php artisan es:indices:drop my_index 

Reindexing data (with zero downtime)

First, why reindexing?

Changing index mapping doesn't reflect without data reindexing, otherwise your search results will not work on the right way.

To avoid down time, your application should work with index alias not index name.

The index alias is a constant name that application should work with to avoid change index names.

Assume that we want to change mapping for my_index, this is how to do that:
  1. Add alias as example my_index_alias to my_index configuration and make sure that application is working with.
"aliases" => [
    "my_index_alias"
]       
  1. Update index with command:
$ php artisan es:indices:update my_index
  1. Create a new index as example my_new_index with your new mapping in configuration file.
$ php artisan es:indices:create my_new_index
  1. Reindex data from my_index into my_new_index with command:
$ php artisan es:indices:reindex my_index my_new_index

# Control bulk size. Adjust it with your server.

$ php artisan es:indices:reindex my_index my_new_index --bulk-size=2000

# Control query scroll value.

$ php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --scroll=2m

# Skip reindexing errors such as mapper parsing exceptions.

$ php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --skip-errors 

# Hide all reindexing errors and show the progres bar only.

$ php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --skip-errors --hide-errors
  1. Remove my_index_alias alias from my_index and add it to my_new_index in configuration file and update with command:
$ php artisan es:indices:update

Usage as a Laravel Scout driver

First, follow Laravel Scout installation.

All you have to do is updating these lines in config/scout.php configuration file.

# change the default driver to 'es'
	
'driver' => env('SCOUT_DRIVER', 'es'),
	
# link `es` driver with default elasticsearch connection in config/es.php
	
'es' => [
    'connection' => env('ELASTIC_CONNECTION', 'default'),
],

Have a look at laravel Scout documentation.

Elasticsearch data model

Each index type has a corresponding "Model" which is used to interact with that type. Models allow you to query for data in your types or indices, as well as insert new documents into the type.

Basic usage
<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class Post extends Model
{
        
    protected $type = "posts";
    
}

The above example will use the default connection and default index in es.php. You can override both in the next example.

<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class Post extends Model
{
    
    # [optional] Default: default elasticsearch driver
    # To override default conenction name of es.php file.
    # Assumed that there is a connection with name 'my_connection'
    protected $connection = "my_connection";
    
    # [optional] Default: default connection index
    # To override default index name of es.php file.
    protected $index = "my_index";
    
    protected $type = "posts";
    
}
Retrieving Models

Once you have created a model and its associated index type, you are ready to start retrieving data from your index. For example:

<?php

use App\Post;

$posts = App\Post::all();

foreach ($posts as $post) {
    echo $post->title;
}
Adding Additional Constraints

The all method will return all of the results in the model's type. Each elasticsearch model serves as a query builder, you may also add constraints to queries, and then use the get() method to retrieve the results:

$posts = App\Post::where('status', 1)
               ->orderBy('created_at', 'desc')
               ->take(10)
               ->get();
Retrieving Single Models
// Retrieve a model by document key...
$posts = App\Post::find("AVp_tCaAoV7YQD3Esfmp");
Inserting Models

To create a new document, simply create a new model instance, set attributes on the model, then call the save() method:

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Create a new post instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $post = new Post;

        $post->title = $request->title;

        $post->save();
    }
}
Updating Models

The save() method may also be used to update models that already exist. To update a model, you should retrieve it, set any attributes you wish to update, and then call the save method.

$post = App\Post::find(1);

$post->title = 'New Post Title';

$post->save();
Deleting Models

To delete a model, call the delete() method on a model instance:

$post = App\Post::find(1);

$post->delete();
Query Scopes

Scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all posts that are considered "popular". To define a scope, simply prefix an Eloquent model method with scope.

Scopes should always return a Query instance.

<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class Post extends Model
{
    /**
     * Scope a query to only include popular posts.
     *
     * @param \Basemkhirat\Elasticsearch\Query $query
     * @return \Basemkhirat\Elasticsearch\Query
     */
    public function scopePopular($query, $votes)
    {
        return $query->where('votes', '>', $votes);
    }

    /**
     * Scope a query to only include active posts.
     *
     * @param \Basemkhirat\Elasticsearch\Query $query
     * @return \Basemkhirat\Elasticsearch\Query
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

Once the scope has been defined, you may call the scope methods when querying the model. However, you do not need to include the scope prefix when calling the method. You can even chain calls to various scopes, for example:

$posts = App\Post::popular(100)->active()->orderBy('created_at')->get();
Accessors & Mutators
Defining An Accessor

To define an accessor, create a getFooAttribute method on your model where Foo is the "studly" cased name of the column you wish to access. In this example, we'll define an accessor for the title attribute. The accessor will automatically be called by model when attempting to retrieve the value of the title attribute:

<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class post extends Model
{
    /**
     * Get the post title.
     *
     * @param  string  $value
     * @return string
     */
    public function getTitleAttribute($value)
    {
        return ucfirst($value);
    }
}

As you can see, the original value of the column is passed to the accessor, allowing you to manipulate and return the value. To access the value of the accessor, you may simply access the title attribute on a model instance:

$post = App\Post::find(1);

$title = $post->title;

Occasionally, you may need to add array attributes that do not have a corresponding field in your index. To do so, simply define an accessor for the value:

public function getIsPublishedAttribute()
{
    return $this->attributes['status'] == 1;
}

Once you have created the accessor, just add the value to the appends property on the model:

protected $appends = ['is_published'];

Once the attribute has been added to the appends list, it will be included in model's array.

Defining A Mutator

To define a mutator, define a setFooAttribute method on your model where Foo is the "studly" cased name of the column you wish to access. So, again, let's define a mutator for the title attribute. This mutator will be automatically called when we attempt to set the value of the titleattribute on the model:

<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class post extends Model
{
    /**
     * Set the post title.
     *
     * @param  string  $value
     * @return void
     */
    public function setTitleAttribute($value)
    {
        return strtolower($value);
    }
}

The mutator will receive the value that is being set on the attribute, allowing you to manipulate the value and set the manipulated value on the model's internal $attributes property. So, for example, if we attempt to set the title attribute to Awesome post to read:

$post = App\Post::find(1);

$post->title = 'Awesome post to read';

In this example, the setTitleAttribute function will be called with the value Awesome post to read. The mutator will then apply the strtolower function to the name and set its resulting value in the internal $attributes array.

Attribute Casting

The $casts property on your model provides a convenient method of converting attributes to common data types. The $casts property should be an array where the key is the name of the attribute being cast and the value is the type you wish to cast the column to. The supported cast types are: integer, float, double, string, boolean, object and array.

For example, let's cast the is_published attribute, which is stored in our index as an integer (0 or 1) to a boolean value:

<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class Post extends Model
{
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'is_published' => 'boolean',
    ];
}

Now the is_published attribute will always be cast to a boolean when you access it, even if the underlying value is stored in the index as an integer:

$post = App\Post::find(1);

if ($post->is_published) {
    //
}

Usage as a query builder

Creating a new index

ES::create("my_index");
    
# or 
    
ES::index("my_index")->create();
Creating index with custom options (optional)
ES::index("my_index")->create(function($index){
        
    $index->shards(5)->replicas(1)->mapping([
        'my_type' => [
            'properties' => [
                'first_name' => [
                    'type' => 'string',
                ],
                'age' => [
                    'type' => 'integer'
                ]
            ]
        ]
    ])
    
});
    
# or
    
ES::create("my_index", function($index){
  
      $index->shards(5)->replicas(1)->mapping([
          'my_type' => [
              'properties' => [
                  'first_name' => [
                      'type' => 'string',
                  ],
                  'age' => [
                      'type' => 'integer'
                  ]
              ]
          ]
      ])
  
});

Dropping index

ES::drop("my_index");
    
# or
    
ES::index("my_index")->drop();

Running queries

$documents = ES::connection("default")
                ->index("my_index")
                ->type("my_type")
                ->get();    # return a collection of results

You can rewrite the above query to

$documents = ES::type("my_type")->get();    # return a collection of results

The query builder will use the default connection, index name in configuration file es.php.

Connection and index names in query overrides connection and index names in configuration file es.php.

Getting document by id
ES::type("my_type")->id(3)->first();
    
# or
    
ES::type("my_type")->_id(3)->first();
Sorting
ES::type("my_type")->orderBy("created_at", "desc")->get();
    
# Sorting with text search score
    
ES::type("my_type")->orderBy("_score")->get();
Limit and offset
ES::type("my_type")->take(10)->skip(5)->get();
Select only specific fields
ES::type("my_type")->select("title", "content")->take(10)->skip(5)->get();
Where clause
ES::type("my_type")->where("status", "published")->get();

# or

ES::type("my_type")->where("status", "=", "published")->get();
Where greater than
ES::type("my_type")->where("views", ">", 150)->get();
Where greater than or equal
ES::type("my_type")->where("views", ">=", 150)->get();
Where less than
ES::type("my_type")->where("views", "<", 150)->get();
Where less than or equal
ES::type("my_type")->where("views", "<=", 150)->get();
Where like
ES::type("my_type")->where("title", "like", "foo")->get();
Where field exists
ES::type("my_type")->where("hobbies", "exists", true)->get(); 

# or 

ES::type("my_type")->whereExists("hobbies", true)->get();
Where in clause
ES::type("my_type")->whereIn("id", [100, 150])->get();
Where between clause
ES::type("my_type")->whereBetween("id", 100, 150)->get();

# or 

ES::type("my_type")->whereBetween("id", [100, 150])->get();
Where not clause
ES::type("my_type")->whereNot("status", "published")->get(); 

# or

ES::type("my_type")->whereNot("status", "=", "published")->get();
Where not greater than
ES::type("my_type")->whereNot("views", ">", 150)->get();
Where not greater than or equal
ES::type("my_type")->whereNot("views", ">=", 150)->get();
Where not less than
ES::type("my_type")->whereNot("views", "<", 150)->get();
Where not less than or equal
ES::type("my_type")->whereNot("views", "<=", 150)->get();
Where not like
ES::type("my_type")->whereNot("title", "like", "foo")->get();
Where not field exists
ES::type("my_type")->whereNot("hobbies", "exists", true)->get(); 

# or

ES::type("my_type")->whereExists("hobbies", true)->get();
Where not in clause
ES::type("my_type")->whereNotIn("id", [100, 150])->get();
Where not between clause
ES::type("my_type")->whereNotBetween("id", 100, 150)->get();

# or

ES::type("my_type")->whereNotBetween("id", [100, 150])->get();
Search by a distance from a geo point
ES::type("my_type")->distance("location", ["lat" => -33.8688197, "lon" => 151.20929550000005], "10km")->get();

# or

ES::type("my_type")->distance("location", "-33.8688197,151.20929550000005", "10km")->get();

# or

ES::type("my_type")->distance("location", [151.20929550000005, -33.8688197], "10km")->get();  
Search using array queries
ES::type("my_type")->body([
    "query" => [
         "bool" => [
             "must" => [
                 [ "match" => [ "address" => "mill" ] ],
                 [ "match" => [ "address" => "lane" ] ]
             ]
         ]
     ]
])->get();

# Note that you can mix between query builder and array queries.
# The query builder will will be merged with the array query.

ES::type("my_type")->body([

	"_source" => ["content"]
	
	"query" => [
	     "bool" => [
	         "must" => [
	             [ "match" => [ "address" => "mill" ] ]
	         ]
	     ]
	],
	   
	"sort" => [
		"_score"
	]
     
])->select("name")->orderBy("created_at", "desc")->take(10)->skip(5)->get();

# The result query will be
/*
Array
(
    [index] => my_index
    [type] => my_type
    [body] => Array
        (
            [_source] => Array
                (
                    [0] => content
                    [1] => name
                )
            [query] => Array
                (
                    [bool] => Array
                        (
                            [must] => Array
                                (
                                    [0] => Array
                                        (
                                            [match] => Array
                                                (
                                                    [address] => mill
                                                )
                                        )
                                )
                        )
                )
            [sort] => Array
                (
                    [0] => _score
                    [1] => Array
                        (
                            [created_at] => desc
                        )
                )
        )
    [from] => 5
    [size] => 10
    [client] => Array
        (
            [ignore] => Array
                (
                )
        )
)
*/
Search the entire document
ES::type("my_type")->search("hello")->get();
    
# search with Boost = 2
    
ES::type("my_type")->search("hello", 2)->get();

# search within specific fields with different weights

ES::type("my_type")->search("hello", function($search){
	$search->boost(2)->fields(["title" => 2, "content" => 1])
})->get();
Search with highlight fields
$doc = ES::type("my_type")->highlight("title")->search("hello")->first();

# Multiple fields Highlighting is allowed.

$doc = ES::type("my_type")->highlight("title", "content")->search("hello")->first();

# Return all highlights as array using $doc->getHighlights() method.

$doc->getHighlights();

# Also you can return only highlights of specific field.

$doc->getHighlights("title");
Return only first record
ES::type("my_type")->search("hello")->first();
Return only count
ES::type("my_type")->search("hello")->count();
Scan-and-Scroll queries
# These queries are suitable for large amount of data. 
# A scrolled search allows you to do an initial search and to keep pulling batches of results
# from Elasticsearch until there are no more results left.
# It’s a bit like a cursor in a traditional database
    
$documents = ES::type("my_type")->search("hello")
                 ->scroll("2m")
                 ->take(1000)
                 ->get();

# Response will contain a hashed code `scroll_id` will be used to get the next result by running

$documents = ES::type("my_type")->search("hello")
                 ->scroll("2m")
                 ->scrollID("DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAFMFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABSxZSUDhLU3ZySFJJYXFNRV9laktBMGZ3AAAAAAAAAU4WUlA4S1N2ckhSSWFxTUVfZWpLQTBmdwAAAAAAAAFPFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABTRZSUDhLU3ZySFJJYXFNRV9laktBMGZ3")
                 ->get();

# And so on ...
# Note that you don't need to write the query parameters in every scroll. All you need the `scroll_id` and query scroll time.
    
# To clear `scroll_id` 
  
ES::type("my_type")->scrollID("DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAFMFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABSxZSUDhLU3ZySFJJYXFNRV9laktBMGZ3AAAAAAAAAU4WUlA4S1N2ckhSSWFxTUVfZWpLQTBmdwAAAAAAAAFPFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABTRZSUDhLU3ZySFJJYXFNRV9laktBMGZ3")
        ->clear();
Paginate results with 5 records per page
$documents = ES::type("my_type")->search("hello")->paginate(5);
    
# Getting pagination links
    
$documents->links();

# Bootstrap 4 pagination

$documents->links("bootstrap-4");

# Simple bootstrap 4 pagination

$documents->links("simple-bootstrap-4");

# Simple pagination

$documents->links("simple-default");

These are all pagination methods you may use:

$documents->count()
$documents->currentPage()
$documents->firstItem()
$documents->hasMorePages()
$documents->lastItem()
$documents->lastPage()
$documents->nextPageUrl()
$documents->perPage()
$documents->previousPageUrl()
$documents->total()
$documents->url($page)
Getting the query array without execution
ES::type("my_type")->search("hello")->where("views", ">", 150)->query();
Getting the original elasticsearch response
ES::type("my_type")->search("hello")->where("views", ">", 150)->response();
Ignoring bad HTTP response
ES::type("my_type")->ignore(404, 500)->id(5)->first();
Query Caching (Laravel & Lumen)

Package comes with a built-in caching layer based on laravel cache.

ES::type("my_type")->search("hello")->remember(10)->get();
	
# Specify a custom cache key

ES::type("my_type")->search("hello")->remember(10, "last_documents")->get();
	
# Caching using other available driver
	
ES::type("my_type")->search("hello")->cacheDriver("redis")->remember(10, "last_documents")->get();
	
# Caching with cache key prefix
	
ES::type("my_type")->search("hello")->cacheDriver("redis")->cachePrefix("docs")->remember(10, "last_documents")->get();
Executing elasticsearch raw queries
ES::raw()->search([
    "index" => "my_index",
    "type"  => "my_type",
    "body" => [
        "query" => [
            "bool" => [
                "must" => [
                    [ "match" => [ "address" => "mill" ] ],
                    [ "match" => [ "address" => "lane" ] ]
                ]
            ]
        ]
    ]
]);
Insert a new document
ES::type("my_type")->id(3)->insert([
    "title" => "Test document",
    "content" => "Sample content"
]);
     
# A new document will be inserted with _id = 3.
  
# [id is optional] if not specified, a unique hash key will be generated.
Bulk insert a multiple of documents at once.
# Main query

ES::index("my_index")->type("my_type")->bulk(function ($bulk){

    # Sub queries

	$bulk->index("my_index_1")->type("my_type_1")->id(10)->insert(["title" => "Test document 1","content" => "Sample content 1"]);
	$bulk->index("my_index_2")->id(11)->insert(["title" => "Test document 2","content" => "Sample content 2"]);
	$bulk->id(12)->insert(["title" => "Test document 3", "content" => "Sample content 3"]);
	
});

# Notes from the above query:

# As index and type names are required for insertion, Index and type names are extendable. This means that: 

# If index() is not specified in subquery:
# -- The builder will get index name from the main query.
# -- if index is not specified in main query, the builder will get index name from configuration file.

# And

# If type() is not specified in subquery:
# -- The builder will get type name from the main query.

# you can use old bulk code style using multidimensional array of [id => data] pairs
 
ES::type("my_type")->bulk([
 
	10 => [
		"title" => "Test document 1",
		"content" => "Sample content 1"
	],
	 
	11 => [
		"title" => "Test document 2",
		"content" => "Sample content 2"
	]
 
]);
 
# The two given documents will be inserted with its associated ids
Update an existing document
ES::type("my_type")->id(3)->update([
   "title" => "Test document",
   "content" => "sample content"
]);
    
# Document has _id = 3 will be updated.
    
# [id is required]
# Bulk update

ES::type("my_type")->bulk(function ($bulk){
    $bulk->id(10)->update(["title" => "Test document 1","content" => "Sample content 1"]);
    $bulk->id(11)->update(["title" => "Test document 2","content" => "Sample content 2"]);
});
Incrementing field
ES::type("my_type")->id(3)->increment("views");
    
# Document has _id = 3 will be incremented by 1.
    
ES::type("my_type")->id(3)->increment("views", 3);
    
# Document has _id = 3 will be incremented by 3.

# [id is required]
Decrementing field
ES::type("my_type")->id(3)->decrement("views");
    
# Document has _id = 3 will be decremented by 1.
    
ES::type("my_type")->id(3)->decrement("views", 3);
    
# Document has _id = 3 will be decremented by 3.

# [id is required]
Update using script
# increment field by script
    
ES::type("my_type")->id(3)->script(
    "ctx._source.$field += params.count",
    ["count" => 1]
);
    
# add php tag to tags array list
    
ES::type("my_type")->id(3)->script(
    "ctx._source.tags.add(params.tag)",
    ["tag" => "php"]
);
    
# delete the doc if the tags field contain mongodb, otherwise it does nothing (noop)
    
ES::type("my_type")->id(3)->script(
    "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
    ["tag" => "mongodb"]
);
Delete a document
ES::type("my_type")->id(3)->delete();
    
# Document has _id = 3 will be deleted.
    
# [id is required]
# Bulk delete

ES::type("my_type")->bulk(function ($bulk){
    $bulk->id(10)->delete();
    $bulk->id(11)->delete();
});

Releases

See Change Log.

Author

Basem Khirat - [email protected] - @basemkhirat

Bugs, Suggestions and Contributions

Thanks to everyone who has contributed to this project!

Please use Github for reporting bugs, and making comments or suggestions.

License

MIT

Have a happy searching..

elasticsearch's People

Contributors

alnutile avatar basemkhirat avatar finalgamer avatar imikemiller avatar intech avatar joecampo avatar kkomelin avatar markvaughn avatar mikemillers avatar nbz4live avatar rapita avatar thomasjsn avatar web-care 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

elasticsearch's Issues

Console commands missing (Lumen)

I followed the manual inside a freshly created Lumen application up to the point where the first command is used

$ php artisan es:indices:list

It returns:

[Symfony\Component\Console\Exception\CommandNotFoundException]
There are no commands defined in the "es:indices" namespace.

Listing all artisan commands shows that they are all missing. So my guess is that the application has a problem in the register function of the ElasticsearchServiceProvider.php where the commands are defined for command line use.

I am using:
"php": ">=7.1",
"laravel/lumen-framework": "5.4.*",
"basemkhirat/elasticsearch": "^1.0"

Any Idea what to do? Thankful for every helping hand!

Using 1.3 package without Laravel?

Error FATAL ERROR
generated by file vendor\basemkhirat\elasticsearch\src\Model.php on line 277:
Call to undefined function Basemkhirat\Elasticsearch\app()

is this package enable for native php application (without laravel) ?

Error BadRequest400Exception when try to retrieve data

When i try to get data of my ES, i get this error:

$posts = Post::all();
foreach ($posts as $post) {
    echo $post->title;
}

Post.php

<?php
namespace App;

use Basemkhirat\Elasticsearch\Model;

class Post extends Model
{    
    protected $connection = "default";    
    protected $index = "bank";    
    protected $type = "account";    
}

Error:

in Connection.php (line 611)
--
at Connection->process4xxError(array('http_method'  => 'GET', 'scheme' => 'http', 'uri' =>  '/bank/account/_search?from=0&size=10', 'body' => '{}', 'headers'  => array('Host' => array('127.0.0.1:9200'), 'Content-Type' => array('application/json'), 'Accept' => array('application/json')), 'client' => array('curl' => array(1, ':'))), array('transfer_stats' => array('url'  => 'http://127.0.0.1:9200/bank/account/_search?from=0&size=10',  'content_type' => 'text/html;charset=utf-8', 'http_code' => 411,  'header_size' => 340, 'request_size' => 285, 'filetime' => -1,  'ssl_verify_result' => 0, 'redirect_count' => 0, 'total_time'  => 0.23400000000000001, 'namelookup_time' => 0.0, 'connect_time'  => 0.20300000000000001, 'pretransfer_time' => 0.20300000000000001,  'size_upload' => 2.0, 'size_download' => 4351.0, 'speed_download'  => 18594.0, 'speed_upload' => 8.0, 'download_content_length'  => 4351.0, 'upload_content_length' => 2.0, 'starttransfer_time'  => 0.23400000000000001, 'redirect_time' => 0.0, 'redirect_url'  => '', 'primary_ip' => '127.0.0.1', 'certinfo' => array(),  'primary_port' => 40080, 'local_ip' => '127.0.0.1', 'local_port'  => 53588, 'error' => '', 'errno' => 0), 'curl' => array('error'  => '', 'errno' => 0), 'effective_url' =>  'http://127.0.0.1:9200/bank/account/_search?from=0&size=10',  'headers' => array('Server' => array('squid/3.5.20'), 'Mime-Version' => array('1.0'), 'Date' => array('Tue, 21 Nov 2017 11:03:51 GMT'), 'Content-Type' => array('text/html;charset=utf-8'), 'Content-Length' => array('4351'), 'X-Squid-Error' => array('ERR_INVALID_REQ 0'), 'X-Cache' => array('MISS from pxl0squid109'), 'X-Cache-Lookup' => array('NONE from pxl0squid109:4001'), 'Via' => array('1.1 pxl0squid109 (squid/3.5.20)'), 'Connection' => array('close')),  'version' => '1.1', 'status' => 411, 'reason' => 'Length  Required', 'body' => '<html><head><meta  type="copyright" content="Copyright (C) 1996-2016 The Squid Software  Foundation and contributors"><meta http-equiv="Content-Type"  content="text/html; charset=utf-8"><title>ERRO: A URL  requisitada não pode ser recuperada</title><style  type="text/css"><!--  /* Stylesheet for Squid Error pages Adapted  from design by Free CSS Templates http://www.freecsstemplates.org  Released for free under a Creative Commons Attribution 2.5 License*//*  Page basics */* {	font-family: verdana, sans-serif;}html body {	margin:  0;	padding: 0;	background: #efefef;	font-size: 12px;	color: #1e1e1e;}/*  Page displayed title area */#titles {	margin-left: 15px;	padding: 10px;	 padding-left: 100px;	background:  url(\'http://www.squid-cache.org/Artwork/SN.png\') no-repeat left;}/*  initial title */#titles h1 {	color: #000000;}#titles h2 {	color:  #000000;}/* special event: FTP success page titles */#titles ftpsuccess { 	background-color:#00ff00;	width:100%;}/* Page displayed body content  area */#content {	padding: 10px;	background: #ffffff;}/* General text  */p {}/* error brief description */#error p {}/* some data which may  have caused the problem */#data {}/* the error message received from the  system or other software */#sysmsg {}pre {     font-family:sans-serif;}/* special event: FTP / Gopher directory listing  */#dirmsg {    font-family: courier;    color: black;    font-size:  10pt;}#dirlisting {    margin-left: 2%;    margin-right: 2%;}#dirlisting  tr.entry td.icon,td.filename,td.size,td.date {    border-bottom:  groove;}#dirlisting td.size {    width: 50px;    text-align: right;     padding-right: 5px;}/* horizontal lines */hr {	margin: 0;}/* page  displayed footer area */#footer {	font-size: 9px;	padding-left:  10px;}body:lang(fa) { direction: rtl; font-size: 100%; font-family:  Tahoma, Roya, sans-serif; float: right; }:lang(he) { direction: rtl; }  --></style></head><body  id="ERR_INVALID_REQ"><div  id="titles"><h1>ERRO</h1><h2>A URL requisitada não  pôde ser recuperada</h2></div><hr><div  id="content"><p><b>Requisição Inválida</b> erro  encontrado ao tentar processar a requisição:</p><blockquote  id="data"><pre>GET /bank/account/_search?from=0&amp;size=10  HTTP/1.1Proxy-Authorization: ** NOT DISPLAYED ** Authorization: ** NOT DISPLAYED ** Proxy-Connection: Keep-Alive Content-Type: application/json Accept: application/json Content-Length: 2 Host: 127.0.0.1:9200 </pre></blockquote><p>Alguns dos possíveis problemas  são:</p><ul><li id="missing-method"><p>Método de  requisição faltando ou desconhecido.</p></li><li  id="missing-url"><p>URL faltando.</p></li><li  id="missing-protocol"><p>Identificador HTTP faltando  (HTTP/1.0).</p></li><li><p>Requisição é muito  grande.</p></li><li><p>Content-Length faltando  para requisições POST ou  PUT.</p></li><li><p>Caracter ilegal no nome de  host; underscores não são  permitidos.</p></li><li><p>HTTP/1.1  <q>Expect:</q> requisição proveniente de um software  HTTP/1.0.</p></li></ul><p>Seu administrador do  cache é <a  href="mailto:root?subject=CacheErrorInfo%20-%20ERR_INVALID_REQ&amp;body=CacheHost%3A%20pxl0squid109%0D%0AErrPage%3A%20ERR_INVALID_REQ%0D%0AErr%3A%20%5Bnone%5D%0D%0ATimeStamp%3A%20Tue,%2021%20Nov%202017%2011%3A03%3A51%20GMT%0D%0A%0D%0AClientIP%3A%20172.18.3.46%0D%0A%0D%0AHTTP%20Request%3A%0D%0AGET%20%2Fbank%2Faccount%2F_search%3Ffrom%3D0%26size%3D10%20HTTP%2F1.1%0AProxy-Authorization%3A%20Basic%20RjU4MjkyNTM6MjU4OTcwMTI%3D%0D%0AAuthorization%3A%20Basic%20Og%3D%3D%0D%0AProxy-Connection%3A%20Keep-Alive%0D%0AContent-Type%3A%20application%2Fjson%0D%0AAccept%3A%20application%2Fjson%0D%0AContent-Length%3A%202%0D%0AHost%3A%20127.0.0.1%3A9200%0D%0A%0D%0A%0D%0A">root</a>.</p><br></div><script  language="javascript">if (\'GET\' != \'[unknown method]\')  document.getElementById(\'missing-method\').style.display = \'none\';if  (\'http://127.0.0.1:9200/bank/account/_search?from=0&amp;size=10\'  != \'[no URL]\') document.getElementById(\'missing-url\').style.display =  \'none\';if (\'http\' != \'[unknown protocol]\')  document.getElementById(\'missing-protocol\').style.display =  \'none\';</script><hr><div id="footer"><p>Gerado  Tue, 21 Nov 2017 11:03:51 GMT por pxl0squid109  (squid/3.5.20)</p><!-- ERR_INVALID_REQ  --></div></body></html>'), array())in Connection.php (line 274)
at Connection->Elasticsearch\Connections\{closure}(array('transfer_stats' => array('url'  => 'http://127.0.0.1:9200/bank/account/_search?from=0&size=10',  'content_type' => 'text/html;charset=utf-8', 'http_code' => 411,  'header_size' => 340, 'request_size' => 285, 'filetime' => -1,  'ssl_verify_result' => 0, 'redirect_count' => 0, 'total_time'  => 0.23400000000000001, 'namelookup_time' => 0.0, 'connect_time'  => 0.20300000000000001, 'pretransfer_time' => 0.20300000000000001,  'size_upload' => 2.0, 'size_download' => 4351.0, 'speed_download'  => 18594.0, 'speed_upload' => 8.0, 'download_content_length'  => 4351.0, 'upload_content_length' => 2.0, 'starttransfer_time'  => 0.23400000000000001, 'redirect_time' => 0.0, 'redirect_url'  => '', 'primary_ip' => '127.0.0.1', 'certinfo' => array(),  'primary_port' => 40080, 'local_ip' => '127.0.0.1', 'local_port'  => 53588, 'error' => '', 'errno' => 0), 'curl' => array('error'  => '', 'errno' => 0), 'effective_url' =>  'http://127.0.0.1:9200/bank/account/_search?from=0&size=10',  'headers' => array('Server' => array('squid/3.5.20'), 'Mime-Version' => array('1.0'), 'Date' => array('Tue, 21 Nov 2017 11:03:51 GMT'), 'Content-Type' => array('text/html;charset=utf-8'), 'Content-Length' => array('4351'), 'X-Squid-Error' => array('ERR_INVALID_REQ 0'), 'X-Cache' => array('MISS from pxl0squid109'), 'X-Cache-Lookup' => array('NONE from pxl0squid109:4001'), 'Via' => array('1.1 pxl0squid109 (squid/3.5.20)'), 'Connection' => array('close')),  'version' => '1.1', 'status' => 411, 'reason' => 'Length  Required', 'body' => '<html><head><meta  type="copyright" content="Copyright (C) 1996-2016 The Squid Software  Foundation and contributors"><meta http-equiv="Content-Type"  content="text/html; charset=utf-8"><title>ERRO: A URL  requisitada não pode ser recuperada</title><style  type="text/css"><!--  /* Stylesheet for Squid Error pages Adapted  from design by Free CSS Templates http://www.freecsstemplates.org  Released for free under a Creative Commons Attribution 2.5 License*//*  Page basics */* {	font-family: verdana, sans-serif;}html body {	margin:  0;	padding: 0;	background: #efefef;	font-size: 12px;	color: #1e1e1e;}/*  Page displayed title area */#titles {	margin-left: 15px;	padding: 10px;	 padding-left: 100px;	background:  url(\'http://www.squid-cache.org/Artwork/SN.png\') no-repeat left;}/*  initial title */#titles h1 {	color: #000000;}#titles h2 {	color:  #000000;}/* special event: FTP success page titles */#titles ftpsuccess { 	background-color:#00ff00;	width:100%;}/* Page displayed body content  area */#content {	padding: 10px;	background: #ffffff;}/* General text  */p {}/* error brief description */#error p {}/* some data which may  have caused the problem */#data {}/* the error message received from the  system or other software */#sysmsg {}pre {     font-family:sans-serif;}/* special event: FTP / Gopher directory listing  */#dirmsg {    font-family: courier;    color: black;    font-size:  10pt;}#dirlisting {    margin-left: 2%;    margin-right: 2%;}#dirlisting  tr.entry td.icon,td.filename,td.size,td.date {    border-bottom:  groove;}#dirlisting td.size {    width: 50px;    text-align: right;     padding-right: 5px;}/* horizontal lines */hr {	margin: 0;}/* page  displayed footer area */#footer {	font-size: 9px;	padding-left:  10px;}body:lang(fa) { direction: rtl; font-size: 100%; font-family:  Tahoma, Roya, sans-serif; float: right; }:lang(he) { direction: rtl; }  --></style></head><body  id="ERR_INVALID_REQ"><div  id="titles"><h1>ERRO</h1><h2>A URL requisitada não  pôde ser recuperada</h2></div><hr><div  id="content"><p><b>Requisição Inválida</b> erro  encontrado ao tentar processar a requisição:</p><blockquote  id="data"><pre>GET /bank/account/_search?from=0&amp;size=10  HTTP/1.1Proxy-Authorization: ** NOT DISPLAYED ** Authorization: ** NOT DISPLAYED ** Proxy-Connection: Keep-Alive Content-Type: application/json Accept: application/json Content-Length: 2 Host: 127.0.0.1:9200 </pre></blockquote><p>Alguns dos possíveis problemas  são:</p><ul><li id="missing-method"><p>Método de  requisição faltando ou desconhecido.</p></li><li  id="missing-url"><p>URL faltando.</p></li><li  id="missing-protocol"><p>Identificador HTTP faltando  (HTTP/1.0).</p></li><li><p>Requisição é muito  grande.</p></li><li><p>Content-Length faltando  para requisições POST ou  PUT.</p></li><li><p>Caracter ilegal no nome de  host; underscores não são  permitidos.</p></li><li><p>HTTP/1.1  <q>Expect:</q> requisição proveniente de um software  HTTP/1.0.</p></li></ul><p>Seu administrador do  cache é <a  href="mailto:root?subject=CacheErrorInfo%20-%20ERR_INVALID_REQ&amp;body=CacheHost%3A%20pxl0squid109%0D%0AErrPage%3A%20ERR_INVALID_REQ%0D%0AErr%3A%20%5Bnone%5D%0D%0ATimeStamp%3A%20Tue,%2021%20Nov%202017%2011%3A03%3A51%20GMT%0D%0A%0D%0AClientIP%3A%20172.18.3.46%0D%0A%0D%0AHTTP%20Request%3A%0D%0AGET%20%2Fbank%2Faccount%2F_search%3Ffrom%3D0%26size%3D10%20HTTP%2F1.1%0AProxy-Authorization%3A%20Basic%20RjU4MjkyNTM6MjU4OTcwMTI%3D%0D%0AAuthorization%3A%20Basic%20Og%3D%3D%0D%0AProxy-Connection%3A%20Keep-Alive%0D%0AContent-Type%3A%20application%2Fjson%0D%0AAccept%3A%20application%2Fjson%0D%0AContent-Length%3A%202%0D%0AHost%3A%20127.0.0.1%3A9200%0D%0A%0D%0A%0D%0A">root</a>.</p><br></div><script  language="javascript">if (\'GET\' != \'[unknown method]\')  document.getElementById(\'missing-method\').style.display = \'none\';if  (\'http://127.0.0.1:9200/bank/account/_search?from=0&amp;size=10\'  != \'[no URL]\') document.getElementById(\'missing-url\').style.display =  \'none\';if (\'http\' != \'[unknown protocol]\')  document.getElementById(\'missing-protocol\').style.display =  \'none\';</script><hr><div id="footer"><p>Gerado  Tue, 21 Nov 2017 11:03:51 GMT por pxl0squid109  (squid/3.5.20)</p><!-- ERR_INVALID_REQ  --></div></body></html>'))in FulfilledPromise.php (line 25)
at FulfilledPromise->then(object(Closure), null, null)in CompletedFutureValue.php (line 55)
at CompletedFutureValue->then(object(Closure), null, null)in Core.php (line 341)
at Core::proxy(object(CompletedFutureArray), object(Closure))in Connection.php (line 295)
at Connection->Elasticsearch\Connections\{closure}(array('http_method'  => 'GET', 'scheme' => 'http', 'uri' =>  '/bank/account/_search?from=0&size=10', 'body' => '{}', 'headers'  => array('Host' => array('127.0.0.1:9200'), 'Content-Type' => array('application/json'), 'Accept' => array('application/json')), 'client' => array('curl' => array(1, ':'))), object(Connection), object(Transport), array())in Connection.php (line 172)
at Connection->performRequest('GET', '/bank/account/_search', array('from' => 0, 'size' => 10), '{}', array(), object(Transport))in Transport.php (line 100)
at Transport->performRequest('GET', '/bank/account/_search', array('from' => 0, 'size' => 10), array(), array())in Client.php (line 1578)
at Client->performRequest(object(Search))in Client.php (line 952)
at Client->search(array('from' => 0, 'size' => 10))in Query.php (line 860)
at Query->response(null)in Query.php (line 828)
at Query->getResult(null)in Query.php (line 799)
at Query->get()in Model.php (line 300)
at Model::all()in ExampleController.php (line 20)
at ExampleController->teste()
at call_user_func_array(array(object(ExampleController), 'teste'), array())in BoundMethod.php (line 29)
at BoundMethod::Illuminate\Container\{closure}()in BoundMethod.php (line 87)
at BoundMethod::callBoundMethod(object(Application), array(object(ExampleController), 'teste'), object(Closure))in BoundMethod.php (line 31)
at BoundMethod::call(object(Application), array(object(ExampleController), 'teste'), array(), null)in Container.php (line 549)
at Container->call(array(object(ExampleController), 'teste'), array())in RoutesRequests.php (line 373)
at Application->callControllerCallable(array(object(ExampleController), 'teste'), array())in RoutesRequests.php (line 339)
at Application->callLumenController(object(ExampleController), 'teste', array(true, array('uses' => 'App\\Http\\Controllers\\ExampleController@teste'), array()))in RoutesRequests.php (line 313)
at Application->callControllerAction(array(true, array('uses' => 'App\\Http\\Controllers\\ExampleController@teste'), array()))in RoutesRequests.php (line 275)
at Application->callActionOnArrayBasedRoute(array(true, array('uses' => 'App\\Http\\Controllers\\ExampleController@teste'), array()))in RoutesRequests.php (line 260)
at Application->handleFoundRoute(array(true, array('uses' => 'App\\Http\\Controllers\\ExampleController@teste'), array()))in RoutesRequests.php (line 160)
at Application->Laravel\Lumen\Concerns\{closure}()in RoutesRequests.php (line 413)
at Application->sendThroughPipeline(array(), object(Closure))in RoutesRequests.php (line 166)
at Application->dispatch(null)in RoutesRequests.php (line 107)
at Application->run()in index.php (line 28)

If i use postman or insomnia i get the data:


{
	"took": 2,
	"timed_out": false,
	"_shards": {
		"total": 5,
		"successful": 5,
		"skipped": 0,
		"failed": 0
	},
	"hits": {
		"total": 1000,
		"max_score": 1.0,
		"hits": [
			{
				"_index": "bank",
				"_type": "account",
				"_id": "25",
				"_score": 1.0,
				"_source": {
					"account_number": 25,
					"balance": 40540,
					"firstname": "Virginia",
					"lastname": "Ayala",
					"age": 39,
					"gender": "F",
					"address": "171 Putnam Avenue",
					"employer": "Filodyne",
					"email": "[email protected]",
					"city": "Nicholson",
					"state": "PA"
				}
			},
			{
				"_index": "bank",
				"_type": "account",
				"_id": "44",
				"_score": 1.0,
				"_source": {
					"account_number": 44,
					"balance": 34487,
					"firstname": "Aurelia",
					"lastname": "Harding",
					"age": 37,
					"gender": "M",
					"address": "502 Baycliff Terrace",
					"employer": "Orbalix",
					"email": "[email protected]",
					"city": "Yardville",
					"state": "DE"
				}
			}
		]
	}
}

search exact value in hole type

Hi basemkhirat

i want to search in my type..
and wrote all my queries, and work fine.. here i have a challenge!
i want to search in hole type... any field that exist, i wrote a query like this :
`

"query" => [
      "bool" => [    
             "must" => [
                     "query_string" => [
                             "query" => $keyword_exact,
                              "boost" => 1
                         ]
                  ]
           ]      
   ] 

`

it works, but i want to get documents with the exact query...
for example: if i search sales... i want this exact word, not 'resalat' or 'Assaluyeh' or... because of having letters 'sal' .

this was my issue,
i hope it's clear for you
and thanks

pagination for huge number of records

hello,

first i want to thank you for your awesome work.

i'm trying to paginate one million+ records for to user. naturally i'm getting the "Result window is too large" error. is it possible to achieve this?

i read about the scroll function. but i don't know how to use the function with your library. can you help me about the subject?

thank you.

Data not filtered if the document containts "count" as field name

Say if my document consists of the following fields,
[ "id" => 0, "tag" => "some-tag", "count" => 0]

When i perform the following filter... the results are not filtered accordingly...
ES::index(self::INDEX)->type($type)->where('count', '>', 0)->get();

If i change the count field to some other name say "total_count" it works....

Is "count" a reserved keyword in elastic search (or) is it a bug?

properties and _all

Related to #20
Even now there is no possibility to control _all field, because type mapping is passed directly to properties. Because of this there is no possibility to tune _all field for a type (add analyzer, for example).

So config should be straight forward, like in elastic docs. This is a breaking change, however, so maybe should have some special key in config.. Or set it to 1.x release

error while using distance query

I am getting error while using distance query.

My mapping for distance is :

"pin": {
"properties": {
"location": {
"properties": {
"lat": {
"type": "double"
},
"lon": {
"type": "double"
}
}
}
}
}
Can you please tell me, what I am doing wrong into it.

Thank you in advance.

doc_as_upsert, etc.

Hi!
Really needed to be able to use updateOrCreate/createOrUpdate methotds.
As I see in the ES docs, it's posible.

Using 1.2 package without Laravel?

Hi, I just updated from 1.1 to 1.2 and now I get this error message while running a query.

Thing to note, is that I'm using your package in a Drupal 8 env, not Laravel.

Error: Call to undefined function Basemkhirat\Elasticsearch\config() in Basemkhirat\Elasticsearch\Model->getConnection() (line 89 of foo/bar/drupal/vendor/basemkhirat/elasticsearch/src/Model.php).

Of course, the global function config() does not exist in my env.

Is using Model required now? How can I set a default connection.

Thanks,
Jérémy

how to use orwhere in basemkhirat/elasticsearch package

hi thanks for your great package.
i have a collection and wanna to do multiple searches...
so i wrote where clauses on it.. but it works like and where that i don't want that response..
i want do orwhere on my collection.
can you give me a solution how to write that?

thanks

how to add mapping with scout driver

hi
in my case i am indexing with laravel scout..
and using this command
php artisan scout:import "App\Post"

question is, how can i add mapping during index?
i set configuration like all said in document, but it doesn't care about the configuration and just do the indexing...

is there any way to add mapping after index all my data?
i prefer use curl.. but i don't know how to add mapping for an existing index

thanks

what about aggregations

This repository looks really great. Is there also a solution for aggregation metrics?

regards
Ppeer

Can we pass the id instead of letting ES to create a id

In elasticsearch we can pass the id (ex: if the model is already in mysql we have a id and want to use same id in ES as well)

In your documentation I saw examples where you retrieve by id

$post = App\Post::find(1);

which means we should be able to create by giving id=1 as well.
How to do this?
When I check the code it seems to me that you're assuming passing _id field means it's a update? Correct me if I'm wrong

Thank you

Search Pattern

when i use
ES::type('listings')->search('ile')->get();
it show results
1.ile maurice
2. ile more ile
No other string having ile in it like mobile

When i use
ES::type('listings')->search('more')->get();
It show
1.more
but when i type
ES::type('listings')->search('maur')->get();
It is not showing ile maurice as the work has maurice
So basically it is searching to exact match of word.

I want the match to be substring and best match at first.
ES::type('listings')->search('ile')->get();
Should show

  1. ile maurice
  2. ile more ile
  3. mobile device
    and when i type
    ES::type('listings')->search('maur')->get();
    it should show maurice

Model return empty array

i am using latest lumen, and this my configuration based on doc.
es.php

<?php

return [

    'default' => env('ELASTIC_CONNECTION', 'default'),


    'connections' => [

        'default' => [

            'servers' => [

                [
                    "host" => env("ELASTIC_HOST", "127.0.0.1"),
                    "port" => env("ELASTIC_PORT", 9200),
                    'user' => env('ELASTIC_USER', ''),
                    'pass' => env('ELASTIC_PASS', ''),
                    'scheme' => env('ELASTIC_SCHEME', 'http'),
                ]

            ],

            'index' => env('ELASTIC_INDEX', 'products_1'),
        ]
    ],

    'indices' => [

        'products_1' => [

            "aliases" => [
                "products"
            ],

            'settings' => [
                "number_of_shards" => 1,
                "number_of_replicas" => 0,
            ],

            'mappings' => [
                'products' => [
                    "properties" => [
                        'title' => [
                            'type' => 'text'
                         ]
                    ]
                ]
            ]

        ]

    ]

];

i am change string to text because when i used string i got this error

{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"No handler for type [string] declared on field [title]"}],
  "type":"mapper_parsing_exception","reason":"No handler for type [string] declared on field [title]"},"status":400}

scout.php

'driver' => env('SCOUT_DRIVER', 'es'),
'es' => [
        'connection' => env('ELASTIC_CONNECTION', 'default'),
    ],

Products.php


<?php

namespace App;

use Basemkhirat\Elasticsearch\Model;

class Product extends Model {

    protected $index = "products";
    protected $type = "products";
}

Then i use

php artisan es:indices:create

I got

Creating index: products_1
Creating alias: products for index: products_1
Creating mapping for type: products in index: products_1

I check the index

+---------------------+--------+--------+------------+------------------------+-----+-----+------------+--------------+------------+----------------+
| configured (es.php) | health | status | index      | uuid                   | pri | rep | docs.count | docs.deleted | store.size |
 pri.store.size |
+---------------------+--------+--------+------------+------------------------+-----+-----+------------+--------------+------------+----------------+
| yes                 | green  | open   | products_1 | dh8IUmv5T4qsx7IoAVmeng | 1   | 0   | 0          | 0            | 233b       | 233b           |
+---------------------+--------+--------+------------+------------------------+-----+-----+------------+--------------+------------+----------------+

I using scout to my model

php artisan scout:import "App\Product"

i got

All [App\Product] records have been imported.

But when i tried to access via controller

 $products = \App\Product::all();
 return  $products;

i got

[]

On database i have at least 11 product, so why i got empty array instead of all my product ? is my configuration wrong ?

Type removal

Is there a way to remove a type from mappings?

history in elastic search

Is there is any way to get search history in elastic search? If yes, then can you please explain how we can implement it.

Thank you in adance.

multiple analyzers not working

When i try to update my index with:

php artisan es:indices:update

I get the following error

{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"analyzer [autocomplete_search] not found for field [firstname]"}],"type":"mapper_parsing_exception","reason":"analyzer [autocomplete_search] not found for field [firstname]"},"status":400}

here is my index configuration:

   'project-live => [

        'aliases' => [
            'project'
        ],

        'settings' => [
            'analysis' => [
                'filter' => [
                    'autocomplete_filter' => [
                        'type' => 'edge_ngram',
                        'min_gram' => 1,
                        'max_gram' => 20
                    ],
                    'portuguese_stop' => [
                        'type' => 'stop',
                        'stopwords' => '_portuguese_'
                    ],
                ],
                'analyzer' => [
                    'autocomplete' => [
                        'type' => 'custom',
                        'tokenizer' => 'standard',
                        'filter' => [
                            'autocomplete_filter',
                            'asciifolding',
                            'lowercase',
                            'portuguese_stop',
                        ],
                    ],
                    'autocomplete_search' => [
                        'type' => 'custom',
                        'tokenizer' => 'standard',
                        'filter' => [
                            'asciifolding',
                            'lowercase',
                            'portuguese_stop',
                        ],
                    ],
                ],
            ],
            'number_of_shards' => 1,
            'number_of_replicas' => 1
        ],

        'mappings' => [
            'doctors_index' => [
                'properties' => [
                    'firstname' => [
                        'type' => 'string',
                        'analyzer' => 'autocomplete',
                        'search_analyzer' => 'autocomplete_search'
                    ],
                    'lastname' => [
                        'type' => 'string',
                        'analyzer' => 'autocomplete',
                        'search_analyzer' => 'autocomplete_search'
                    ]
                ]
            ],
            'clinics_index' => [
                'properties' => [
                    'name' => [
                        'type' => 'string',
                        'analyzer' => 'autocomplete',
                        'search_analyzer' => 'autocomplete_search'
                    ]
                ]
            ],
            'examCategories_index' => [
                'properties' => [
                    'name' => [
                        'type' => 'string',
                        'analyzer' => 'autocomplete',
                        'search_analyzer' => 'autocomplete_search'
                    ]
                ]
            ],
            'specialties_index' => [
                'properties' => [
                    'name' => [
                        'type' => 'string',
                        'analyzer' => 'autocomplete',
                        'search_analyzer' => 'autocomplete_search'
                    ]
                ]
            ],
        ]
    ],

Accents

Hi !

I'm trying to search using your code trought scout, like stated in the docs. What I'm trying is to do a query that ignores accents, I mean:

  • I have a field "name" with "Málaga"
  • I have a field "name" with "Malaga" <- no accent

Now, when I search with accents, I can only get the accented words, if I search without accents, I can only get the non-accented words. I have used Elasticquent in the past, where I could:

    protected $indexSettings = [
        'analysis' => [
            'analyzer' => [
                'folding' => [
                    'tokenizer' => 'standard',
                    'filter' => [ 'standard', 'lowercase', 'asciifolding', 'spanish_stemmer', 'spanish_snowball' ],
                ],
            ],
            'filter' => [
                'spanish_stemmer' => [
                    "type" => "stemmer",
                    "name" => "spanish"
                ],
                'spanish_snowball' => [
                    "type" => "snowball",
                    "language" => "Spanish"
                ]
            ]
        ],
    ];
	protected $mappingProperties = [
		'titulo' => [ 'type' => 'string', 'analyzer' => 'folding' ],
		'texto' => [ 'type' => 'string', 'analyzer' => 'folding' ],
	];

Using that, I could search in the "Titulo" and "Texto" fields, and both "Málaga" and "Malaga" would appear for a search "malaga" or "málaga". However, that's old code and I'm not even sure that would work with current Elasticsearch... so.. any way I can set the analyzer in ES so I can define "ignore accents"?

Missing configuration for index: my_index

Hi @basemkhirat
when i want update my index with new alias... it show me this error : Missing configuration for index: my_index

why its happened?
I want to create a mapping for my index..
and update my 'config/es.php' file.. i don't know where is wrong!

this is my es.php file:
`
'connections' => [

    'default' => [

        'servers' => [

            [
                "host" => env("ELASTICSEARCH_HOST", "127.0.0.1"),
                "port" => env("ELASTIC_PORT", 9200),
                'user' => env('ELASTIC_USER', ''),
                'pass' => env('ELASTIC_PASS', ''),
                'scheme' => env('ELASTIC_SCHEME', 'http'),
            ]

        ],

        'index' => env('ELASTICSEARCH_INDEX', ''),

        'type' => env('ELASTIC_TYPE', '')

    ]
],

'indices' => [

    'irantalent_map_1' => [

        "aliases" => [
            "irantalent_alias"
        ],

        'settings' => [
            "number_of_shards" => 1,
            "number_of_replicas" => 0,
        ],

        'mappings' => [
            'employer_positions' => [
                'properties' => [
                    'expired_at' => [
                        'type' => 'date',
                        "null_value" => "NULL"
                    ]
                ]
            ]
        ]

    ]

]`

[QUESTION] Search query in many fields

How do I search a sentence in many fields?
i.e.:
My model have name and description fields and I want to search this text in both (or all fields of my model)

Is that possible without create many most bool filters?

malformed query with body and where

$query = ES::index($indexName)
            ->type($type)
            ->body([
                "query" => [
                    "match" => [
                        '_all' => [
                            'query' => $term,
                            'fuzziness' => 3
                        ]
                    ],
                ]
            ]);

// $query->get(); will work properly now
// but if add where:
 $query->where('status', 'foo')->get();
{"error":{"root_cause":[{"type":"parsing_exception","reason":"[match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]","line":1,"col":73}],"type":"parsing_exception","reason":"[match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]","line":1,"col":73},"status":400}

The object looks fine:
2017-04-27 22 22 34

appending into the query object

Hi..

is there any way to append raw query into the query object body ??
i need to add should clause which have a bool and must_not clause in it .. i need to append it into the query body

Implement ArrayAccess

I have noticed that SomeModel::all()->pluck('id'); does not work because the data_get() helper function in Illuminate\Support\Arr always returns null. I have fixed this by extending the Model class and adding the ArrayAccess interface and methods.

Would be nice it the ArrayAccess interface is included in the default Model class.

http://php.net/manual/de/class.arrayaccess.php

Problem while trying to create index

Hi basemkhirat

I am getting an error while try to create index, "No alive nodes found in your cluster ". I am using amazon elastic search. And I am able to create index and mapping using kibana.

Thank you in advance.

1.3 doesnot work with lumen/laravel 5.4 anymore

When we install version 1.3 via composer update our tests do not run anymore, if we downgrade to 1.2 everything works again.

Errors:

phpunit
PHP Fatal error: Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Notifications\Dispatcher] is not instantiable. in /[...]/vendor/illuminate/container/Container.php:895
Stack trace:
#0 /[...]/vendor/illuminate/container/Container.php(735): Illuminate\Container\Container->notInstantiable('Illuminate\Cont...')
#1 /[...]/vendor/illuminate/container/Container.php(608): Illuminate\Container\Container->build('Illuminate\Cont...')
#2 /[...]/vendor/illuminate/container/Container.php(575): Illuminate\Container\Container->resolve('Illuminate\Cont...')
#3 /[...]/vendor/laravel/lumen-framework/src/Application.php(210): Illuminate\Container\Container->make('Illuminate\Cont...')
#4 /[...]/vendor/laravel/lumen-framework/src/helpers.php(38): Laravel\Lumen\Application->make in /[...]/vendor/illuminate/container/Container.php on line 895`

Failed Performing Insert Without Id ( Generated Hash Id )

Code

  $sample = json_decode(file_get_contents($app->basePath().'/tests/json/mydata.json')); # Sample of my json doccument
  $app['es']->index('sample')->type('sample')->insert($sample);

Trace

  [2017-02-27 10:42:05] lumen.ERROR: Elasticsearch\Common\Exceptions\UnexpectedValueException: "id" is not a valid parameter. Allowed parameters are "client", "consistency", "custom", "filter_path", "human", "op_type", "parent", "percolate", "pipeline", "refresh", "replication", "routing", "timeout", "timestamp", "ttl", "version", "version_type" in /home/project/myapp/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/AbstractEndpoint.php:221
  Stack trace:
  #0 /home/project/myapp/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Endpoints/AbstractEndpoint.php(74): Elasticsearch\Endpoints\AbstractEndpoint->checkUserParams(Array)
  #1 /home/project/myapp/vendor/elasticsearch/elasticsearch/src/Elasticsearch/Client.php(751): Elasticsearch\Endpoints\AbstractEndpoint->setParams(Array)
  #2 /home/project/myapp/vendor/basemkhirat/elasticsearch/src/Query.php(1005): Elasticsearch\Client->index(Array)
  #3 /home/project/myapp/routes/web.php(25): Basemkhirat\Elasticsearch\Query->insert(Object(stdClass))
  #4 [internal function]: Closure->{closure}()
...

Composer.lock

  "name": "basemkhirat/elasticsearch",
            "version": "0.9.1",
            "source": {
                "type": "git",
                "url": "https://github.com/basemkhirat/elasticsearch.git",
                "reference": "b8baccf8341c3719a11935a9c84388f697cb2819"
            },
  ..............
  "name": "elasticsearch/elasticsearch",
            "version": "v5.1.3",
            "source": {
                "type": "git",
                "url": "https://github.com/elastic/elasticsearch-php.git",
                "reference": "ee931ebb6f371dc31eecbd9ec2ab2cc563823453"
            },
  ..............
    "name": "laravel/lumen-framework",
            "version": "v5.4.4",
            "source": {
                "type": "git",
                "url": "https://github.com/laravel/lumen-framework.git",
                "reference": "e88e7ac6fa22bdedb0fc0f55e6abed0d8a6c4ca2"
            },

My Workaround
src/Query.php:1004

       if (is_null($parameters['id'])) {
            unset($parameters['id']);
        }

how to get a number of my search result

hi @basemkhirat
I wrote my own search query, and it works great.. thanks
the issue that I have here is; I want to speed up my search, and one of things that I want to do is, to filter 500 result after sorting, I mean just return me 500 relevance data.. and filter the others

Thanks

how to add a new index

hi @basemkhirat
I index my data using scout and toSearchableArray() method
in the present, I have an Index.. and with toSearchableArray() I had indexed my models to two different types.. but because of a low speed during for my search result. I decided to create another index and index one of my types to the new index..
I looked in scout config and es config.. but I couldn't find any setting to define multiple index.. can you tell me how to do that?

thanks

Highlight section in response

How i can get highlight section in response?
ex.

$json = '{
    "query" : {
            "match": { "name": "hello" }
    },
    "highlight" : {
        "fields" : {
            "name" : {}
         }
    }
}';
$result = json_decode($json);
$search = ES::type('posts')->body($result)->get();

How to save each Model to it's own index with Scout?

Hello @basemkhirat ,

Thank you for your work! I am trying to learn how the plugin works and hopefully you can help me with this.

How could a Model be saved in it's own index using this Plugin as Scout driver?

The way we use Elasticsearch is that each model (Posts, Comments, etc.) is stored in its own index. We believe this is how it's meant to be used. And the creators of Elasticsearch are also dropping types in the future: https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html

[Laravel] How to use _all mapping?

It seems that mappings from config are passed directly to properties of Elastic mapper. But how to set _all map? that is on higher level of map document?

Raw queries, "match_all" error

hello,

i'm trying to execute:

$col = ES::raw()->search([
            "index" => "my_index",
            "type"  => "my_type",
            "body" => [
                "query" => [
                    "bool" => [
                        "must" => [
                            "match_all" => []
                        ]
                    ]
                ]
            ]
        ]);

and it throws an error:

parsing_exception: [match_all] query malformed, no start_object after query name

match is working fine. am i doing something wrong with the match_all?

by the way the raw query example on the main page still contains : signs. i think you forgot to change them with =>.

thank you.

Adding Common options

Hi

Could you provide a way to adding query string like Common options ?

Something like this :
In Basemkhirat\Elasticsearch\ScoutEngine::performSearch()

$params = [
            'index' => $this->index,
            'type' => $builder->model->searchableAs(),
            'body' => [
                'query' => [
                    'bool' => [
                        'must' => [['query_string' => [ 'query' => $builder->query]]]
                    ]
                ]
            ],
            "some_key_from_options" => "some_value_from_options"
        ];

merge options and params or another binding from build maybe helpful, something like

$builder->model->setParams()

that return a array that check with checkUserParams() in ES

Thanks

How to use the ES:: ?

This part of code
$documents = $connection->search("john")->get();

Is very clear and working.

When I try to do this code it fails:

$documents = $connection->ES::type("my_type")->_id(3)->first();

Can you please upload one example that works including all required USE.

Thank you

how to get inner_hits result?

hi @basemkhirat

i used nested type in my_document.. and when i run my query the result that returns to me show the hole document.. while i want to show me just inner_hits result..

i think in your package you return hits result..
if i want to get inner_hits what can i do?

thanks

Missing $_SERVER variables in tests

When running functional tests, the $_SERVER variables are not present, which causes the library to throw errors and makes it untestable. Please use the $app->request->server instead of $_SERVER.

How can I set a parent / child relationship

I'm trying to create two documents, one build a child of the other. My mapping config is like this

'mappings' => [ "roadType" => [ "properties" => [ ] ], "carType" => [ "_parent" => [ "type" => "roadType" ], "properties" => [ "make" => [ "type" => "string" ], "model" => [ "type" => "string" ] ] ] ]

I got this error message .

{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"No handler for type [roadType] declared on fie ld [_parent]"}],"type":"mapper_parsing_exception","reason":"No handler for type [roadType] declared on field [_pare nt]"},"status":400}

Does anyone have any idea. Thank you

how to search between dates

In elastic search docs for search between dates.

POST crawler/ads/_search
{
    "query": {
        "range" : {
            "created_at" : {
                "gte": "23/05/2017",
                "lte": "25/05/2017",
                "format": "dd/MM/yyyy||yyyy"
            }
        }
    }
}

How to search using this package in Laravel ?

Batch Delete

Hi,

I would like to batch delete ES documents by ID. Any idea how to accomplish this task by using your library?

Thanks

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.