Coder Social home page Coder Social logo

meilisearch / meilisearch-php Goto Github PK

View Code? Open in Web Editor NEW
570.0 14.0 95.0 1.25 MB

PHP wrapper for the Meilisearch API

Home Page: https://meilisearch.com

License: MIT License

PHP 99.79% Shell 0.05% Dockerfile 0.06% Twig 0.11%
meilisearch sdk client php

meilisearch-php's Introduction

Meilisearch-PHP

Meilisearch PHP

Codecov coverage Latest Stable Version Test License Bors enabled

โšก The Meilisearch API client written for PHP ๐Ÿ˜

Meilisearch PHP is the Meilisearch API client for PHP developers.

Meilisearch is an open-source search engine. Learn more about Meilisearch.

Table of Contents

๐Ÿ“– Documentation

To learn more about Meilisearch PHP, refer to the in-depth Meilisearch PHP Documentation. To learn more about Meilisearch in general, refer to our documentation or our API reference.

โšก Supercharge your Meilisearch experience

Say goodbye to server deployment and manual updates with Meilisearch Cloud. Get started with a 14-day free trial! No credit card required.

๐Ÿ”ง Installation

To get started, simply require the project using Composer.
You will also need to install packages that "provide" psr/http-client-implementation and psr/http-factory-implementation.
A list with compatible HTTP clients and client adapters can be found at php-http.org.

If you don't know which HTTP client to use, we recommend using Guzzle 7:

composer require meilisearch/meilisearch-php guzzlehttp/guzzle http-interop/http-factory-guzzle:^1.0

Here is an example of installation with the symfony/http-client:

composer require meilisearch/meilisearch-php symfony/http-client nyholm/psr7:^1.0

๐Ÿ’ก More HTTP client installations compatible with this package can be found in this section.

Run Meilisearch

There are many easy ways to download and run a Meilisearch instance.

For example, using the curl command in your Terminal:

#Install Meilisearch
curl -L https://install.meilisearch.com | sh

# Launch Meilisearch
./meilisearch --master-key=masterKey

NB: you can also download Meilisearch from Homebrew or APT or even run it using Docker.

๐Ÿš€ Getting started

Add documents

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Meilisearch\Client;

$client = new Client('http://127.0.0.1:7700', 'masterKey');

# An index is where the documents are stored.
$index = $client->index('movies');

$documents = [
    ['id' => 1,  'title' => 'Carol', 'genres' => ['Romance, Drama']],
    ['id' => 2,  'title' => 'Wonder Woman', 'genres' => ['Action, Adventure']],
    ['id' => 3,  'title' => 'Life of Pi', 'genres' => ['Adventure, Drama']],
    ['id' => 4,  'title' => 'Mad Max: Fury Road', 'genres' => ['Adventure, Science Fiction']],
    ['id' => 5,  'title' => 'Moana', 'genres' => ['Fantasy, Action']],
    ['id' => 6,  'title' => 'Philadelphia', 'genres' => ['Drama']],
];

# If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
$index->addDocuments($documents); // => { "uid": 0 }

With the uid, you can check the status (enqueued, canceled, processing, succeeded or failed) of your documents addition using the task.

Basic Search

// Meilisearch is typo-tolerant:
$hits = $index->search('wondre woman')->getHits();
print_r($hits);

Output:

Array
(
    [0] => Array
        (
            [id] => 2
            [title] => Wonder Woman
            [genres] => Array
                (
                     [0] => Action, Adventure
                )
        )
)

Custom Search

All the supported options are described in the search parameters section of the documentation.

๐Ÿ’ก More about the search() method in the Wiki.

$index->search(
    'phil',
    [
        'attributesToHighlight' => ['*'],
    ]
)->getRaw(); // Return in Array format

JSON output:

{
    "hits": [
        {
            "id": 6,
            "title": "Philadelphia",
            "genre": ["Drama"],
            "_formatted": {
                "id": 6,
                "title": "<em>Phil</em>adelphia",
                "genre": ["Drama"]
            }
        }
    ],
    "offset": 0,
    "limit": 20,
    "processingTimeMs": 0,
    "query": "phil"
}

Custom Search With Filters

If you want to enable filtering, you must add your attributes to the filterableAttributes index setting.

$index->updateFilterableAttributes([
  'id',
  'genres'
]);

You only need to perform this operation once.

Note that Meilisearch will rebuild your index whenever you update filterableAttributes. Depending on the size of your dataset, this might take time. You can track the process using the tasks).

Then, you can perform the search:

$index->search(
  'wonder',
  [
    'filter' => ['id > 1 AND genres = Action']
  ]
);
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action","Adventure"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 0,
  "query": "wonder"
}

๐Ÿค– Compatibility with Meilisearch

This package guarantees compatibility with version v1.x of Meilisearch, but some features may not be present. Please check the issues for more info.

๐Ÿ’ก Learn more

The following sections in our main documentation website may interest you:

๐Ÿงฐ HTTP Client Compatibilities

You could use any PSR-18 compatible client to use with this SDK. No additional configurations are required.
A list of compatible HTTP clients and client adapters can be found at php-http.org.

If you want to use this meilisearch-php:

  • with guzzlehttp/guzzle (Guzzle 7), run:
