Is it possible to query objects filtering by nested attribute values or by array of objects? - yql

I'm trying to compare vespa query capabilities with both ES and MongoDb but I'm having a hard time to figure out what kind of support the YQL has for advanced queries over JSON. (by the way, this would be an awesome post for vespa blog)
For example given an object Person (see example below) with nested documents and/or an array of objects, how could do:
Select all Persons whose hobbies contains 'sport'.
Select all Persons whose Phones area code equals 'NY'.
Select all Persons whose Mother's Birthdate is greater than 1960.
Person = {
Name: 'Joe',
Hobbies: ['sports','books','bonzais'],
Phones: [{Number: '12-3456-7890', areaCode: 'NY'},{Number: '22-3456-7890', areaCode: 'CA'}],
Mother: {
Name: 'Mom',
Birthdate: '1961-24-02'
}
}
Also, are there any best practices regarding how should I model an object for Vespa/YQL?
Thanks in advance.

A clarification first: YQL is just a query syntax. The JSON query language (https://docs.vespa.ai/documentation/reference/select-reference.html) is another. Yet another way (the most common) is to construct queries directly from the data received from clients in a Searcher (Java) component.
Below I show to construct your three examples in each of these variants. Vespa does not have a date type so here I've assumed you have a 'Birthyear' integer field instead.
Select all Persons whose hobbies contains 'sport'.
// YQL (as GET URL parameters)
?query=select * from Persons where hobbies contains 'sports';&type=yql
// JSON (POST body)
{ "contains" : [ "hobbies", "sports" ]}
// Java code
query.getModel().getQueryTree().setRoot(new WordItem("sports", "hobbies"));
Select all Persons whose Phones area code equals 'NY'.
// YQL (as GET URL parameters)
?query=select * from Persons where phones.areaCode contains 'NY';&type=yql
// JSON (POST body)
{"select" : { "where" : { "contains" : [ "phones.areaCode", "NY" ] } } }
// Java code
query.getModel().getQueryTree().setRoot(new WordItem("NY", "phones.areaCode"));
Select all Persons whose Mother's Birthdate is greater than 1960.
// YQL (as GET URL parameters)
?query=select * from Persons where mother.Birthyear > 1960;&type=yql
// JSON (POST body)
{"select" : { "where" : { "range" : [ "mother.Birthyear", { ">": 1960}] } } }
// Java code
query.getModel().getQueryTree().setRoot(new IntItem(">1960", "mother.Birthyear"));
Note:
Structured fields are referenced by dotting into the structures.
Containers becomes (has these tokens) or (equals) depending on the field matching setting.

Related

How to only return subset of larger query in GraphQL without returning the whole thing?

I am new to GraphQL and trying to understand how to only return a subsection of the resulting query. For example, here is my cypher query in Neo4j:
MATCH (u:User)-[HAS_FRIEND]-(f:User)
WHERE u.name = "Joe"
RETURN f.name
Notice how the above query only returns the friend names.
GraphQL
Here's a sample of GraphQL version of the above cypher query.
Input
{
"where": {
"name": "Joe"
}
}
Query
query Query($where: NameWhere) {
names(where: $where) {
name
friends {
name
}
}
}
Expected Output
Obviously, I expect output to include friends name array and the name of the user having the friend. But, what if I only want the friends array like the cypher query gives me?
While this is a simple example, I am using a graph database that has a series of connections and my nesting is looking pretty deep, but I only really need a subquery from the result. A great example is with localization - I don't need the country and language (locale), etc. in every query; I only want the subquery result (e.g. the spelling in UK/ENG is colour and in US/ENG is color).
Neo4j offers a Javascript GraphQL library that is able to handle your use case (and many more)!
Please have a look at the documentation for information on how to get started. See even the example queries, where this one is fairly close to what you are looking for.
Anyhow, the GraphQL query provided above, using the #neo4j/graphql library, could look like so:
query Query {
users (where: { name: "Joe" }) {
friends {
name
}
}
}
(Note that the where parameter ("Joe") is provided in-line in this case)

How to search product data by id and name in one query

I have a index named product in elastic-search with field id, name etc..
I want to search products by id or name but my id field is integer and name is a text, I tried following but getting error while searching by name.
error type":"number_format_exception","reason":"For input string:
\"test\""
def self.search_by_name_or_id(query)
__elasticsearch__.search({
query: {
bool: {
must:{
multi_match: {
query: query,
fields: ["id", "name"]
}
}
}
}
})
end
Problem
Exception is clear, that you are trying to search test which is a String in the integer field and elasticsearch is not able to convert test in integer, instead of test if you had search for 10 or 100, then elasticsearch would convert it into integer and would not throw the exception.
Solution
You are trying to mix 2 things here, I am not sure about your design but if your id field can contain pure numbers i.e. integers, then it's not possible to achieve in a single query the way you are doing.
If you can convert your id field to String, then multi_match query would work perfectly fine, otherwise, you need to first check in your application, that search term can be converted to number or not, i.e. 10 or 100 would work fine but test10 or test100 would not and anyway there is no point of searching these terms in id field as it won't be present as its defined as integer in ES and ES would reject documents containing these terms during indexing time only. So based on your application code check you can construct the ES query which may or may not include the id field in multi-match.

Returning multi value in dynamic query using neo4j client

Following the question I asked: Build a dynamic query using neo4j client
I got an answer about how can I return value dynamically using string only.
When I'm trying to use the syntax to return multi values from the query it failed,
I tried the following query:
var resQuery2 = WebApiConfig.GraphClient.Cypher
.Match("(movie:Movie {title:{title}})")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.WithParam("title", title)
.Return(() => Return.As<string>("movie, collect([person.name, head(split(lower(type(r)), '_')), r.roles])"));
I'm getting the following error:
The deserializer is running in single column mode, but the response
included multiple columns which indicates a projection instead. If
using the fluent Cypher interface, use the overload of Return that
takes a lambda or object instead of single string. (The overload with
a single string is for an identity, not raw query text: we can't map
the columns back out if you just supply raw query text.)
Is it possible to return multiple nodes using only strings?
We can't get an output like in the question you asked previously - this is due to the fact that you are asking for a Node (the movie) and a Collection of strings (the collect) and they have no common properties, or even styles of property.
Firstly, let's look at the painful way to do this:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => Return.As<string>("{movie:movie, roles:collect([person.name, head(split(lower(type(r)), '_')), r.roles])}"));
var results = q.Results;
Here we take the query items (movie, r, person) and create a type with them the {} around the results, and cast that to a string.
This will give you a horrible string with the Node data around the movie and then a collection of the roles:
foreach (var m in results)
{
//This is going to be painful to navigate/use
dynamic d = JsonConvert.DeserializeObject<dynamic>(m);
Console.WriteLine(d.movie);
Console.WriteLine(d.roles);
}
You'd be a lot better off doing something like:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => new
{
Movie = Return.As<Node<string>>("movie"),
Roles = Return.As<IEnumerable<string>>("collect([person.name, head(split(lower(type(r)), '_')), r.roles])")
});
var res = q.Results;
You could either JsonConvert.DeserializeObject<dynamic>() the Movie node, at your leisure, or write a strongly typed class.
In terms of a 'dynamic' object, I don't know how you were wanting to interact with the collect part of the return statement, if this doesn't help, you might need to update the question to show a usage expectation.

