Im using FOSElasticaBundle to integrate my symfony3 project with elasticsearch. I have a mapping similar to:
company:
mappings:
id: { boost: 1, type: string, index: not_analyzed }
cik: { boost: 2, type: string, index: not_analyzed }
conformedName: { boost: 6, analyzer: custom_analyzer }
assignedSIC:
type: object
properties:
id: { type: string, index: not_analyzed }
code: { type: string, index: not_analyzed }
title: { analyzer: custom_analyzer }
businessAddress:
type: object
properties:
street1: { analyzer: custom_analyzer }
street2: { analyzer: custom_analyzer }
city: { analyzer: custom_analyzer }
state: { analyzer: custom_analyzer }
zip: { type: string, index: not_analyzed }
phone: { type: string, index: not_analyzed }
I want to filter by city, state and zip of the nested businessAddress property.
I have this query:
$boolQuery = new BoolQuery();
$cityQuery = new Match();
$cityQuery->setFieldQuery('businessAddress.city', array($city]));
$cityQuery->setFieldParam('businessAddress.city', 'analyzer', 'custom_analyzer');
$boolQuery->addMust($cityQuery);
$this->finder->find($boolQuery);
json query as
{"query":{"bool":{"must":[{"match":{"businessAddress.city":{"query":["NY"],"analyzer":"custom_analyzer"}}}]}}}
But have 0 results, i dont know if the sintax businessAddress.city will be handled automatically by the bundle or do i need to create a nested query. In case that is a nested query how i can build that?
EDIT
After some comments below i notice i was setting match term as array, now i change from:
$cityQuery->setFieldQuery('businessAddress.city', array($city]));
to
$cityQuery->setFieldQuery('businessAddress.city', $city]);
resulting on json query:
{"query":{"bool":{"must":[{"match":{"businessAddress.state":{"query":"NY","analyzer":"custom_analyzer"}}}]}}}
i have check over internet and found nothing.
Please help. Thanks
Here is how to implement it with a nested query.
First of all you need to update your mapping to make your businessAddressa type nested
...
businessAddress:
type: nested
...
Then you will be able to perform the following Nested query
$boolQuery = new BoolQuery();
$cityQuery = new Match();
$cityQuery->setFieldQuery('businessAddress.city', array($city]));
$cityQuery->setFieldParam('businessAddress.city', 'analyzer', 'custom_analyzer');
$boolQuery->addMust($cityQuery);
// nested query
$query = new Nested();
$query->setPath('businessAddress');
$query->setQuery($boolQuery);
// pass the nested query to your finder
$this->finder->find($query);
Hope this helps
Related
I have a relationship between Product and its units describe in schema as below:
public static schema: Realm.ObjectSchema = {
name: Product.schemaName,
primaryKey: 'sku',
properties: {
name: {type: 'string'},
baseUnit: {type: SoldableUnit.schemaName},
derivedUnits: {type: 'list', objectType: SoldableUnit.schemaName},
},
};
public static schema: Realm.ObjectSchema = {
name: SoldableUnit.schemaName,
embedded: true,
properties: {
unitName: {type: 'string'},
sellingPrice: {type: 'double'},
weight: {type: 'float'},
barcode: {type: 'string', optional: true, indexed:true},
},
};
The idea is a product can have one baseUnit and many derivedUnits, and both baseUnit and derivedUnits are SoldableUnit with the same schema. I want to query product by barcode. Here I have to return the product, providing a barcode which maybe equals to barcode of baseUnit or derived units. After some research, I find out that I can use backlink to return inverse relationship. The problem here is SoldableUnit occurs both in baseUnit and derivedUnits and I dont know how to achieve this goals? And whether the schema design of Product is appropriate?
I'm trying to insert an array into an object and I'm not having any luck. I think the schema is rejecting it based on validation but I'm not sure why. If I console.log(this.state.typeOfWork) and check typeof it states its an Object which contains:
(2) ["Audit - internal", "Audit - external"]
0: "Audit - internal"
1: "Audit - external"
My collection after an update contains:
"roleAndSkills": {
"typeOfWork": []
}
Example: Schema
roleAndSkills: { type: Object, optional: true },
'roleAndSkills.typeOfWork': { type: Array, optional: true },
'roleAndSkills.typeOfWork.$': { type: String, optional: true }
Example: update
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simple schema has some problems with validation on Objects or Arrays, i had the same problem in a recent app i developed.
What can you do?
well, what i did, on the Collections.js file, when you are saying:
typeOfWork:{
type: Array
}
Try adding the property blackbox:true, like this:
typeOfWork:{
blackbox: true,
type: Array
}
This will tell your Schema that this field is taking an Array, but ignore further validation.
The validation i made was on main.js, just to be sure i had no empty array and the data was plain text.
As requested here is my update method, im my case i used objects not arrays but it works the same way.
editUser: function (editedUserVars, uid) {
console.log(uid);
return Utilizadores.update(
{_id: uid},
{$set:{
username: editedUserVars.username,
usernim: editedUserVars.usernim,
userrank: {short: editedUserVars.userrank.short,
long: editedUserVars.userrank.long},
userspec: {short: editedUserVars.userspec.short,
long: editedUserVars.userspec.long},
usertype: editedUserVars.usertype}},
{upsert: true})
},
here it the collection schema
UtilizadoresSchema = new SimpleSchema({
username:{
type: String
},
usernim:{
type: String
},
userrank:{
blackbox: true,
type: Object
},
userspec:{
blackbox: true,
type: Object
},
usertype:{
type: String
}
});
Utilizadores.attachSchema(UtilizadoresSchema);
Hope it helps
Rob
typeOfWork is an Array. You should push your value in it :
$push: {
"roleAndSkills.typeOfWork": this.state.typeOfWork
}
for multiple values :
$push: {
"roleAndSkills.typeOfWork": { $each: [ "val1", "val2" ] }
}
mongo $push operator
mongo dot notation
You state that this.state.typeOfWork is an array (of strings) but then when you .update() your document you are enclosing it in square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simply remove the redundant square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: this.state.typeOfWork
}
}
});
Also since your array is just an array of strings you can simplify your schema a bit by declaring it as such with [String] for the type:
'roleAndSkills.typeOfWork': { type: [String] }
Note furthermore that objects and arrays are by default optional so you can even omit the optional flag.
I am trying to get subschemas to work as an array, which I assume is the correct way to handle my problem (but please correct me if I am wrong!). I provide a simplified working example to show my problem, based on the BooksSchema example provided by the AutoForm package. In my example, I have a collection of Libraries, and one of the fields in the 'Libraries' object is supposed to be the library's collection of books. Rendering the AutoForm does not give me any input labels as defined in my Book collection, but instead just shows one (1) empty text input field.
Schemas:
import SimpleSchema from 'simpl-schema';
SimpleSchema.extendOptions(['autoform']);
BooksSchema = new SimpleSchema({
title: {
type: String,
label: "Title",
max: 200
},
author: {
type: String,
label: "Author"
},
copies: {
type: Number,
label: "Number of copies",
min: 0
},
lastCheckedOut: {
type: Date,
label: "Last date this book was checked out",
optional: true
},
summary: {
type: String,
label: "Brief summary",
optional: true,
max: 1000
}
}, { tracker: Tracker });
LibrariesSchema = new SimpleSchema({
collection: {
type: Array
},
'collection.$': {
type: BooksSchema,
minCount: 1
}
});
LibrariesSchema.extend(BooksSchema);
Libraries = new Mongo.Collection("libraries");
Libraries.attachSchema(LibrariesSchema);
AutoForm:
{{> quickForm collection="Libraries" id="insertBookForm" type="insert"}}
Thank you so much in advance for your time, really been struggling with this for a long time now!
In my case I was indeed able to resolve the issue by using John Smith's example without the brackets.
LibrariesSchema = new SimpleSchema({
'books': {
type: BooksSchema,
minCount: 1
}
});
LibrariesSchema = new SimpleSchema({
'books': {
type: [BooksSchema],
minCount: 1
}
});
Arrays of a specific types, for use in check() or schema definitions, are specified as [SomeType], eg. [String], or [BooksSchema] in your case.
From what I understand in the docs, you can define your schema like this:
MySchema = new SimpleSchema({
//This says that the addresses key is going to contain an array
addresses: {
type: [Object],
},
// To indicate the presence of an array, use a $:
"addresses.$.street": {
type: String,
},
"addresses.$.city": {
type: String,
}
});
Ok, I get this part. But what if I wanted to validate the contents in a specific array index? I want something like this:
MySchema = new SimpleSchema({
//This says that the itemsOrdered key is going to contain an array
itemsOrdered: {
type: [Object],
},
// Here I want to validate certain indexes in the array.
"itemsOrdered.0.sku": {
type: String
},
"itemsOrdered.0.price": {
type: Number
},
"itemsOrdered.1.sku": {
type: String
},
"itemsOrdered.1.price": {
type: Number
},
"itemsOrdered.1.quantity": {
type: Number
},
"itemsOrdered.2.sku": {
type: String
},
"itemsOrdered.2.price": {
type: Number
},
"itemsOrdered.2.customerNotes": {
type: String
optional: true
}
});
So here I'm trying to validate the values inside array index 0, 1, and 2. Each array index has a different item that has been ordered.
Normally I would use a hash table data structure, but for this purpose I need to preserve order which is why I'm using an array.
When I try to run this code I get an error Cannot read property 'blackbox' of undefined
Have you considered custom validation?
https://github.com/aldeed/meteor-simple-schema/blob/master/README.md#custom-validation
According to the doc within the function the key property of this will provide the information you want. So you could have something like:
MySchema = new SimpleSchema({
//This says that the itemsOrdered key is going to contain an array
itemsOrdered: {
type: [Object],
},
// Here I want to validate certain indexes in the array.
"itemsOrdered.$.sku": {
type: String,
custom: function () {
var key = this.key,
re = /\d+/;
var index = Number(key.match(re)[0]);
// Do some custom validation
}
},
"itemsOrdered.$.price": {
type: Number
},
"itemsOrdered.$.quantity": {
type: Number,
optional: true
},
"itemsOrdered.$.customerNotes": {
type: String,
optional: true
}
});
Here I put the validation logic in the sku field since it's required.
I'm trying elasticsearch and it looks great!
I noticed, however, a problem very uncomfortable, in a field that contains hello world if I search hello wo returns no result!
Why does this happen?
Place my configuration (FOSElasticaBundle):
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
indexes:
website:
client: default
settings:
index:
analysis:
analyzer:
custom_search_analyzer:
type: custom
tokenizer: standard
filter : [standard, worddelimiter, stopwords, snowball, lowercase, asciifolding]
custom_index_analyzer:
type: custom
tokenizer: nGram
filter : [standard, worddelimiter, stopwords, snowball, lowercase, asciifolding]
filter:
stopwords:
type: stop
stopwords: [_italian_]
ignore_case : true
worddelimiter :
type: word_delimiter
tokenizer:
nGram:
type: nGram
min_gram: 1
max_gram: 20
types:
structure:
mappings:
name: { boost: 9, search_analyzer: custom_search_analyzer, index_analyzer: custom_index_analyzer, type: string }
Any idea on how to solve?
EDIT
Here my query:
{
query: {
bool: {
must: [ ]
must_not: [ ]
should: [
{
term: {
structure.name: hello wo
}
}
]
}
}
from: 0
size: 10
sort: [ ]
facets: { }
}
EDIT 2
Ok, I don't understand this behavior ...
Now I run this query:
{
query: {
bool: {
must: [
{
term: {
structure.name: hello
}
}
{
term: {
structure.name: wo
}
}
]
must_not: [ ]
should: [ ]
}
}
from: 0
size: 10
sort: [ ]
facets: { }
}
This query is the result I wanted, but I do not understand what is the difference in putting a must with two words and two must have a word with everyone!
I could explain this behavior?
Well i need to explain you probably how its working
When you index text elastic search will try to split it to terms if text is analyzed(as its in your mapping) so in your case "hello world" will be spited to two terms "hello" and "world" when you will do term search you write term hello world which does not fit any of your two terms.
To avoid spiting to terms you can set in mapping that field name is not analyzed, then it will not be spitted to two words and will be handled as one token.
Other solution is you can multiterm query
{
"query": {
"terms": {
"structure.name": [
"world",
"hello"
]
}
}
}
Also when you use query_string it return result since it has different algorithm.
So depends on you needs you should use different queries, but to search by name you should use query_string, term should be used if you want to filter lets say categoryId, tags and stuff like that.