composer require meilisearch/meilisearch-php guzzlehttp/guzzle http-interop/http-factory-guzzle:^1.0
  • with php-http/guzzle6-adapter (Guzzle < 7), run:
composer require meilisearch/meilisearch-php php-http/guzzle6-adapter:^2.0 http-interop/http-factory-guzzle:^1.0
  • with symfony/http-client, run:
composer require meilisearch/meilisearch-php symfony/http-client nyholm/psr7:^1.0
  • with php-http/curl-client, run:
composer require meilisearch/meilisearch-php php-http/curl-client nyholm/psr7:^1.0
  • with kriswallsmith/buzz, run:
composer require meilisearch/meilisearch-php kriswallsmith/buzz nyholm/psr7:^1.0

Customize your HTTP Client

For some reason, you might want to pass a custom configuration to your own HTTP client.
Make sure you have a PSR-18 compatible client when you initialize the Meilisearch client.

Following the example in the Getting started section, with the Guzzle HTTP client:

new Client('http://127.0.0.1:7700', 'masterKey', new GuzzleHttpClient(['timeout' => 2]));

โš™๏ธ Contributing

Any new contribution is more than welcome in this project!

If you want to know more about the development workflow or want to contribute, please visit our contributing guidelines for detailed instructions!


Meilisearch provides and maintains many SDKs and Integration tools like this one. We want to provide everyone with an amazing search experience for any kind of project. If you want to contribute, make suggestions, or just know what's going on right now, visit us in the integration-guides repository.

meilisearch-php's People

Contributors

94noni avatar abihafatima avatar aivchen avatar alallema avatar bidoubiwa avatar bofalke avatar bors[bot] avatar brunoocasali avatar claudiunicolaa avatar curquiza avatar dependabot[bot] avatar eskombro avatar girijakar avatar jonatanrdsantos avatar karlomikus avatar kjellknapen avatar meili-bors[bot] avatar meili-bot avatar mmachatschek avatar nextra avatar norkunas avatar ppshobi avatar qdequele avatar shokme avatar srichter avatar stloyd avatar strift avatar szainmehdi avatar tgalopin avatar the-sinner 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

meilisearch-php's Issues

Recommend using the latest version of guzzle

Since this PR #88, guzzle 7 is compatible with our package, we should use guzzle 7 instead of guzzle 6:

  • in our test suite by default
  • in the Installation section

And we should let an example with guzzle 6 in the HTTP Client Compatibilities section.

updateFilterableAttributes

I'm trying to call updateFilterableAttributes in my code... and it seems to exist in this repository in the tests but I'm unable to figure it out?

The docs say you can do this but I can't call that method myself.

$client->index('movies')->updateFilterableAttributes([
  'genres',
  'director'
]);

Error in code sample for updateSynonyms

Description
There is an error in the code sample for updateSynonyms

  $client->index('movies')->updateSynonyms([
    'wolverine': ['xmen', 'logan'],
    'logan': ['wolverine', 'xmen'],
    'wow': ['world of warcraft']
  ]);

Should be:

  $client->index('movies')->updateSynonyms([
    'wolverine' => ['xmen', 'logan'],
    'logan' => ['wolverine', 'xmen'],
    'wow' => ['world of warcraft']
  ]);

Hello everyone!

I recently found the Laravel Scout package as well as Meilisearch and I want to test it for my little private project at university.
My problem is maybe totally easy to solve but right now I am stuck for two days.

So what's the Problem:
The data is stored in the database and I imported it successfully to the meilisearch web interface. Now I want to create synonyms for the project, but it isnยดt obviously working for me. I tried to get the synoynms in with curl like that:

curl -X POST http://localhost:7700/indexes/teams/settings/synonyms --data '{"Paris Saint-Germain": ["PSG"]}'

with that Iยดll get the message: "Invalid JSON:expected value at line 1 column1..." and so on. Every other curl command (for example to see the synonyms) is working.
After that I tried to solve the problem with PHP logic like in the docs. So I created a file named "meilisearch_testsettings.php" with the following content:

<?php

require_once 'vendor/autoload.php';

use MeiliSearch\Client;

$client = new Client("http://localhost:7700");
$index = $client->getIndex('teams');

$a = $client->index('teams')->getSynonyms();

$b = $client->index('teams')->updateSynonyms([
    'Paris Saint-Germain': ['PSG', 'psg']
  ]);
 
print_r($b);

If I want to try the .php file with "php meilisearch_testsettings.php", Iยดll get the error, that there is a PHP Parse error: syntax error, unexpected token ":", expecting "]". Maybe I am pretty dumb and I did something totally wrong and I donยดt see it but I donยดt get my mistake.

Thanks in advance!

Originally posted by @ballingm in meilisearch/meilisearch#1482

Apply declare_strict

Apply declare_strict in PHP CS, following this discussion.

The problem is that currently, passing an int to getDocument makes it failed, but it should not since MS can accept int and string as document id.

@devrck feel free to complete this issue if you think it's not clear enough ๐Ÿ™‚

Everyone is welcome to give their opinion about the subject here!

Unable to `updateSettings` for an index

Description
I want to update and index settings using the script below

