Algolia : How to filter out a list of objectIDs client side - algolia

I have a list of users a user can follow, coming from the algolia index.
const index = client.initIndex('index');
const f= { filters: 'objectType:user };
index.search('user', f, (e, c) => {
const x = c.hits;
});
Now, there's an array of user IDs of users this user is already following, I don't want those users to be returned from this search, how do I filter those out. UserID is objectID in this case.

I recommend using Algolia's "Negative Filters" do achieve that.
https://www.algolia.com/doc/guides/searching/filtering/?language=instantsearchjs#negative-filters
So if you have an array of user ids, you could build a filter that looks like:
const filters = 'objectType:user AND NOT objectID:123 AND NOT objectID:456 ...';
You could build this filter string by looping through you array of ids, and adding this to your filter for each user id:
`AND NOT objectID:${userId}`

Related

How can I apply conditional filtering to MongoDB documents?

I am having a really hard time figuring out how to apply multiple levels of filtering to my mongoDB documents (not sure if this phrasing is correct).
I am trying to create an app that will allow users to perform a search and retrieve only those documents that match the filters they have chosen to apply. A user might chose to apply only one filter or combine multiple filters.
For example, the user might be looking for a house. The available filters could be location, size and type. If the user applies the location filter with a value of ‘London’, they should get only those houses available in London. If they choose to combine the above location with the type filter with a value of ‘2-bedroom-apartment’, they should get all 2-bedroom apartments available in London.
How can I make sure that the results are conditionally filtered, depending on the filters that the user has applied?
I think I am supposed to use $match, but I don’t understand if I can use multiple queries with it.
What I have come up with so far is the following:
const getFilteredData = async(req, res) => {
try {
const { filter1, filter2, filter3 } = req.query;
const filteredData = await dataModel.aggregate([
{$match:{
$and:[{filter1:filter1},{filter2: filter2}, {filter3:filter3}] //1st option: all of the filters are applied by the user
}}
])
res.status(201).json({data: filteredData});
}
catch(err) {
res.status(404).json({message: err.message});
}
}
With the above code, the results are filtered only when all 3 filters are being applied. How can I cater to different combinations of filters being applied by the user (only one filter, filter1 & filter3 combined etc)?
Any help will be massively appreciated.
Assuming req.query can be {name: "Coat", city: "Athens"} You can do something like:
const getFilteredData = async(req, res) => {
try {
const filtersArr = [];
for (const filterKey of ['name', 'city', 'category']) {
if (req.query[filterKey]) {
const thisFilter = {};
thisFilter[filterKey] = req.query[filterKey];
filtersArr.push(thisFilter);
}
}
console.log(filtersArr)
const filteredData = await filteredDataModel.aggregate([
{$match:{
$and: filtersArr //1st option: all of the filters are applied by the user
}}
])
res.status(201).json({data: filteredData});
}
catch(err) {
res.status(404).json({message: err.message});
}
}
You can also use the original req.query like this:
const filteredData = await filteredDataModel.find(req.query)
But iterating using the code allows you to validate the keys that you want...

Format response from mongoose into a model

How do you go about formatting the data response from mongoose? For a simple Post Schema
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
}
},{
timestamps: true
});
Whenever I do a GET request to find all the post, It returns me all its fields including _id and __v in which I wouldn't want to return those fields in an API.
Is there a way I would select only certain fields that I would want to return?
As far as I've found was that I could set a second parameter of title onto my query and it would return only the _id and title.
const post = await Post.find({},'title');
I find the method above isn't the proper way to filter fields in cases in the future where the values are deeply nested object and we would like to pick out certain values.
Is there perhaps a way to create a Model/Class and pick the fields based on the Model/Class and return the respond?
You can use select from mongoose.
You can either select only the fields you want.
var find = await model.find({}).select("my_field")
Or not show the fields you don't want
var find = await model.find({}).select("-my_field")
Check the documentation

Algolia search for array that contains value

I am using Algolia search, and right now I use this to find a specific item by id:
algolia.getObject(id)
However, I need to make a search by barcode rather than ID - need a pointer in the right direction here.
The barcodes field is an array that can contain one or more barcode numbers.
You can trigger a search with filters on the barcodes attributes. The filters parameter supports multiple format, numeric values included. It does not matter if the attribute hold a single or multiple (an array) values. Here is an example with the JavaScript client:
const algoliasearch = require('algoliasearch');
const client = algoliasearch('YOUR_APP_ID', 'YOUR_API_KEY');
const index = client.initIndex('YOUR_INDEX_NAME');
index
.search({
filters: 'barcodes = YOUR_BARCODE_VALUE',
})
.then(response => {
console.log(response.hits);
});
The above example assumes that your records have a structure like this one:
{
"barcodes": [10, 20, 30]
}

Sequelize: where query string is in array of strings postgresql

I am trying to perform a query in sequelize where I want to get only users that have the correct role. Roles are stored as an array of strings. For example ['student'] or ['school_owner', 'admin'].
In this particular case, I'm actually trying to get a school and include the school owners for that school. The failing relevant query is
const ownerQuery: { [key: string]: any } = {};
ownerQuery.roles = {$in: ["{school_owner}"]};
School
.findById(req.params.id,{include: [{ model: User, where: ownerQuery }]})
.then((school: School | null) => {
if (school === null) {
res.status(404).json({message: "Not Found"})
} else {
res.json(school)
}
}, (error) => next(error))
sequelize is storing the array values as something like {school_owner, admin}. The documentation says that I can use the following instead for my $in query
ownerQuery.roles = {$in: ["school_owner"]};
Which removes the {} but it gives me a Array value must start with "{" or dimension information.' error.
In the first example, the query doesn't fail, but it doesn't work like an $in query either. I have to match the contents of roles exactly. For example, if a user has both admin and school_owner roles I have to say
ownerQuery.roles = {$in: ["{school_owner, admin}"]};
What's the correct way to perform an $in query so that I can match all users that have a specific roles?
The correct way to implement this functionality is to do the following
ownerQuery.roles = { $contains: ["school_owner"] };
This will return all users that have a role school_owner in their array of roles

Send more than one term to algolia search

I'm implementing algolia search in my site and i want to get a set of data matching any id's i send to the search, so i need to know how could i send more than one parameter to the search, so i can send a set of ids, something like this:
let client = algoliasearch(APP_ID, API_KEY),
index = client.initIndex(INDEX_NAME);
let term=["3223212","2423434"];
index.search(term, callback)
This is not working right now, have any idea? or even how could i achieve my goal using another algolia feautre like filtering for instance?
If you're trying to retrieve objects by their objectIDs (which you can manually set at creation time to match your database ids), you can simply use the getObjects method.
Extract from the documentation:
You can also retrieve a set of objects:
index.getObjects(['myObj1', 'myObj2'], function(err, content) {
console.log(content);
});
If you're trying to list all the records that belong to a group with a specific id, you can use a facet that will contain this id and filter on it.
Inside your record:
{
"group_id": "3223212",
// or
"group_ids": ["3223212", "2423434"]
}
Inside your index settings:
{
attributesForFaceting: [
'onlyFilter(group_id)'
]
}
At query time:
let ids = ["3223212", "2423434"];
let filters = ids.map(id => `group_id:${id}`).join(' OR ');
index.search('', { filters: filters }, callback);