Using two ArangoSearch Views in a traversal - arangodb

Consider the following dataset:
arangosh> db.createDocumentCollection('city')
arangosh> db.createDocumentCollection('colour')
arangosh> db._createEdgeCollection('likes')
LET cities =
[
{
"key": "1",
"cities": ["toronto", "kingston"]
},
{
"key": "2",
"cities": ["seattle", "kingston"]
},
{
"key": "3",
"cities": ["seat", "kingston"]
},
{
"key": "4",
"cities": ["toronto", "seattle"]
}
]
FOR c IN cities
INSERT c INTO city
RETURN NEW
LET colours = [
{
"key": "1",
"colors": ["red", "love green"]
},
{
"key": "2",
"colors": ["we like red", "blue and purple"]
},
{
"key": "3",
"colors": ["grassy green"]
},
{
"key": "4",
"colors": ["red"]
},
{
"key": "5",
"colors": ["red"]
},
{
"key": "6",
"colors": ["red", "green"]
}
]
FOR c IN colours
INSERT c INTO colour
RETURN NEW
LET likes = [
{
"from": "city/1", "to": "colour/3"
},
{
"from": "city/1", "to": "colour/1"
},
{
"from": "city/2", "to": "colour/3"
},
{
"from": "city/3", "to": "colour/2"
},
{
"from": "city/3", "to": "colour/3"
},
{
"from": "city/3", "to": "colour/5"
},
{
"from": "city/4", "to": "colour/5"
},
{
"from": "city/4", "to": "colour/6"
}
]
FOR l IN likes
INSERT l INTO likes
RETURN NEW
arangosh> db._createView("city_v", "arangosearch");
arangosh> var link = {
includeAllFields: false,
fields : { cities : { analyzers : [ "text_en" ] } }
};
arangosh> db._view("city_v").properties({ links: { city: link }});
arangosh> db._createView("colour_v", "arangosearch");
arangosh> var link = {
includeAllFields: false,
fields : { colors : { analyzers : [ "text_en" ] } }
};
arangosh> db._view("colour_v").properties({ links: { colour: link }});
FOR p IN city_v SEARCH ANALYZER (p.cities == 'toronto', 'text_en') RETURn p._id returns
[
"city/1",
"city/4"
]
FOR p IN colour_v SEARCH ANALYZER (p.colors == 'green', 'text_en') RETURn p._id returns
[
"colour/1",
"colour/3",
"colour/6"
]
I need help with writing a query that would return the city and colour combinations where city satisfies the ArangoSearchView of toronto that like colours that satisfy the ArangoSearchView of green.
The result sould be: city/1 —> colour/3 and city/1 —> city/1 and city/4 —> city/6
While I understand that this example could use a FULLTEXT index or some other FILTER mechanism, the goal is to use the search features of ArangoSearch Views to accomplish this.

Would you slightly change your model like this:
LET cities =
[
{
"_key": "1",
"cities": ["toronto", "kingston"]
},
{
"_key": "2",
"cities": ["seattle", "kingston"]
},
{
"_key": "3",
"cities": ["seat", "kingston"]
},
{
"_key": "4",
"cities": ["toronto", "seattle"]
}
]
FOR c IN cities
INSERT c INTO city
RETURN NEW
LET colours = [
{
"_key": "1",
"colors": ["red", "love green"]
},
{
"_key": "2",
"colors": ["we like red", "blue and purple"]
},
{
"_key": "3",
"colors": ["grassy green"]
},
{
"_key": "4",
"colors": ["red"]
},
{
"_key": "5",
"colors": ["red"]
},
{
"_key": "6",
"colors": ["red", "green"]
}
]
FOR c IN colours
INSERT c INTO colour
RETURN NEW
LET likes = [
{
"_from": "city/1", "_to": "colour/3"
},
{
"_from": "city/1", "_to": "colour/1"
},
{
"_from": "city/2", "_to": "colour/3"
},
{
"_from": "city/3", "_to": "colour/2"
},
{
"_from": "city/3", "_to": "colour/3"
},
{
"_from": "city/3", "_to": "colour/5"
},
{
"_from": "city/4", "_to": "colour/5"
},
{
"_from": "city/4", "_to": "colour/6"
}
]
FOR l IN likes
INSERT l INTO likes
RETURN NEW
And run the query below:
LET clr = (FOR p IN colour_v SEARCH ANALYZER (p.colors == 'green', 'text_en') RETURN p)
FOR doc IN city_v SEARCH ANALYZER (doc.cities == 'toronto', 'text_en')
FOR v,e,p in 1..1 OUTBOUND doc likes
filter p.vertices[1] in clr
RETURN {"v":v,"e":e,"p":p}

