Elasticsearch completion by url - javascript

Using nodejs elasticsearch api I am trying to get completion by url. The problem is when I am trying to define the way the autocompletion should work, it splits my url in several chuncks, not the whole url:
E,g: URL=https://www.edx.org/course/introduction-mongodb-using-mean-stack-mongodbx-m101x-0
This is the index that I created:
elasticClient.index({
index: indexName,
type: 'mooc',
body: {
name: document.name,
url: document.url,
platform: document.platform,
idMooc: document.idMooc,
platformId: document.platformId,
duration: document.duration,
points: document.points,
tags: document.tags,
dirty: document.dirty,
area: document.area,
description: document.description,
suggest: {
input: document.name.split(' '),
output: document.name,
payload: document || {}
},
urlsuggest:{
input: document.url,
output: document.url,
payload: document || {}
}
}
})
This is my search:
elasticClient.search({
index: indexName,
type: 'mooc',
body: {
query: {
filtered: {
query: {
match: {
// match the query agains all of
// the fields in the posts index
_all: input
}
},
filter: {
// only return documents that the flag dirty is false
term: { dirty: false }
}
}
},
suggest: {
urlsuggest: {
text: input,
term: {
field: 'urlsuggest'
}
}
}
}
})
I got the following urlsuggest:
"suggest":{
"urlsuggest":[
{
"text":"https",
"offset":0,
"length":5,
"options":[
]
},
{
"text":"www",
"offset":8,
"length":3,
"options":[
]
},
{
"text":"edx",
"offset":12,
"length":3,
"options":[
]
},
{
"text":"org",
"offset":16,
"length":3,
"options":[
]
},
{
"text":"course",
"offset":20,
"length":6,
"options":[
]
},
{
"text":"introduction",
"offset":27,
"length":12,
"options":[
]
},
{
"text":"mongodb",
"offset":40,
"length":7,
"options":[
]
},
{
"text":"using",
"offset":48,
"length":5,
"options":[
]
},
{
"text":"mean",
"offset":54,
"length":4,
"options":[
]
},
{
"text":"stack",
"offset":59,
"length":5,
"options":[
]
},
{
"text":"mongodbx",
"offset":65,
"length":8,
"options":[
]
},
{
"text":"m",
"offset":74,
"length":1,
"options":[
]
},
{
"text":"x",
"offset":78,
"length":1,
"options":[
]
}
]
}
Which, for some reason splits my url in many inputs. Apart from that is not finding properly since it does not find any match and my elasticsearch contains that field and was properly populated.

Related

Mongoose add to array of nested array if exists create otherwise

i'm trying to accomplish the following in mongoose:
Say i have the following collection
{
"_id": {
"$oid": "111"
},
"email": "xxx#mail.com",
"givenName": "xxx",
"familyName": "xxx",
"favoriteProducts": [{
"soldTo": "33040404",
"skus": ["W0541", "W2402"]
}, {
"soldTo": "1223",
"skus": ["12334"]
}]
}
i want to be able to add a sku to the favorite products array based on soldTo and _id.
When doing this there are two possible scenarios.
a. There is already an object in favoriteProducts with the given soldTo in which case the sku is simply added to the array.(for example add sku '12300' to soldTo '1223' for id '111')
b. There is no object with the given soldTo yet in which case this object need to be created with the given sku and soldTo. (for example add sku '123' to soldTo '321' for id '111')
so far i've done this but i feel like there is a way to do it in one query instead.
private async test() {
const soldTo = '1223';
const sku = '12300';
const id = '111';
const hasFavoriteForSoldTo = await userModel.exists({
_id: id,
'favoriteProducts.soldTo': soldTo,
});
if (!hasFavoriteForSoldTo) {
await userModel
.updateOne(
{
_id: id,
},
{ $addToSet: { favoriteProducts: { skus: [sku], soldTo } } },
)
.exec();
} else {
await userModel
.updateOne(
{
_id: id,
'favoriteProducts.soldTo': soldTo,
},
{ $addToSet: { 'favoriteProducts.$.skus': sku } }
)
.exec();
}
}
Use update-documents-with-aggregation-pipeline
Check out mongo play ground below. Not sure you want Output 1 or Output 2.
Output 1
db.collection.update({
_id: { "$oid": "111222333444555666777888" }
},
[
{
$set: {
favoriteProducts: {
$cond: {
if: { $in: [ "1223", "$favoriteProducts.soldTo" ] },
then: {
$map: {
input: "$favoriteProducts",
as: "f",
in: {
$cond: {
if: { $eq: [ "1223", "$$f.soldTo" ] },
then: { $mergeObjects: [ "$$f", { skus: [ "12300" ] } ] },
else: "$$f"
}
}
}
},
else: {
$concatArrays: [ "$favoriteProducts", [ { skus: [ "12300" ], soldTo: "1223" } ] ]
}
}
}
}
}
],
{
multi: true
})
mongoplayground
Output 2
db.collection.update({
_id: { "$oid": "111222333444555666777888" }
},
[
{
$set: {
favoriteProducts: {
$cond: {
if: { $in: [ "1223", "$favoriteProducts.soldTo" ] },
then: {
$map: {
input: "$favoriteProducts",
as: "f",
in: {
$cond: {
if: { $eq: [ "1223", "$$f.soldTo" ] },
then: {
$mergeObjects: [
"$$f",
{ skus: { $concatArrays: [ [ "12300" ], "$$f.skus" ] } }
]
},
else: "$$f"
}
}
}
},
else: {
$concatArrays: [ "$favoriteProducts", [ { skus: [ "12300" ], soldTo: "1223" } ] ]
}
}
}
}
}
],
{
multi: true
})
mongoplayground

