How to do complex querying with logical operations by using searchkick - ruby-on-rails

Iam using searchkick library as an elasticsearch client for Product searching.
https://github.com/ankane/searchkick
It is possible to create 'OR' condition and 'AND' condition;
AND operation
Product.search where: {price: {lte: 200}, in_stock: true}
OR operation
Product.search where: {or: [[{in_stock: true}, {backordered: true}]]}
But Iam stuck with creating multiple 'AND' 'OR' conditions with searchkick.
I need something like
A OR B OR ( C AND D )
or I need like this,
A AND B AND ( C OR D )
Please guide me, how to achieve this
Thanks

A OR B OR ( C AND D )
Product.search where: {or: [[{brand: 'nike'}, {in-stock: true}, {price: {lte: 12}, color: 'red'}]]}
A AND B AND ( C OR D )
Product.search where: {brand: 'nike', in-stock: true, or: [ [{price: {lte: 12}}, {color: 'red'}] ]}
Update
(A OR B) AND (C OR D)
Product.search where: {or: [[ {or: [[{brand: "nike"}, {in-stock: "true"}]]}], [{or: [[{price: 100}, {color: "red"}]]}]]}

For (A OR B) AND (C OR D) it should be (no double brackets needed):
where: { _and: [ { _or: [ {A}, {B} ] }, { _or: [{C}, {D}] } ] }

Related

Dart How to distinct (unique) `List<List<Object>>`

I have a List<List<Object>>>. What is the best readable way to unique my list?
For instance:
[[A, B], [B, A], [B, C, B]]
to:
[[A, B], [B, C, B]]
If you are alright with modelling the result as a Set<Set<Object>>, I would suggest this approach.
I am using the collection package because it provides an easy way to check if two collections are equal.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C'],
];
Set<Set<String>> unique = HashSet<Set<String>>(
equals: SetEquality().equals,
hashCode: SetEquality().hash,
);
unique.addAll(items.map((v) => v.toSet()));
print(items);
print(unique);
}
output
[[A, B], [B, A], [B, C]]
{{A, B}, {B, C}}
Since the inner lists
are arbitrary length
can contain duplicates
are not necessarily in the same order (but we want to treat different orderings as the same list)
It does make things more complicated, but you can use the same general approach. Here we will keep the inner elements as lists, but we will provide definitions for equals and hashCode that take the above constraints into account.
If the elements implement Comparable then you can use .sorted() to account for constraint #3, and ListEquality().equals and ListEquality().hash to account for constraints #1 and #2.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C', 'B'],
];
Set<List<String>> unique = HashSet<List<String>>(
equals: (a, b) => ListEquality().equals(a.sorted(), b.sorted()),
hashCode: (a) => ListEquality().hash(a.sorted()),
);
unique.addAll(items);
print(items);
print(unique);
}
output
[[A, B], [B, A], [B, C, B]]
{[A, B], [B, C, B]}
However, what if the elements don't implement Comparable?
You have a few options in this case.
First, the .sorted() method optionally accepts a function that you can use to provide custom sorting logic.
The other approach would be to get a count of occurrences of each element in the list and compare the counts. I have implemented this approach below.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C', 'B'],
];
Set<List<String>> unique = HashSet<List<String>>(
equals: (a, b) => MapEquality().equals(counts(a), counts(b)),
hashCode: (a) => MapEquality().hash(counts(a)),
);
unique.addAll(items);
print(items);
print(unique);
}
Map<T, int> counts<T>(List<T> items) {
Map<T, int> result = {};
for (final item in items) {
result.update(item, (v) => v + 1, ifAbsent: () => 1);
}
return result;
}
output
[[A, B], [B, A], [B, C, B]]
{[B, C, B], [A, B]}
Note that the elements are in a different order than the previous solution, this is because HashSet does not preserve the insertion order, if you do want to preserve the order you can use a LinkedHashSet instead.

Erlang: proplists:get_value/2 or pattern matching?