Related

mongodb aggregation: get all values for the particular field in list

I have collections like:
[
{
"_id": "1",
"religion": "south",
"tested": true,
"fruit": "orange",
"created_at": 2211123333
},
{
"_id": "2",
"religion": "north",
"tested": false,
"fruit": "apple",
"created_at": 223444433
},
{
"_id": "3",
"religion": "north",
"tested": true,
"fruit": "orange",
"created_at": 234567876
}
]
if religion is south and tested is true then get all the values of fruits in list.
tried:
pipeline = [{"$match": {"$and": [{"religion": "south"}, {"tested": true}]}}, {}{"$project": {"fruit": 1, "_id": 0}}]
db.collection.aggregate(pipeline).to_list(length=None)
getting result as : [{"fruit": "orange"}, {"fruit": "apple"}]
but result should be like: {"fruit" : ["orange", "apple"]}
use $group, $addToSet and using$cond you don't need the $match stage
test it at mongoPlayground
[
{
"$group": {
"_id": null,
"fruit": {
"$addToSet": {
"$cond": [
{
"$and": [
{
"$eq": [
"$religion",
"south"
]
},
{
"$eq": [
"$tested",
true
]
}
]
},
"$fruit",
"$$REMOVE"
]
}
}
}
},
{
"$project": {
fruit: 1,
_id: 0
}
}
]

Mongoose Sort by elements in array

i have my docs like this :
[
{
"_id": "1",
"name": "Valerio",
"gender": "M",
"favorites": []
},
{
"_id": "2",
"name": "Samad",
"gender": "M",
"favorites": ["1", "3", "4"]
},
{
"_id": "3",
"name": "Ayub",
"gender": "F",
"favorites": ["1", "4"]
},
{
"_id": "4",
"name": "Apour",
"gender": "M",
"favorites": ["2"]
}
]
and I want to sort them based on _id : "1" exists in the favorites array like this :
[
{
"_id": "2",
"name": "Samad",
"gender": "M",
"favorites": ["1", "3", "4"]
},
{
"_id": "3",
"name": "Ayub",
"gender": "F",
"favorites": ["1", "4"]
},
{
"_id": "4",
"name": "Apour",
"gender": "M",
"favorites": ["2"]
},
{
"_id": "1",
"name": "Valerio",
"gender": "M",
"favorites": []
}
]
here is what I'm using but this is not sorting it's just filtering :
User.find({
$and: [
{ favorites: { $all: [mongoose.Types.ObjectId("1")] } },
{ gender: { $in:['M'] } },
],
})
i would really appreciate any help, thank you in advance.
You can add a temporary key that holds 1 if the key exists
and 0 if the key doesn't exist, sort in descending order based on this temporary key.
db.collection.aggregate([
{
"$addFields": {
"searchElemExists": {
"$cond": {
"if": {
"$in": [
"1", // <-- Value you want to be sorted based on existance
"$favorites"
]
},
"then": 1,
"else": 0
}
}
},
},
{
"$sort": {
"searchElemExists": -1
}
},
{
"$project": {
"searchElemExists": 0,
},
},
])
Playground Execution of Sample Data

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

MongoDB Aggregate with sum of array object values