...
$settings = $index->getSettings();
$index->updateSettings($settings);

Expected behavior
The settings would be successfully updated

Current behavior
An exception is thrown

MeiliSearch\Exceptions\ApiException with message 'Json deserialize error: invalid type: sequence, expected a map at line 1 column 190'

If I use updateSearchableAttributes or updateFilterableAttributes it works as expected

Environment (please complete the following information):

  • OS: [e.g. Mac OSX]
  • MeiliSearch version: [e.g. v.0.21.1]
  • meilisearch-php version: [e.g v0.19.0]

Implementation of a Json Serializer

Description
As a result of this issue, it appears that json_decode transforms

"synonyms": {}

into

["synonyms"]=>
  array(0) {
}

In the same way as:

"stopWords": []

become:

["stopWords"]=>
  array(0) {
}

The problem appears when you send back the synonyms array to the MeiliSearch API who only take:

"synonyms": {},

or

"synonyms": null,

It could be useful to implement our own specific JsonSerializer to check if every data send back to MeiliSearch API are well-formatted.

In the meanwhile a patch will be created see.

Delete index method should return array

In the endpoint class Indexes, the method delete() seems to return void:

$this->http->delete(self::PATH.'/'.$this->uid);

However, the HTTP Client method delete seems to return the value of the executed query:

return $this->execute($request);

Related to Symfony bundle too, the bundle will probably return an error when trying to delete an entry, because is seems to return an array in the next line:
https://github.com/meilisearch/meilisearch-symfony/blob/3714b3cbb1a5c9a7e8a57b289e197f548b532cc7/src/Engine.php#L140

So we need to decide if we return void from the beginning to the end or an array.
From my perspective, I would prefer to return an array with the response if the index has been deleted correctly or not.

Support PHP 8

  • meilisearch/meilisearch-php v0.12.0 requires php ^7.2 -> your php version (8.0.0beta1) does not satisfy that requirement.

[Search] FacedFilters documentation

Hi everyone ๐Ÿ‘‹

While working on a separate Symfony bundle that wrap this SDK, I'm facing a strange behaviour/situation, I'm trying to build a Search class which allows to build complex queries and ease the search process.

The issue is when I'm trying to build facetFilters, I've looked at the internals of search and it seems that the request wait for a specific array:

// Index::parseOptions()

    private function parseOptions(array $options)
    {
        foreach ($options as $key => $value) {
            if ('facetsDistribution' === $key || 'facetFilters' === $key) {
                $options[$key] = json_encode($value);
            } elseif (is_array($value)) {
                $options[$key] = implode(',', $value);
            }
        }

        return $options;
    }

The issue here is that the documentation of this SDK does not explain this type of search and/or the type of body required to be encoded, is there any internal documentation about it? ๐Ÿค”

Thanks for the feedback and have a great day ๐Ÿ™‚

Placeholder Search

According to the Documentation it is possible to ommit the query parameter.

Looking at Indexes.php, it seems there's no way to run a search without the query parameter currently.

There should be a way to run a "search" without the query parameter, but with filters for example.

Branching and release strategy

Hi, as of now, we are directly merging commits on to the master branch, which at this point I assume is fine. But I would like to propose a better model, where the contributors check out from master to either

  • a feature/feature-* branch
  • a hotfix/hotfix-* branch

And should create a PR against the develop branch instead of master. hotfix/hotfix-* branches may be issued against the master branch. once we have enough features/changes in develop branch the maintainer could merge develop on to master (maybe cleanup the commit history by squashing nasty commits during the process) and issue a release by changing the version number. Security patches maybe directly make it to the master directly.

so the master branch stays stable, developers will have a clean slate to start with which in turn will help in reducing the conflicts as well. Also, we have a streamlined releasing process and each version has something meaningful in it.

If we add this in the Readme.md other devs could easily follow this as well.

Improve the search() method

Hi everyone ๐Ÿ‘‹

Small issue here (more a feature idea in fact), after using the search() method recently, I've faced a use case where a SearchResult object could be a good idea: sorting hits.

Actually, we can easily sort hits via native functions but it force use to grab the hits key, sort and put back it on the result array, it works but thinking on terms of DX, that's probably not an "easy" way of doing it.

So, the idea is to add a new class and update the search method:

    public function search($query, array $options = [], bool $asObject = false) // The return typehint cannot be specified until PHP 8
    {
        $parameters = array_merge(
            ['q' => $query],
            $options
        );

        $result = $this->http->post(self::PATH.'/'.$this->uid.'/search', $parameters);

        return $asObject ? SearchResult::create($result) : $result;
    }

Here's the SearchResult that I'm thinking about:

<?php

declare(strict_types=1);

namespace MeiliSearch;

use ArrayIterator;
use function array_filter;
use function array_key_exists;
use function count;
use function end;
use function is_array;

final class SearchResult
{
    /**
     * @var array<int, array>
     */
    private $hits = [];

    /**
     * @var int
     */
    private $offset;

    /**
     * @var int
     */
    private $limit;

    /**
     * @var int
     */
    private $nbHits;

    /**
     * @var bool
     */
    private $exhaustiveNbHits = false;

    /**
     * @var int
     */
    private $processingTimeMs;