json filter nested array with javascript

I want to pull with javascript: {"subNav0", "subNav1", "subNav2", "subNav3", "subNav4", "subNav5"}.
my json:
var data = {
"menus":{
"GrandparentNav0":{
"name":"TopNav",
"items":[
{
"name":"ParentNav0",
"iconClass":"",
"items":[
{
"name":"ParentNav1",
"iconClass":"",
"items":[
{
"name":"subNav0",
"iconClass":""
},
{
"name":"subNav1",
"iconClass":""
},
{
"name":"subNav2",
"iconClass":""
},
{
"name":"subNav3",
"iconClass":""
},
{
"name":"subNav4",
"iconClass":""
},
{
"name":"subNav5",
"iconClass":""
}
]
},
]
}
]
}
},
};
i know basic filter of an array:
data .forEach(function(o) {
o.variable = o.variable.filter(s => s.value == value);
});
I dont know how to get through menus, GrandparentNav0 to pull the subNav(s)
By "pull the subNav(s)" do you mean like accessing it through something like bracket notation?
let subNavs = data['menus']['GrandparentNav0']['items'][0]['items']
console.log(subNavs)
/* would return
[
{
"name": "subNav0",
"iconClass": ""
},
{
"name": "subNav1",
"iconClass": ""
},
{
"name": "subNav2",
"iconClass": ""
},
{
"name": "subNav3",
"iconClass": ""
},
{
"name": "subNav4",
"iconClass": ""
},
{
"name": "subNav5",
"iconClass": ""
}
]
*/
Here is a solution using object-scan. This might be overkill for your requirements, however as you run into other use cases it's a Swiss army knife that makes these types of data interactions very clean
// const objectScan = require('object-scan');
const data = { menus: { GrandparentNav0: { name: 'TopNav', items: [ { name: 'ParentNav0', iconClass: '', items: [ { name: 'ParentNav1', iconClass: '', items: [ { name: 'subNav0', iconClass: '' }, { name: 'subNav1', iconClass: '' }, { name: 'subNav2', iconClass: '' }, { name: 'subNav3', iconClass: '' }, { name: 'subNav4', iconClass: '' }, { name: 'subNav5', iconClass: '' } ] } ] } ] } } };
const result = objectScan(['menus.GrandparentNav0.items[0].items[0].items[*].name'], { reverse: false, rtn: 'value' })(data);
console.log(result);
// => [ 'subNav0', 'subNav1', 'subNav2', 'subNav3', 'subNav4', 'subNav5' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan

Meteor cross collection arrays

I am trying to pull an array from a different collection using collection2. I have been able to do this with objects using the following example for users:
users: {
type: String,
label: "Inspector",
optional: true,
autoform: {
firstOption: 'Choose an Inspector',
options: function() {
return Meteor.users.find({}, {
sort: {
profile: 1,
firstName: 1
}
}).map(function(c) {
return {
label: c.profile.firstName + " " + c.profile.lastName,
value: c._id
};
});
}
}
},
I would like to do the same but for an array of objects. Here is what the source data looks like:
{
"_id": "xDkso4FXHt63K7evG",
"AboveGroundSections": [{
"sectionName": "one"
}, {
"sectionName": "two"
}],
"AboveGroundItems": [{
"itemSection": "one",
"itemDescription": "dfgsdfg",
"itemCode": "dsfgsdg"
}, {
"itemSection": "two",
"itemDescription": "sdfgsdfg",
"itemCode": "sdfgsdgfsd"
}]
}
Here is what my function looks like:
agSection: {
type: String,
optional: true,
autoform: {
firstOption: 'Select A Section Type',
options: function() {
return TemplateData.find({}, {
sort: {
AboveGroundSections: 1,
sectionName: [0]
}
}).map(function(c) {
return {
label: c.AboveGroundSections.sectionName,
value: c.AboveGroundSections.sectionName
}
});
}
}
},
I know this, it's just not pulling the data for me. I am sure, I am just missing something small. I am trying to pull all objects within the AboveGroundSection array.
Your .map() is iterating over the set of documents but not over the arrays inside each document. Also I don't think your sorting is going to work the way you hope because of the inner nesting.
Try:
agSection: {
type: String,
optional: true,
autoform: {
firstOption: 'Select A Section Type',
options() {
let opt = [];
TemplateData.find().forEach(c => {
c.AboveGroundSections.forEach(s => { opt.push(s.sectionName) });
});
return opt.sort().map(o => { return { label: o, value: o } });
}
}
},
Also if your AboveGroundSections array only has a single key per element then you can simplify:
"AboveGroundSections": [
{ "sectionName": "one" },
{ "sectionName": "two" }
]
To:
"AboveGroundSections": [
"one",
"two"
]