I have a collection with the following data:
{ "id": 1,
"name": "abc",
"age" : "12"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
I would like to perform the MongoDB Aggregation for a sum of quizzes for each document.and set it to totalTimes field
And this is the result that I would like to get after the querying:
{ "id": 1,
"name": "abc",
"age" : "12",
"totalTimes": "30"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20",
"totalTimes": "36"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
How can I query to get the sum of quizzes time?
Quite simple using $reduce
db.collection.aggregate([
{
$addFields: {
totalTimes: {
$reduce: {
input: "$quizzes",
initialValue: 0,
in: {
$sum: [
{
$toInt: "$$this.time"
},
"$$value"
]
}
}
}
}
}
])
Mongo Playground

Synonyms, storing weights in document for relevance scoring in Elastic Search

The story: Given the example documents below and by extending them, is it possible to get the following ranking:
A search on "Cereals" results in the following ranking
Cornflakes
Rice Krispies
A search on "Rice" results in the following ranking
Basmati
Rice Krispies
The documents against the search is performed:
[{
name: "Cornflakes"
},
{
name: "Basmati"
},
{
name: "Rice Krispies"
}]
Of course, some of them does not even held the search term, so an option is to add an array of synonyms with a text value and weight with would help in computing the ranking:
[{
name: "Cornflakes",
synonyms: [
{t: 'Cereals', weight: 100},
{t: 'Sugar', weight: 100}]
},
{
name: "Basmati",
synonyms: [
{t: 'Cereals', weight: 1},
{t: 'Rice', weight: 1000}]
},
{
name: "Rice Krispies",
synonyms: [
{t: 'Cereals', weight: 10},
{t: 'Rice', weight: 1}]
}]
Is it the right approach?
What is the Elastic Search query for taking into account weighted synonyms?
I think "tags" would be a more appropriate name for the field than "synonyms".
You could use a nested type to store tags and use function score to combine the value of the tags.weight field (of the best matching tag if any) with the match score on the name field.
One such implementation could look as follows:
put test
put test/tag_doc/_mapping
{
"properties" : {
"tags" : {
"type" : "nested" ,
"properties": {
"t" : {"type" : "string"},
"weight" : {"type" : "double"}
}
}
}
}
put test/tag_doc/_bulk
{ "index" : { "_index" : "test", "_type" : "tag_doc", "_id":1} }
{"name": "Cornflakes","tags": [{"t": "Cereals", "weight":100},{"t": "Sugar", "weight": 100}]}
{ "index" : { "_index" : "test", "_type" : "tag_doc","_id":2} }
{ "name": "Basmati","tags": [{"t": "Cereals", "weight": 1},{"t": "Rice", "weight": 1000}]}
{ "index" : { "_index" : "test", "_type" : "tag_doc","_id":3} }
{ "name": "Rice Krispies", "tags": [{"t": "Cereals", "weight": 10},{"t": "Rice", "weight": 1}]}
post test/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"name": {
"query": "cereals",
"boost": 100
}
}
},
{
"nested": {
"path": "tags",
"query": {
"function_score": {
"functions": [
{
"field_value_factor": {
"field": "tags.weight"
}
}
],
"query": {
"match": {
"tags.t": "cereals"
}
},
"boost_mode": "replace",
"score_mode": "max"
}
},
"score_mode": "max"
}
}
]
}
}
}
Result :
"hits": {
"total": 3,
"max_score": 100,
"hits": [
{
"_index": "test",
"_type": "tag_doc",
"_id": "1",
"_score": 100,
"_source": {
"name": "Cornflakes",
"tags": [
{
"t": "Cereals",
"weight": 100
},
{
"t": "Sugar",
"weight": 100
}
]
}
},
{
"_index": "test",
"_type": "tag_doc",
"_id": "3",
"_score": 10,
"_source": {
"name": "Rice Krispies",
"tags": [
{
"t": "Cereals",
"weight": 10
},
{
"t": "Rice",
"weight": 1
}
]
}
},
{
"_index": "test",
"_type": "tag_doc",
"_id": "2",
"_score": 1,
"_source": {
"name": "Basmati",
"tags": [
{
"t": "Cereals",
"weight": 1
},
{
"t": "Rice",
"weight": 1000
}
]
}
}
]
}

Resources