    /**
     * @var string
     */
    private $query;

    /**
     * @var bool|null
     */
    private $exhaustiveFacetsCount;

    /**
     * @var array<string, mixed>
     */
    private $facetsDistribution;

    public static function create(array $result): SearchResult
    {
        $self = new self();

        // Build the object using the array

        return $self;
    }

    /**
     * {@inheritdoc}
     */
    public function filter(callable $callback): SearchResult
    {
        $results = array_filter($this->hits, $callback, ARRAY_FILTER_USE_BOTH);

        $this->hits = $results;
        $this->nbHits = count($results);

        return $this;
    }

    public function getHit(int $key, $default = null)
    {
        return $this->hits[$key] ?? $default;
    }

    /**
     * @return array<int, array>
     */
    public function getHits(): array
    {
        return $this->hits;
    }

    public function getOffset(): int
    {
        return $this->offset;
    }

    public function getLimit(): int
    {
        return $this->limit;
    }

    public function getNbHits(): int
    {
        return $this->nbHits;
    }

    public function getExhaustiveNbHits(): bool
    {
        return $this->exhaustiveNbHits;
    }

    public function getProcessingTimeMs(): int
    {
        return $this->processingTimeMs;
    }

    public function getQuery(): string
    {
        return $this->query;
    }

    public function getExhaustiveFacetsCount(): ?bool
    {
        return $this->exhaustiveFacetsCount;
    }

    /**
     * @return array<string, mixed>
     */
    public function getFacetsDistribution(): array
    {
        return $this->facetsDistribution;
    }

