elasticsearch -- sort by distance and price/rating - sorting

I'm trying to compose a query that'd be sorted by _geo_distance in the first place and by price or rating secondly. Basically, what I want to achieve is the closest results with the highest rating/price.
Here's an example query:
{
query: {
bool: {
must: [
{
bool: {
should: [
{
match_all: {
}
}
]
}
}
],
filter: [
{
bool: {
}
}
]
}
},
sort: [
{
_geo_distance: {
location: {
lat: 51.840191,
lon: 61.581038
},
order: 'asc',
unit: 'km',
distance_type: 'sloppy_arc'
}
},
{
rating: {
order: 'desc'
}
}
],
size: 10,
from: 0,
_source: [
'id',
'_score',
'location',
'price',
'rating'
]
}
The problem is that the result contains only the closest results with low ratings and prices.
My question is whether it is possible to adjust the sorting somehow, or should I attach a function_score query to replace the sorting?

Related

elastic search method based on condition in nodejs or nestjs

Hello I am implementing elasticsearch using nestjs.
Currently, I am trying to add match keywords according to each condition, but I am having difficulties, so I leave a question.
const { hits } = await this.elasticsearchService.search({
index: 'myindex',
body: {
query: {
bool: {
must: [
{
match: {
type: type.join(' or '),
},
},
keyword && {
query_string: {
query:
'*' + keyword.replace(/ /g, '* AND *') + '*',
},
},
],
},
},
},
});
In the code above, keyword is a variable that may or may not exist.
But I am getting an error in the above code, how should I set the condition?
const query = [];
if(keyword){
query.push({
query_string:{
query : xxx
}
})
}
const { hits } = await this.elasticsearchService.search({
index: 'myindex',
body: {
query: {
bool: {
must: query
},
},
},
});

How to filter dynamically in strapi