I have a list of tuples that has always the same form (i.e. the tuples come always in the same order):
1> L = [{a, 1}. {b,2}, {c, 3}, {d, 4}].
Knowing that the list has only a few elements, what is the best way to extract the values associated to the keys?
Suppose the list is passed as argument to a function, to extract the values should I use:
proplists:get_value(a, L).
proplists:get_value(b, L).
...
proplists:get_valus(d, L).
Or should I simply use pattern matching as:
[{a, 1}. {b,2}, {c, 3}, {d, 4}] = L.
If you really know your lists is in same form pattern matching is simplest
[{a, A}, {b, B}, {c, C}, {d, D}] = L,
you can compare it with following
[A, B, C, D] = [ proplists:get_value(X, L) || X <- [a,b,c,d] ],
or
A = proplists:get_value(a, L),
B = proplists:get_value(b, L),
C = proplists:get_value(c, L),
D = proplists:get_value(d, L),
or
[A, B, C, D] = [ V || Key <- [a,b,c,d], {K, V} <- L, K =:= Key ],
Pattern matching will be also fastest. You can also use lists:keyfind/3 which is implemented as Bif and is way faster than proplist:get_value/2 but it doesn't matter for short lists.

Rails method for concatenate

Is it possible to concatenate Array ['a', 'b', 'c'] to String "a, b and c" ?
But ['a', 'b'] should transform to "a and b".
Rails provides a to_sentence helper:
> ['a', 'b'].to_sentence
=> "a and b"
> ['a', 'b', 'c'].to_sentence
=> "a, b, and c"
If you want a, b and c rather than a, b, and c you can change the last_word_connector:
> ['a', 'b', 'c'].to_sentence(last_word_connector: " and ")
=> "a, b and c"
a = %w{ a b }
str = a[0..-3].each_with_object("") do |item, res|
res << "#{item}, "
res
end
str << "#{a[-2]} and #{a[-1]}"
p str
a = ['a', 'b', 'c']
result = a[0...-1].join ', '
result += " and #{a[-1]}" if a.length > 1
result # => a, b and C
a = ['a', 'b']
result = a[0...-1].join ', '
result += " and #{a[-1]}" if a.length > 1
result # => a and b

Does Smalltalk have closures?

If it has closures, can I assume that I can use many of strong functional style techniques on there?
Yes, Smalltalk has closures. The following code creates a closure that returns the sum of its two arguments:
sum := [ :a :b | a + b ].
Closures are objects that can be instantiated, passed around and manipulated. To evaluate a closure you send value, value:, value:value:, ...
sum value: 1 value: 2.
Closures are prominently used with collections to iterate, filter, map, ... all values of a collection:
aCollection select: [ :each | each isOdd ].
aCollection inject: 0 into: [ :each :result | each + result ].
Furthermore, they are used for control structures like loops:
[ iterator hasNext ]
whileTrue: [ iterator next ].
1 to: 10 do: [ :each | ... ].
Also conditionals are implemented using closures:
condition
ifTrue: [ do this ]
ifFalse: [ do that ]
Pharo has them:
all VMs have closure support required
for latest images
makeAdder := [ :x | [ :y | x + y ]].
add2 := makeAdder value: 2.
add2 value: 3.
Returns 5.
But notice that
makeCounter := [ :init | [ init := init + 1. init ]].
won't work (Cannot store into ->init …), like (for example) in CL:
CL-USER> ((lambda (init) (lambda () (incf init))) 0)
#<COMPILED-LEXICAL-CLOSURE #xC7A495E>
CL-USER> (funcall *)
1
CL-USER> (funcall **)
2
CL-USER> (funcall ***)
3
If I'm not mistaken, this used to work before the new closure compiler was introduced. I'm not sure why it doesn't work with the new compiler.

quasiquote/quote in lua?

In Lisp, I can have:
(a b c d e f g)
which means
look up b, c, d, e, f, g
look up a; apply value of a to above
Then, I can also have:
`(a b c d e f g)
which is the equiv to
(list 'a 'b 'c 'd 'e 'f 'g)
Now, in lua, I can have:
[snipplet 1]
foo = {
Foo,
{Cat, cat},
{Dog, dog}
};
Which ends up most likely expanding into:
{ nil, { nil, nil}, {nil, nil}}
Whereas what I really want is something like:
[snipplet 2]
{ "Foo", {"Cat", "cat"}, {"Dog", "dog"}}
Is there some backquote-like method in lua?
I find [snipplet 1] to be more visually pleasing than [snipplet 2], but what I mean is snipplet 2.
Thanks!
There's no syntax for that but you can try this run-time solution:
setmetatable(_G,{__index=function (t,k) return k end})
This makes all undefined variables behave as if they contained strings with their names.
Consider a function that parses a string containing the Lisp syntax and constructs a table from that.

Resources