Google Visualization style roles in object-literal DataTable

I am creating Google Visualizations (currently ColumnCharts) from AJAX data, by returning an object literal suitable for passing to the DataTable constructor according to these docs:
https://developers.google.com/chart/interactive/docs/reference#dataparam
Data is all working, but I can't work out how to get the style roles working. I thought the documentation was saying to create the below, but it doesn't set the colors; what am I doing wrong?
{
"cols":[
{
"id":"x",
"type":"number",
"label":"Period"
},
{
"id":"quotes",
"type":"number",
"label":"Quotes"
},
{
"id":"quotes_style",
"type":"number",
"p":{
"role":"style"
}
},
{
"id":"orders",
"type":"number",
"label":"Converted to Orders"
},
{
"id":"orders_style",
"type":"number",
"p":{
"role":"style"
}
}
],
"rows":[
{
"c":[
{
"v":0,
"f":"2/2015"
},
{
"v":4
},
{
"p":{
"style":"color:#dddddd"
}
},
{
"v":3
},
{
"p":{
"style":"color:#00d67c"
}
}
],
"p":null
}
],
"p":null
}
Okay, fixed it; first of all in cols the type of the column should be string; then in the rows, the style columns should just have v set to the style string, not p.
(Found this by calling google.visualization.arrayToDataTable() on a working array in the help documentation and calling .toJSON() on the result - very handy!)
Final JSON:
{
"cols":[
{
"id":"x",
"type":"number",
"label":"Period"
},
{
"id":"quotes",
"type":"number",
"label":"Quotes"
},
{
"id":"quotes_style",
"type":"string",
"p":{
"role":"style"
}
},
{
"id":"orders",
"type":"number",
"label":"Converted to Orders"
},
{
"id":"orders_style",
"type":"string",
"p":{
"role":"style"
}
}
],
"rows":[
{
"c":[
{
"v":0,
"f":"2/2015"
},
{
"v":4
},
{
"v":"color:#dddddd"
},
{
"v":3
},
{
"v":"color:#00d67c"
}
],
"p":null
}
],
"p":null
}

Querying nested objects in PouchDB

I googled some examples and tutorials but couldn't find any clear example for my case.
I get a JSON response from my server like this:
var heroes = [
{
id: 5,
name: 'Batman',
realName: 'Bruce Wayne',
equipments: [
{
type: 'boomarang',
name: 'Batarang',
},
{
type: 'cloak',
name: 'Bat Cloak',
},
{
type: 'bolas',
name: 'Bat-Bolas',
}
]
},
{
id: 6,
name: 'Cat Woman',
realName: 'Selina Kyle',
equipments: [
{
type: 'car',
name: 'Cat-illac',
},
{
type: 'bolas',
name: 'Cat-Bolas',
}
]
}
];
I would like to query for example: "get heroes with equipment type of bolas"
and It should return both hero objects in an array.
I know it is not right but what I am trying to do is to form a map function like this:
function myMapFunction(doc) {
if(doc.equipments.length > 0) {
emit(doc.equipment.type);
}
}
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function(result) {
console.log(result);
}).catch(function(err) {
// handle errors
});
Is it possible? If not what alternatives do I have?
P.S: I also checked LokiJS and underscoreDB. However PouchDB looks more sophisticated and capable of such query.
Thank you guys in advance
Your map function should be:
function myMapFunction(doc) {
doc.equipments.forEach(function (equipment) {
emit(equipment.type);
});
}
Then to query, you use {key: 'bolas'}:
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function (result) {
// got result
});
Then your result will look like:
{
"total_rows": 5,
"offset": 0,
"rows": [
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
},
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
}
]
}
Also be sure to create an index first! Details are in the PouchDB map/reduce guide :)

Categories