This might be the noobiest Strapi or possibly backend question at all, but I have just started doing backend, so please bear with me.
That being said, I have the following case. I am building an online shop and every product I have has a price (a required field) and new_price (optional). When I filter my API by min-max value, I would like to filter price if new_price is not available and new_price if it is available. Is this possible at all in strapi?
{
id: 2,
attributes: {
name: "My name",
createdAt: "2022-01-15T11:28:46.138Z",
updatedAt: "2022-02-16T10:38:20.412Z",
publishedAt: "2022-01-15T11:29:30.306Z",
description: "Lorem ipsum",
item_code: "688002",
slug: "some-slug-here",
available: true,
price: 59,
new_price: 21.9
}
http://localhost:1337/api/products?filters[price || new_price][$gte]=50
You're answer is perfectly fine. Just posted my full implementation here so that it may help others who stumble upon it.
const qs = require("qs");
const query = qs.stringify(
{
filters: {
$or: [
{
$and: [
{ new_price: { $notNull: true } },
{ new_price: { $gte: minPrice } },
{ new_price: { $lte: maxPrice } },
],
},
{
$and: [
{ new_price: { $null: true } },
{ price: { $gte: minPrice } },
{ price: { $lte: maxPrice } },
],
},
],
},
},
{
encodeValuesOnly: true,
}
);
await request(`/api/books?${query}`);
So I came up with this solution. It might be ugly and not how it's done, but it works, and I couldn't think of anything else. If somebody has a better solution, I will greatly appreciate it!
const query = qs.stringify(
{
populate: '*',
pagination: {
page: page,
pageSize: PER_PAGE
},
filters: {
$or: [
{
$and: [
[
{
new_price: {
$null: true
}
},
{
price: {
$gte: minPrice
}
},
{
price: {
$lte: maxPrice
}
}
]
]
},
{
$and: [
[
{
new_price: {
$notNull: true
}
},
{
new_price: {
$gte: minPrice
}
},
{
new_price: {
$lte: maxPrice
}
}
]
]
}
]
}
},

Sort by nested Geo distance properties

I'm trying to sort by geo distance an index with nested geo point mapping.
Here is my simplified mapping :
'organizations' => [
'_doc' => [
'properties' => [
'locations' => [
'type' => 'nested',
'properties' => [
'point' => [
'type' => 'geo_point',
'ignore_malformed' => true
]
]
]
]
]
]
Here, each organizations can have several locations point.
Documents :
PUT /organizations ,
[
{
name: "A",
locations: [
{
point: {
lat: 1.5,
lon: 2
}
},
{
point: {
lat: 1,
lon: 0
}
}
]
},
{
name: "B",
locations: [
{
point: {
lat: 4.2,
lon: 3
}
}
]
},
{
name: "C",
locations: [
{
point: {
lat: 0.4,
lon: 1
}
}
]
}
];
Here is my query (PHP array):
[
"query" => [
"query_string" => [
"query" => "*"
]
]
"sort" => [
0 => [
"_geo_distance" => [
"locations.point" => "1, 0.1"
"order" => "asc"
"unit" => "km"
"nested_path" => "locations"
]
]
]
]
expected result :
1 => name : A
2 => name : C
3 => bame : B
I'd like to get resources ordered by geo_point (check each location, and then check if a location is close to the given lat / lon )
You can meet your goal using function_score.
For example with the query like:
note: code is in js;
"_geo_distance": {
"place.location.geo.coordinates": [
this._geo.lon,
this._geo.lat
],
"order": "asc",
"unit": "km",
"distance_type": "plane"
}
Then in function_score add script_score
function_score["script_score"] = {
"script": {
"source": `1000/doc['place.location.geo.coordinates'].planeDistance(${this._geo.lat},${this._geo.lon}) `
}
The original function_score is like:
'function_score': {
"score_mode": "multiply",
'query': {
"dis_max": {
"tie_breaker": 0.7,
"boost": 1.9,
"queries": this._query
},
},
'boost': 0.06,
'boost_mode': 'sum',
}

ElasticSearch update query using NOT and AND to be ES 5.5 compatible

I need to update this query to be ES 5.5 compatible (NOT and AND are deprecated).
:and => [
{
term: {
type: 'group'
}
},
{
:not => {
terms: {
group_id: group_ids
}
}
},
{
:not => {
terms: {
user_id: user_ids
}
}
}
]
Try this:
:bool => [
:filter => [
{
term: {
type: 'group'
}
}
],
:must_not => [
{
terms: {
group_id: group_ids
}
},
{
terms: {
user_id: user_ids
}
}
]
]

Mongoose model configuration for elasticsearch

I'm having an issue with mongoosastic in combination with KeystoneJS. I want to search for all the posts of a specific user with a match on the title. I have the following model:
var keystone = require('keystone'),
Types = keystone.Field.Types,
mongoosastic = require('mongoosastic');
User = keystone.list('User');
var Post = new keystone.List('Post');
Post.add({
title: {
type: String,
required: true,
es_indexed: true
},
state: {
type: Types.Select,
options: 'draft, published, archived',
default: 'draft',
es_indexed: true
},
author: {
type: Types.Relationship,
ref: 'User',
es_schema: User,
es_include_in_parent: true,
es_indexed:true
},
publishedDate: {
type: Types.Date,
index: true,
dependsOn: {
state: 'published'
}
},
content: {
extended: {
type: Types.Html,
wysiwyg: true,
height: 400,
es_indexed: true
}
},
});
Post.schema.plugin(mongoosastic, {
hosts: [
process.env.SEARCHBOX_SSL_URL
]
});
Post.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Post.register();
Post.model.createMapping(function(err, mapping) {
if (err) {
console.log('error creating mapping', err);
} else {
console.log('mapping created for Post');
}
});
User.schema.plugin(mongoosastic, {
populate: [
{path: 'author'}
]
});
I tried the approach of "Indexing Mongoose references" (https://github.com/mongoosastic/mongoosastic#indexing-mongoose-references) but I can't get it to work. Elasticsearch is not returning any hits.. My search query:
{
"query": {
"filtered": {
"query": { "match": { "title": "req.query.query" }},
"filter": { "term": { "author": "req.query.userId" }} // or author._id
}
}
}
Can someone provide me the correct Post model configuration & search query? Thanks!

Resources