    /**
     * {@inheritdoc}
     */
    public function toArray(): array
    {
        return [
            'hits' => $this->hits,
            'offset' => $this->offset,
            'limit' => $this->limit,
            'nbHits' => $this->nbHits,
            'exhaustiveNbHits' => $this->exhaustiveNbHits,
            'processingTimeMs' => $this->processingTimeMs,
            'query' => $this->query,
            'exhaustiveFacetsCount' => $this->exhaustiveFacetsCount,
            'facetsDistribution' => $this->facetsDistribution,
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function getIterator(): ArrayIterator
    {
        return new ArrayIterator($this->hits);
    }

    /**
     * {@inheritdoc}
     */
    public function count(): int
    {
        return $this->nbHits;
    }
}

The interesting method here is:

    /**
     * {@inheritdoc}
     */
    public function filter(callable $callback): SearchResult
    {
        $results = array_filter($this->hits, $callback, ARRAY_FILTER_USE_BOTH);

        $this->hits = $results;
        $this->nbHits = count($results);

        return $this;
    }

This one allows to filter the hits and return a filtered result without triggering a new search (a method retry() could trigger a new search if needed).

Of course, this class is not required as the developer can always retrieve an array but it could act as an helper when advanced use cases occurs and we need to mutate the result before playing with it.

Let me know if this improvements is a good idea and if it fits the current lib API, thanks for the feedback and have a great day ๐Ÿ™‚

PS: Maybe the current API can be mutated to add a third argument in search (typed callable) that allows us to filter the result before retrieving it and without using an object if a new class is not an option.

getAllIndexes() not returning expected array

Hi,

I'm pretty sure this is a bug, just wanted clarification.

I am using this package in Laravel without Laravel Scout and using the MeiliSearch\Client class to 'getAllIndexes()' however what is returned is an arrayu with 2 private properties, 'uid' & 'primaryKey' as opposed to the list of Indexes I was expecting.

Am I doing something wrong here?

Can't set updateAttributesForFaceting

Description
Iโ€™m running into issues configuring facets on my indices. The docs say I can set my filter attributes with updateAttributesForFaceting() but it doesn't appear to be working

Expected behavior
I expect to be able to set filterable attributes via updateAttributesForFaceting()

Current behavior
What happened.

Screenshots
image
image

Environment:

  • OS: Mac 10.15.7
  • MeiliSearch version: v0.21.0rc0--7700
  • meilisearch-php version: 0.18.3

Got the issue with Invalid URL: scheme is missing

I have installed the newest MeiliSearch with MAC, Valet and Laravel 8 with Scout. And I got exception when run meilisearch function:

Symfony\Component\HttpClient\Psr18RequestException

Invalid URL: scheme is missing in "1234567890/indexes/xxxxxx/documents". Did you forget to add "http(s)://"?

Please help how can I resolve this issue?
I guest it can be issue with http on localhost, but have no idea to make it works. I can work with http on local with meilisearch v0.20.

Environment:

  • OS: MAC Big Sur
  • PHP v8.3
  • MeiliSearch version: v.0.21.1
  • meilisearch-php version: v0.19.1
  • Laravel ^8.14
  • Laravel Scout ^9.2

Installation error on updating from vs 0.10 to 0.11 or 0.12 using composer

Good morning!

I'm trying to update the lib to the new versions but I'm having a problem while trying to do so. Aparently there is some dependency conflic between your dependencies. I don't use either php-http/guzzle6-adapter nor php-http/httplug. It looks like your library requires guzzle6-adapter in the version ^2.0 and also the this library requires php-http/httplug. It has some conflic doing so. I don't use any of those libraries in my composer file.

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for meilisearch/meilisearch-php ^0.12.0 -> satisfiable by meilisearch/meilisearch-php[v0.12.0].
    - Conclusion: remove php-http/httplug v1.1.0
    - Conclusion: don't install php-http/httplug v1.1.0
    - meilisearch/meilisearch-php v0.12.0 requires php-http/guzzle6-adapter ^2.0 -> satisfiable by php-http/guzzle6-adapter[v2.0.0, v2.0.1].
    - php-http/guzzle6-adapter v2.0.0 requires php-http/httplug ^2.0 -> satisfiable by php-http/httplug[2.1.0, v2.0.0].
    - php-http/guzzle6-adapter v2.0.1 requires php-http/httplug ^2.0 -> satisfiable by php-http/httplug[2.1.0, v2.0.0].
    - Can only install one of: php-http/httplug[2.1.0, v1.1.0].
    - Can only install one of: php-http/httplug[v2.0.0, v1.1.0].
    - Installation request for php-http/httplug (locked at v1.1.0) -> satisfiable by php-http/httplug[v1.1.0].

Have you any idea of how to solve this problem?

I've already tried to remove vendor and package.lock and reinstall everything again but the problem keeps happening.

This is my composer.json file dependencies:

"config": {
		"platform": {
			"php": "7.2"
		},
		"process-timeout":10000
	},
	"require": {
		"lusitanian/oauth": "~0.8.9",
		"ircmaxell/password-compat": "~1.0.4",
		"kriswallsmith/assetic": "~1.3.2",
		"patchwork/jsqueeze": "~2.0.3",
		"onelogin/php-saml": "3.1.1",
		"phpoffice/phpword": "^0.17.0",
		"dompdf/dompdf": "~0.6.2",
		"ezyang/htmlpurifier": "~4.7.0",
		"mailgun/mailgun-php": "^2.1",
		"jakezatecky/array_group_by": "~1.0.0",
		"vlucas/phpdotenv": "^2.2",
		"gregwar/image": "^2.0",
		"bjeavons/zxcvbn-php": "^0.1.4",
		"greenlion/php-sql-parser": "^4.3.0",
		"marcj/topsort": "1.0.0",
		"websharks/js-minifier": "150511",
		"ramsey/uuid": "^2.9",
		"symfony/expression-language": "^2.8",
		"nodejs-php-fallback/uglify": "^1.0",
		"predis/predis": "^1.1",
		"leafo/scssphp": "^0.4.0",
		"league/flysystem": "^1.0.35",
		"aws/aws-sdk-php": "^3.74",
		"league/flysystem-aws-s3-v3": "^1.0",
		"illuminate/support": "5.2.*",
		"illuminate/database": "5.2.*",
		"illuminate/container": "5.2.*",
		"illuminate/pipeline": "5.2.*",
		"filp/whoops": "^2.1",
		"guzzlehttp/guzzle": "^6.2",
		"guzzlehttp/psr7": "^1.3",
		"daniel-zahariev/php-aws-ses": "^0.8.8",
		"illuminate/redis": "5.2.*",
		"monolog/monolog": "^1.22",
		"maxbanton/cwh": "^0.3.0",
		"league/flysystem-ziparchive": "^1.0",
		"kettle/dynamodb-orm": "^0.2.8",
		"league/csv": "^8.2",
		"doctrine/dbal": "2.9.3",
		"google/cloud-vision": "^0.6.0",
		"ddeboer/transcoder": "^1.0",
		"neitanod/forceutf8": "^2.0.4",
		"box/spout": "^2.7",
		"php-ds/php-ds": "^1.2",
		"firebase/php-jwt": "^4.0.0",
		"thenetworg/oauth2-azure": "^1.3",
		"sonata-project/google-authenticator": "^2.0",
		"fzaninotto/faker": "^1.6",
		"mockery/mockery": "^0.9.7",
		"friendsofphp/php-cs-fixer": "^2.3",
		"phpmd/phpmd" : "^2.6.0",
		"mobiledetect/mobiledetectlib": "^2.8",
		"symfony/lock": "^4.2",
		"justinrainbow/json-schema": "^5.2",
		"symfony/http-foundation": "^4.3",
		"CBIConsulting/laravel-cte": "1.2.122",
		"nilportugues/sql-query-formatter": "^1.2",
		"eluceo/ical": "^0.16.0",
		"spatie/calendar-links": "^1.2",
		"meilisearch/meilisearch-php": "0.10.1"
	},

Thanks for your time. I will keep using the versiรณn 0.10.1 instead and dowgrading my meilisearch server while we can not solve this problem.

We're in a development phase, adding meilisearch to our project so I think it'll be the time to upgrade the lib and server version.

Double forward slash on requests.

Just curious if anyone is experiencing routes being generate with a double forward slash on requests (ending up within incoming requests to the Meilisearch server like POST //indexes//documents?primaryKey=id (taken from the console), it's resulting in 404's not found with the server, when built with the binary.
I'm not really sure where to go from here. When I generate a HTTP request via a rest client it all works fine. Anyone else have any issues?

Canโ€™t remember the version Iโ€™m running. Donโ€™t have it at hand.

Traits break contract for not reducing any complexity

The ways traits are used in this lib feel a bit strange. Take the

  • HandlesDocuments trait or
  • HandlesDumps trait
  • ... and many more...

Both are only used once. Further traits access properies

  • HandlesDocuments accesses http
  • HandlesDumps accesses dumps

that are not declared inside the trait but in the class they are used in. Even with the @property notation added inside the traits, it is not clear to which property you are refering. Using the @property notation is meant for magic methods.

Why using a trait for methods that are only used in one place? Directly implementing would have the same effect and would provide a cleaner style.

I was coming across that when I wanted to increase PHPstans level to 2. With the current implementation this seems not possible.

Let me know if the methods implemented in traits can be moved to the classes itself and/or how PHPstan can correctly determine the traits properties that are not defined inside the trait itself.

grafik

Http requests use json_encode which can fail and cause a TypeError.

Description
If you send content to be updated or inserted with the Client, it uses json_encode before sending the request. If the content is not UTF-8 encoded... in my case I believe it was from using a non-english language, then parsing the content to json can fail. Take a look at the post method for example:

    public function post($path, $body = null, $query = [])
    {
        $request = $this->requestFactory->createRequest(
            'POST',
            $this->baseUrl.$path.$this->buildQueryString($query)
        )->withBody($this->streamFactory->createStream(json_encode($body)));

        return $this->execute($request);
    }

The POST, PUT, and PATCH methods all use createStream() with json_encode.

When json_encode fails it just returns false... but the interface method createStream(string $content = '') requires content to be a string, so when it encounters a boolean from the failed json encoding it crashes with a TypeError.

The functions need to catch the error and maybe use a custom error message with a warning, or use a private function to check for encoding errors before uploading:

        try {
            $stream = $this->streamFactory->createStream(json_encode($body));
        } catch (\TypeError $e) {
            throw new \TypeError('Failed to parse content... make sure it's UTF-8 encoded.');
        }

If it fails you can also call json_last_error() and you'll get an INT which explains reason why the json parser failed. In my code I got a 5: JSON_ERROR_UTF8 but as you can see there are a few reasons why the parser can fail.

Current behavior
This is the current error I receive:

  Nyholm\Psr7\Factory\Psr17Factory::createStream(): Argument #1 ($content) must be of type string, bool given, called in /Users/daniel/Code/www.llresearch/vendor/meilisearch/meilisearch-php/src/Http/Client.php on line 111

  at vendor/nyholm/psr7/src/Factory/Psr17Factory.php:33
     29โ–•
     30โ–•         return new Response($code, [], null, '1.1', $reasonPhrase);
     31โ–•     }
     32โ–•
  โžœ  33โ–•     public function createStream(string $content = ''): StreamInterface
     34โ–•     {
     35โ–•         return Stream::create($content);
     36โ–•     }
     37โ–•

Temporary solution
I was able to discover and overcome the error by forcing UTF-8 encoding, however it took a whole afternoon to debug so a warning would be useful. One reason why it took a long time to debug is that it threw a TypeError instead of an Exception.

    // $document["content"] = $content;
    $document["content"] = utf8_encode($content);

Could not delete document

It seems i can't delete document with this lib.
Step1:

write_log($post_id); // $post_id=281
$rs = $client->index('docindex')->deleteDocument($post_id);
write_log($rs);

$post_id is right and i can get the right return from deleteDocument which is

(
    [updateId] => 262
)

And i can also confirm that this action is processed by visiting http://localhost:7700/indexes/docindex/updates/262.

Step2:
Access http://127.0.0.1:7700/indexes/docindex/documents, i can still see the document with id:281 was there.

Step3:
I use curl -X DELETE 'http://localhost:7700/indexes/docindex/documents/281 to delete the document. And it is done perfectly.

I don't know why, please let me know if i'm in the wrong position. Thanks.

Frequent crashes with "Wrong parameters for MeiliSearch\Exceptions\ApiException"

As ironic as it is, it seems ApiException creation fails due to invalid parameters and crashes.

Here's an image of the stack trace from Sentry:
image

Seems like Client.php uses invalid order for the params in parseResponse error handler.

---- Update:
Nevermind, it seems like the order of parameters is correct for the ApiException constructor. I'll try to investigate further.

On a sidenote: we're using PHP 7.4.

Failures on test suite on a fresh clone on KeysAndPermissionsTest class

Hi, thanks for the awesome library, I was slowly trying to contribute to the library and did a clone and installed the dependencies. But on the first test suite run, I got the following errors

1) KeysAndPermissionsTest::testExceptionIfNoMasterKeyProvided
Failed asserting that exception of type "MeiliSearch\Exceptions\HTTPRequestException" is thrown.

2) KeysAndPermissionsTest::testExceptionIfBadKeyProvidedToGetSettings
Failed asserting that exception of type "MeiliSearch\Exceptions\HTTPRequestException" is thrown.

3) KeysAndPermissionsTest::testExceptionIfBadKeyProvidedToGetKeys
Failed asserting that exception of type "MeiliSearch\Exceptions\HTTPRequestException" is thrown.