Searching for Parse objects where an object's array property contains a specified string

I have a PFObject subclass which stores an array of strings as one of its properties. I would like to query for all objects of this class where one or more of these strings start with a provided substring.
An example might help:
I have a Person class which stores a firstName and lastName. I would like to submit a PFQuery that searches for Person objects that match on name. Specifically, a person should be be considered a match if if any ‘component’ of either the first or last name start with the provided search term.
For example, the name "Mary Beth Smith-Jones" should be considered a match for beth and bet, but not eth.
To assist with this, I have a beforeSave trigger for the Person class that breaks down the person's first and last names into separate components (and also lowercases them). This means that my "Mary Beth Smith-Jones" record looks like this:
firstName: “Mary Beth”
lastName: “Smith-Jones”
searchTerms: [“mary”, “beth”, “smith”, “jones”]
The closest I can get is to use whereKey:EqualTo which will actually return matches when run against an array:
let query = Person.query()
query?.whereKey(“searchTerms”, equalTo: “beth”)
query?.findObjectsInBackgroundWithBlock({ (places, error) -> Void in
//Mary Beth is retuned successfully
})
However, this only matches on full string equality; query?.whereKey(“searchTerms”, equalTo: “bet”) does not return the record in question.
I suppose I could explode the names and store all possible sequential components as search terms (b,e,t,h,be,et,th,bet,etc,beth, etc) but that is far from scalable.
Any suggestions for pulling these records from Parse? I am open to changing my approach if necessary.
Have you tried whereKey:hasPrefix: for this? I am not sure if this can be used on array values.
https://parse.com/docs/ios/guide#queries-queries-on-string-values

Grails searcheble don't use field during fulltext search

Domain (just simple example):
class House {
String address
String region
Long price
static searchable = { only = ['address', 'price', 'region'] }
}
I want to search by address with price selector
Search query is:
"${address} AND price: [100 TO 1000]"
But if address='500' in search result will be Houses with price 500. I need to find house for address only by House.address + House.region
If I understood, you want to use the value of ${address} to query only on the address and region attributes.
In that case, based on the "complex" query string example at http://grails.org/Searchable+Plugin+-+Searching+-+String+Queries, your query string could be:
"+(address:${address} OR region:${address}) price:[100 TO 1000]"
Matches must have a ${address} value for address or a ${address} value for region, and have a value from 100 to 1000 for price.
Note: your input variable has the same name of an entity attribute, "address", wich can cause some confusion when trying to understand query strings using them both. Though not needed, you might want to change that, for easier readable query strings.

Resources