Any idea on these failures?, I have PHP 7.4 running

Consider matching the SDK version to the server version

Perhaps this package should match the server version.

The update to 0.21.0 had some breaking changes on the server side. However, this package can technically still be used with an older server, though that will result in error.

I submitted a PR to Laravel Scout a couple of days ago to add support for these changes. Scout depends on this package, but there is currently no way to check what api version should be used, other than calling the server itself (which is not performant).

It would in my opinion be better if this package would match the server version. That way, Scout (and other packages) could simply check what version of the SDK is installed, and assume that the server will be of a compatible version. It will be op to the user to make sure the versions of the SDK and server match, but there is no way around that.

Missing code samples

An update has been made on the code-samples list of samples.

The following samples are missing from this repository .code-samples.meilisearch.yaml

 settings_guide_synonyms_1
 add_movies_json_1

To know what we expect from these specific samples, please visit the cURL sample file.

Exemple for settings_guide_synonyms_1:

  $ curl \
    -X POST 'http://localhost:7700/indexes/tops/settings' \
    --data '{
        "synonyms": {
            "sweater": ["jumper"],
            "jumper": ["sweater"]
        }
    }'

Reseting displayed attributes does not work (at least its not what I am expecting)

according to this test here, the reset displayed attribute option is working as expected.

public function testResetDisplayedAttributes()
{
$res = static::$index1->resetDisplayedAttributes();
$this->assertIsArray($res);
$this->assertArrayHasKey('updateId', $res);
static::$index1->waitForPendingUpdate($res['updateId']);
$da = static::$index1->getDisplayedAttributes();
$this->assertIsArray($da);
}
}

But I figured if you set a displayed attribute before resetting the displayed attribute in the test. The displayed attribute, in fact, is still there. meaning the reset displayed attribute operation is not working at all.

If I am correct this issue is not all related to the package because the HTTP call returns proper response so the issue rather on the meilisearch itself. If @curquiza can confirm this, an issue can be opened on meilisearch source repo.

POC - try modifying the test to the following

public function testResetDisplayedAttributes()
   {
// set a new displayed attribute
       $new_da = ['title'];
       $res    = static::$index1->updateDisplayedAttributes($new_da);
       static::$index1->waitForPendingUpdate($res['updateId']);
// try resetting it
       $res = static::$index1->resetDisplayedAttributes();
       $this->assertIsArray($res);
       $this->assertArrayHasKey('updateId', $res);
       static::$index1->waitForPendingUpdate($res['updateId']);
       $da = static::$index1->getDisplayedAttributes();
       $this->assertIsArray($da); 
// test passes until here

       $this->assertEmpty($da);
// this line will fail because the displayed attribute comes out as not empty
   }

More fine grained exceptions

Hi all,

The current implementation of HTTPRequestException is to vague and of course allows you to build on it but maybe it's nice to have the more fine grained exceptions already in the package https://github.com/meilisearch/MeiliSearch/blob/master/meilisearch-core/src/error.rs, https://github.com/meilisearch/MeiliSearch/blob/master/meilisearch-error/src/lib.rs .

Wanted to start with this contribution but wanted first to ask you guys if that is ok and if you agree with it?

Create and fill sample file to display PHP examples in MeiliSearch Documentation

Code Samples for MeiliSearch documentation

Introduction

As MeiliSearch grows so does its SDK's. More and more SDK's rises from the ground and they all deserve to be as well documented as the core engine itself.

The most common way for a user to understand MeiliSearch is to go to its official documentation. As of yesterday all examples in the documentation were made with cURL. Unfortunately, most of our users do not communicate with MeiliSearch directly with cURL. Which forces them to search for the specific references somewhere else (in the readme's, in the sdk's code itself,..). This makes for unnecessary friction.

Goal

We want our documentation to include all SDK's

As a first step we want all examples to be made using the most possible SDK's. As did Stripe and Algolia.

sdk_sample

examples with curl, javascript and soon enough this SDK too!

To achieve this it is expected from this SDK to create a sample file containing all the code samples needed by the documentation.

These are the steps to follow:

  • Create your sample file
  • Fill your sample file
  • Add your samples to the documentation

Create your sample file

The sample file is a yaml file added at the root of each MeiliSearch SDK.
Sample files are created based on the sample-template.yaml file.

sample-template file:

get_one_index_1: |-
list_all_indexes_1: |-
create_an_index_1: |-
...

This template is accessible publicly here or in the public directory : .vuepress/public/sample-template.yaml of the documentation.

The name of the file should be .code-samples.meilisearch.yaml

Fill your sample file

After creating the sample file with the content of the sample template you should have a file containing all the sampleId's.

get_one_index_1: |-
list_all_indexes_1: |-
create_an_index_1: |-
...

By the name of the different sampleId you should know where that code sample will be added to the documentation.

For example, create_an_index_1 is the first example in the API References of the index creation.

Using the already existing cURL example in the documentation you should see what is expected from that sample. It is very important that you look at the already existing samples in the documentation as it gives you the parameters to use in your sample to match with the rest of the documentation.

Good sample based on documentation:

create_an_index_1: |-
  client.createIndex({ uid: 'movies' })

Bad sample that does not match with the response.

create_an_index_1: |-
  client.createIndex({ uid: 'otherName' })

Each sample is expected to be written in the respective SDK language.

Javascript example:

get_one_index_1: |-
  client.getIndex('movies').show()
list_all_indexes_1: |-
  client.listIndexes()
create_an_index_1: |-
  client.createIndex({ uid: 'movies' })
  ...

The complete cURL sample file is available at the root of the documentation repository.
Every other SDK sample file should be available at the root of their respective repository.

Formatting Exception

There is one exception to the formatting rule.
When the sampleId finishes with _md it means it is expected to be written in markdown format.

JavaScript sample id with _md extension:
yaml-js-example

Add your samples to the documentation

Once your sample file is filled with the code samples, you will need to create a pull request on the documentation repository.

Open the following file in your IDE:
.vuepress/code-samples/sdks.json

And add your sample file to the list:

[
  ...
  {
    "language": "sdk-language",
    "label": "sdk-label",
    "url": "url to yaml file"
  }
]

The language key expect a supported language for the code highlight.

The label key is the name of the tab. While label and language could have been the same, it created some conflict (i.e: bash and cURL).

The url is the raw link to access your sample file. It should look like this:
https://raw.githubusercontent.com/[PROJECT]/[REPO]/.code-samples.meilisearch.yaml

Stream error

In Stream.php line 204:
                                                     
  [RuntimeException]                                 
  Unable to seek to stream position 0 with whence 0  
                                                     

Exception trace:
  at /var/www/html/vendor/guzzlehttp/psr7/src/Stream.php:204
 GuzzleHttp\Psr7\Stream->seek() at /var/www/html/vendor/symfony/http-client/Psr18Client.php:111
 Symfony\Component\HttpClient\Psr18Client->sendRequest() at /var/www/html/vendor/meilisearch/meilisearch-php/src/Http/Client.php:163
 MeiliSearch\Http\Client->execute() at /var/www/html/vendor/meilisearch/meilisearch-php/src/Http/Client.php:99
 MeiliSearch\Http\Client->post() at /var/www/html/vendor/meilisearch/meilisearch-php/src/Endpoints/Indexes.php:40
 MeiliSearch\Endpoints\Indexes->create() at /var/www/html/vendor/meilisearch/meilisearch-php/src/Delegates/HandlesIndex.php:40
 MeiliSearch\Client->createIndex() at /var/www/html/src/Command/PackageIndexerCommand.php:42
 App\Command\PackageIndexerCommand->execute() at /var/www/html/vendor/symfony/console/Command/Command.php:258
 Symfony\Component\Console\Command\Command->run() at /var/www/html/vendor/symfony/console/Application.php:929
 Symfony\Component\Console\Application->doRunCommand() at /var/www/html/vendor/symfony/framework-bundle/Console/Application.php:96
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/html/vendor/symfony/console/Application.php:264
 Symfony\Component\Console\Application->doRun() at /var/www/html/vendor/symfony/framework-bundle/Console/Application.php:82
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/html/vendor/symfony/console/Application.php:140
 Symfony\Component\Console\Application->run() at /var/www/html/bin/console:34

I just did createIndex

meilisearch running with docker

Change master branch to main

Let's be allies and make this change that means a lot.

Here is a blog post that explain a little more why it's important, and how to easily do it. It will be a bit more complicated with automation, but we still should do it!

Guzzle 7

I am trying to install meilisearch-laravel-scout in my project. This project is using Guzzle 7 because of other dependencies. If I understand it correctly the option in meilisearch-php exists to use another HTTP Client, but this other HTTP Client can not be Guzzle 7. Reason being that the Guzzle 7 requirement conflicts with the php-http/guzzle6-adapter requirement for Guzzle 6.

Is there a way to use meilisearch-php with Guzzle 7?

Dropping support for php 7.2

Would it be alright to drop support for php 7.2 as it is unmainted, not even receiving security updates anymore?
Php 7.3 isn't mainted anymore as well, but at least gets security updates. So that is kind of understandable to still support it.

Lmk if you would accept a PR on this.

Still support PHP 7.2?

Hello the PHP community ๐Ÿ‘‹

Since PHP 7.2 has reached is EOL, should we keep supporting this version in this package?
Concretely it means removing the CI tests with PHP 7.2.

On the other hand, I can guess this is still used a lot in production.

What do you think about it? ๐Ÿ™‚

Thanks again to everyone for your involvement in this project!

How do you install MeiliSearch without composer

Problem

Given this stackoverflow question where the user is is stuck because he does not use composer (still too hard for him to grasp), I would like to know if there is a way we can install MeiliSearch without composer?

I understand the problem lies with the http-client, so the guy in the stackoverflow tried to install it like this:

include './search/guzzle7/src/Client.php';
include './search/guzzle7/src/Promise.php';
include './search/meilisearchPhp/src/MeiliSearch.php';
include './search/meilisearchPhp/src/Client.php';

But it did not work.

Finding a solution

Is there a way to make it work?

Also, alternatively, stripe suggests a manual installation on their README of their PHP client. Would we be able to reproduce something like this?

Motivations

PHP is commonly used in the very beginner js-php-mysql stack and I would like for us to be usable by developers of every level.

Why Stripe allows not to use composer: stripe/stripe-php#138

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.